From 06c0d615a262d1766d91baeaec15c1feb66793b6 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 3 Oct 2022 13:55:46 +0300
Subject: [PATCH 001/400] Create base classes.

---
 vector/src/main/AndroidManifest.xml           |  1 +
 .../features/login/qr/QrCodeLoginActivity.kt  | 36 +++++++++++++++++++
 .../app/features/login/qr/QrCodeLoginArgs.kt  | 25 +++++++++++++
 .../app/features/login/qr/QrCodeLoginType.kt  | 22 ++++++++++++
 .../login/qr/QrCodeLoginViewEvents.kt         | 23 ++++++++++++
 .../features/login/qr/QrCodeLoginViewState.kt | 28 +++++++++++++++
 6 files changed, 135 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt

diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index dbc6458713..1b26f814e6 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -318,6 +318,7 @@
         <activity android:name=".features.settings.devices.v2.othersessions.OtherSessionsActivity" />
         <activity android:name=".features.settings.devices.v2.details.SessionDetailsActivity" />
         <activity android:name=".features.settings.devices.v2.rename.RenameSessionActivity" />
+        <activity android:name=".features.login.qr.QrCodeLoginActivity" />
 
         <!-- Services -->
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
new file mode 100644
index 0000000000..e0a5d244e1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.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.login.qr
+
+import android.content.Context
+import android.content.Intent
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.platform.SimpleFragmentActivity
+
+@AndroidEntryPoint
+class QrCodeLoginActivity : SimpleFragmentActivity() {
+
+    companion object {
+        private const val EXTRA_QR_CODE_LOGIN_ARGS = "EXTRA_QR_CODE_LOGIN_ARGS"
+
+        fun getIntent(context: Context, qrCodeLoginArgs: QrCodeLoginArgs): Intent {
+            return Intent(context, QrCodeLoginActivity::class.java).apply {
+                putExtra(EXTRA_QR_CODE_LOGIN_ARGS, qrCodeLoginArgs)
+            }
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
new file mode 100644
index 0000000000..2f40b3ec4d
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.login.qr
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class QrCodeLoginArgs(
+        val loginType: QrCodeLoginType,
+) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt
new file mode 100644
index 0000000000..b4bb5b667f
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.login.qr
+
+enum class QrCodeLoginType {
+    LOGIN,
+    LINK_A_DEVICE,
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
new file mode 100644
index 0000000000..f5228f1d41
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.login.qr
+
+import im.vector.app.core.platform.VectorViewEvents
+
+sealed class QrCodeLoginViewEvents : VectorViewEvents {
+
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
new file mode 100644
index 0000000000..5ee9ab33d8
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.login.qr
+
+import com.airbnb.mvrx.MavericksState
+
+data class QrCodeLoginViewState(
+    val loginType: QrCodeLoginType,
+) : MavericksState {
+
+    constructor(args: QrCodeLoginArgs) : this(
+            loginType = args.loginType,
+    )
+}

From fdcc7506de5a1c0801731d2125aa6e67912da186 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 3 Oct 2022 18:04:56 +0200
Subject: [PATCH 002/400] Homeservers will not return
 `RelationsResponse.original_event` in the future.

---
 .../room/relation/FetchEditHistoryTask.kt     | 12 ++++++---
 .../room/relation/RelationsResponse.kt        |  1 -
 .../threads/FetchThreadTimelineTask.kt        | 26 ++++++++++++++++---
 3 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
index 5f5c000171..93c7f143fd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/FetchEditHistoryTask.kt
@@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.crypto.CryptoSessionInfoProvider
 import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.room.RoomAPI
+import org.matrix.android.sdk.internal.session.room.timeline.TimelineEventDataSource
 import org.matrix.android.sdk.internal.task.Task
 import javax.inject.Inject
 
@@ -35,7 +36,8 @@ internal interface FetchEditHistoryTask : Task<FetchEditHistoryTask.Params, List
 internal class DefaultFetchEditHistoryTask @Inject constructor(
         private val roomAPI: RoomAPI,
         private val globalErrorReceiver: GlobalErrorReceiver,
-        private val cryptoSessionInfoProvider: CryptoSessionInfoProvider
+        private val cryptoSessionInfoProvider: CryptoSessionInfoProvider,
+        private val eventDataSource: TimelineEventDataSource,
 ) : FetchEditHistoryTask {
 
     override suspend fun execute(params: FetchEditHistoryTask.Params): List<Event> {
@@ -50,10 +52,14 @@ internal class DefaultFetchEditHistoryTask @Inject constructor(
         }
 
         // Filter out edition form other users, and redacted editions
-        val originalSenderId = response.originalEvent?.senderId
+        val originalEvent = eventDataSource.getTimelineEvent(
+                roomId = params.roomId,
+                eventId = params.eventId,
+        )
+        val originalSenderId = originalEvent?.senderInfo?.userId
         val events = response.chunks
                 .filter { it.senderId == originalSenderId }
                 .filter { !it.isRedacted() }
-        return events + listOfNotNull(response.originalEvent)
+        return events + listOfNotNull(originalEvent?.root)
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/RelationsResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/RelationsResponse.kt
index a65165d457..f2b0651e01 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/RelationsResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/RelationsResponse.kt
@@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.events.model.Event
 @JsonClass(generateAdapter = true)
 internal data class RelationsResponse(
         @Json(name = "chunk") val chunks: List<Event>,
-        @Json(name = "original_event") val originalEvent: Event?,
         @Json(name = "next_batch") val nextBatch: String?,
         @Json(name = "prev_batch") val prevBatch: String?
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt
index edd74c2ce0..4cf6445920 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt
@@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.relation.threads
 
 import com.zhuinden.monarchy.Monarchy
 import io.realm.Realm
+import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.crypto.MXCryptoError
 import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
 import org.matrix.android.sdk.api.session.events.model.Event
@@ -24,6 +25,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
+import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
 import org.matrix.android.sdk.internal.database.mapper.asDomain
 import org.matrix.android.sdk.internal.database.mapper.toEntity
@@ -46,6 +48,7 @@ import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.events.getFixedRoomMemberContent
 import org.matrix.android.sdk.internal.session.room.RoomAPI
 import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
+import org.matrix.android.sdk.internal.session.room.timeline.GetEventTask
 import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
@@ -87,6 +90,8 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
         @SessionDatabase private val monarchy: Monarchy,
         private val cryptoService: DefaultCryptoService,
         private val clock: Clock,
+        private val realmSessionProvider: RealmSessionProvider,
+        private val getEventTask: GetEventTask,
 ) : FetchThreadTimelineTask {
 
     enum class Result {
@@ -114,11 +119,26 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
             params: FetchThreadTimelineTask.Params
     ): Result {
         val threadList = response.chunks
-        val threadRootEvent = response.originalEvent
         val hasReachEnd = response.nextBatch == null
 
-        monarchy.awaitTransaction { realm ->
+        val isRootThreadTimelineEventEntityKnown: Boolean
+        var threadRootEvent: Event? = null
 
+        if (hasReachEnd) {
+            isRootThreadTimelineEventEntityKnown = realmSessionProvider.withRealm { realm ->
+                TimelineEventEntity
+                        .where(realm, roomId = params.roomId, eventId = params.rootThreadEventId)
+                        .findFirst()
+            } != null
+            if (!isRootThreadTimelineEventEntityKnown) {
+                // Fetch the root event from the server
+                threadRootEvent = tryOrNull {
+                    getEventTask.execute(GetEventTask.Params(roomId = params.roomId, eventId = params.rootThreadEventId))
+                }
+            }
+        }
+
+        monarchy.awaitTransaction { realm ->
             val threadChunk = ChunkEntity.findLastForwardChunkOfThread(realm, params.roomId, params.rootThreadEventId)
                     ?: run {
                         return@awaitTransaction
@@ -173,7 +193,7 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
                     // Case when thread event is not in the device
                     Timber.i("###THREADS FetchThreadTimelineTask root thread event: ${params.rootThreadEventId} NOT FOUND! Lets create a temp one")
                     val eventEntity = createEventEntity(params.roomId, threadRootEvent, realm)
-                    roomMemberContentsByUser.addSenderState(realm, params.roomId, threadRootEvent.senderId)
+                    roomMemberContentsByUser.addSenderState(realm, params.roomId, threadRootEvent.senderId!!)
                     threadChunk.addTimelineEvent(
                             roomId = params.roomId,
                             eventEntity = eventEntity,

From d7f66d993fc04ad5a3bdddf4c53e11d885d714c8 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 4 Oct 2022 09:55:05 +0200
Subject: [PATCH 003/400] Changelog

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

diff --git a/changelog.d/7282.sdk b/changelog.d/7282.sdk
new file mode 100644
index 0000000000..14b71045cf
--- /dev/null
+++ b/changelog.d/7282.sdk
@@ -0,0 +1 @@
+Stop using `original_event` field from `/relations` endpoint

From 6fbdd87dcf0e206b37efae285b77899446ce66ca Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 4 Oct 2022 17:05:42 +0300
Subject: [PATCH 004/400] Create custom view for header section.

---
 .../src/main/res/values/strings.xml           |  4 ++
 .../stylable_qr_code_login_header_view.xml    | 11 +++
 .../login/qr/QrCodeLoginHeaderView.kt         | 69 +++++++++++++++++++
 .../qr/QrCodeLoginInstructionsFragment.kt     | 29 ++++++++
 .../fragment_qr_code_login_instructions.xml   | 20 ++++++
 .../res/layout/view_qr_code_login_header.xml  | 48 +++++++++++++
 6 files changed, 181 insertions(+)
 create mode 100644 library/ui-styles/src/main/res/values/stylable_qr_code_login_header_view.xml
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
 create mode 100644 vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
 create mode 100644 vector/src/main/res/layout/view_qr_code_login_header.xml

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 4ff7aae750..8520bfbed4 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3323,5 +3323,9 @@
     <!--    Note to translators: for RTL languages, context menu will be at top left corner instead of top right corner. Thanks!-->
     <string name="onboarding_new_app_layout_feedback_message">Tap top right to see the option to feedback.</string>
     <string name="onboarding_new_app_layout_button_try">Try it out</string>
+    
+    <!-- QR Code Login -->
+    <string name="qr_code_login_header_scan_qr_code_title">Scan QR code</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Use the camera on this device to scan the QR code shown on your other device:</string>
 
 </resources>
diff --git a/library/ui-styles/src/main/res/values/stylable_qr_code_login_header_view.xml b/library/ui-styles/src/main/res/values/stylable_qr_code_login_header_view.xml
new file mode 100644
index 0000000000..99f56084d9
--- /dev/null
+++ b/library/ui-styles/src/main/res/values/stylable_qr_code_login_header_view.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <declare-styleable name="QrCodeLoginHeaderView">
+        <attr name="qrCodeLoginHeaderTitle" format="string" />
+        <attr name="qrCodeLoginHeaderDescription" format="string" />
+        <attr name="qrCodeLoginHeaderImageResource" format="reference" />
+        <attr name="qrCodeLoginHeaderImageBackgroundTint" format="color" />
+    </declare-styleable>
+
+</resources>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
new file mode 100644
index 0000000000..315369c462
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.login.qr
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.content.res.use
+import im.vector.app.R
+import im.vector.app.databinding.ViewQrCodeLoginHeaderBinding
+
+class QrCodeLoginHeaderView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private val binding = ViewQrCodeLoginHeaderBinding.inflate(
+            LayoutInflater.from(context),
+            this
+    )
+
+    init {
+        context.obtainStyledAttributes(
+                attrs,
+                R.styleable.QrCodeLoginHeaderView,
+                0,
+                0
+        ).use {
+            setTitle(it)
+            setDescription(it)
+            setImage(it)
+        }
+    }
+
+    private fun setTitle(typedArray: TypedArray) {
+        val title = typedArray.getString(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderTitle)
+        binding.qrCodeLoginHeaderTitleTextView.text = title
+    }
+
+    private fun setDescription(typedArray: TypedArray) {
+        val description = typedArray.getString(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderDescription)
+        binding.qrCodeLoginHeaderDescriptionTextView.text = description
+    }
+
+    private fun setImage(typedArray: TypedArray) {
+        val imageResource = typedArray.getResourceId(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderImageResource, 0)
+        val backgroundTint = typedArray.getColor(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderImageBackgroundTint, 0)
+        binding.qrCodeLoginHeaderImageView.setImageResource(imageResource)
+        binding.qrCodeLoginHeaderImageView.backgroundTintList = ColorStateList.valueOf(backgroundTint)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
new file mode 100644
index 0000000000..68848fad04
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.login.qr
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.databinding.FragmentQrCodeLoginInstructionsBinding
+
+class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginInstructionsBinding>() {
+
+    override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginInstructionsBinding {
+        return FragmentQrCodeLoginInstructionsBinding.inflate(inflater, container, false)
+    }
+}
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
new file mode 100644
index 0000000000..3f085b0aee
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
@@ -0,0 +1,20 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingHorizontal="16dp">
+
+    <im.vector.app.features.login.qr.QrCodeLoginHeaderView
+        android:id="@+id/qrCodeLoginInstructionsHeaderView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:qrCodeLoginHeaderDescription="@string/qr_code_login_header_scan_qr_code_description"
+        app:qrCodeLoginHeaderImageBackgroundTint="?colorPrimary"
+        app:qrCodeLoginHeaderImageResource="@drawable/ic_camera"
+        app:qrCodeLoginHeaderTitle="@string/qr_code_login_header_scan_qr_code_title" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vector/src/main/res/layout/view_qr_code_login_header.xml b/vector/src/main/res/layout/view_qr_code_login_header.xml
new file mode 100644
index 0000000000..4a734ec21e
--- /dev/null
+++ b/vector/src/main/res/layout/view_qr_code_login_header.xml
@@ -0,0 +1,48 @@
+<?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">
+
+    <ImageView
+        android:id="@+id/qrCodeLoginHeaderImageView"
+        android:layout_width="70dp"
+        android:layout_height="70dp"
+        android:layout_marginTop="40dp"
+        android:background="@drawable/circle"
+        android:contentDescription="@string/qr_code_login_header_scan_qr_code_title"
+        android:padding="12dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:tint="?vctr_system"
+        tools:backgroundTint="?colorPrimary"
+        tools:src="@drawable/ic_camera" />
+
+    <TextView
+        android:id="@+id/qrCodeLoginHeaderTitleTextView"
+        style="@style/TextAppearance.Vector.Title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="32dp"
+        android:textStyle="bold"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginHeaderImageView"
+        tools:text="@string/qr_code_login_header_scan_qr_code_title" />
+
+    <TextView
+        android:id="@+id/qrCodeLoginHeaderDescriptionTextView"
+        style="@style/TextAppearance.Vector.Subtitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:gravity="center"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginHeaderTitleTextView"
+        tools:text="@string/qr_code_login_header_scan_qr_code_description" />
+
+</merge>

From 4fdb4e8c79fa0cc70d619e3cd193ac8baa6836bd Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 5 Oct 2022 13:23:39 +0300
Subject: [PATCH 005/400] Create custom view for instructions section.

---
 .../src/main/res/values/strings.xml           |  11 +-
 .../stylable_qr_code_instructions_view.xml    |  11 ++
 .../login/qr/QrCodeLoginInstructionsView.kt   |  61 ++++++++
 ..._qr_code_login_instruction_with_border.xml |  14 ++
 .../fragment_qr_code_login_instructions.xml   |  14 ++
 .../view_qr_code_login_instructions.xml       | 132 ++++++++++++++++++
 6 files changed, 242 insertions(+), 1 deletion(-)
 create mode 100644 library/ui-styles/src/main/res/values/stylable_qr_code_instructions_view.xml
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
 create mode 100644 vector/src/main/res/drawable/circle_qr_code_login_instruction_with_border.xml
 create mode 100644 vector/src/main/res/layout/view_qr_code_login_instructions.xml

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 8520bfbed4..a2642caab3 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3323,9 +3323,18 @@
     <!--    Note to translators: for RTL languages, context menu will be at top left corner instead of top right corner. Thanks!-->
     <string name="onboarding_new_app_layout_feedback_message">Tap top right to see the option to feedback.</string>
     <string name="onboarding_new_app_layout_button_try">Try it out</string>
-    
+
+    <string name="one">1</string>
+    <string name="two">2</string>
+    <string name="three">3</string>
+    <string name="four">4</string>
+   
     <!-- QR Code Login -->
     <string name="qr_code_login_header_scan_qr_code_title">Scan QR code</string>
     <string name="qr_code_login_header_scan_qr_code_description">Use the camera on this device to scan the QR code shown on your other device:</string>
+    <string name="qr_code_login_new_device_instruction_1">Open Element on your other device</string>
+    <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
+    <string name="qr_code_login_new_device_instruction_3">Select \'Link a device\'</string>
+    <string name="qr_code_login_new_device_instruction_4">Select \'Show QR code on this device\'</string>
 
 </resources>
diff --git a/library/ui-styles/src/main/res/values/stylable_qr_code_instructions_view.xml b/library/ui-styles/src/main/res/values/stylable_qr_code_instructions_view.xml
new file mode 100644
index 0000000000..c9a4bb9d05
--- /dev/null
+++ b/library/ui-styles/src/main/res/values/stylable_qr_code_instructions_view.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <declare-styleable name="QrCodeLoginInstructionsView">
+        <attr name="qrCodeLoginInstruction1" format="string" />
+        <attr name="qrCodeLoginInstruction2" format="string" />
+        <attr name="qrCodeLoginInstruction3" format="string" />
+        <attr name="qrCodeLoginInstruction4" format="string" />
+    </declare-styleable>
+
+</resources>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
new file mode 100644
index 0000000000..ddb92b272e
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.login.qr
+
+import android.content.Context
+import android.content.res.TypedArray
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.content.res.use
+import im.vector.app.R
+import im.vector.app.core.extensions.setTextOrHide
+import im.vector.app.databinding.ViewQrCodeLoginInstructionsBinding
+
+class QrCodeLoginInstructionsView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private val binding = ViewQrCodeLoginInstructionsBinding.inflate(
+            LayoutInflater.from(context),
+            this
+    )
+
+    init {
+        context.obtainStyledAttributes(
+                attrs,
+                R.styleable.QrCodeLoginInstructionsView,
+                0,
+                0
+        ).use {
+            setInstructions(it)
+        }
+    }
+
+    private fun setInstructions(typedArray: TypedArray) {
+        val instruction1 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction1)
+        val instruction2 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction2)
+        val instruction3 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction3)
+        val instruction4 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction4)
+        binding.instruction1TextView.setTextOrHide(instruction1)
+        binding.instruction2TextView.setTextOrHide(instruction2)
+        binding.instruction3TextView.setTextOrHide(instruction3)
+        binding.instruction4TextView.setTextOrHide(instruction4)
+    }
+}
diff --git a/vector/src/main/res/drawable/circle_qr_code_login_instruction_with_border.xml b/vector/src/main/res/drawable/circle_qr_code_login_instruction_with_border.xml
new file mode 100644
index 0000000000..cb99e4467c
--- /dev/null
+++ b/vector/src/main/res/drawable/circle_qr_code_login_instruction_with_border.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="ring"
+    android:innerRadius="0dp"
+    android:thicknessRatio="2"
+    android:useLevel="false">
+
+    <solid android:color="?android:colorBackground" />
+
+    <stroke
+        android:width="1dp"
+        android:color="?colorPrimary" />
+
+</shape>
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
index 3f085b0aee..9f87b1b4d4 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
@@ -17,4 +17,18 @@
         app:qrCodeLoginHeaderImageResource="@drawable/ic_camera"
         app:qrCodeLoginHeaderTitle="@string/qr_code_login_header_scan_qr_code_title" />
 
+    <im.vector.app.features.login.qr.QrCodeLoginInstructionsView
+        android:id="@+id/qrCodeLoginInstructionsView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="8dp"
+        android:layout_marginTop="24dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginInstructionsHeaderView"
+        app:qrCodeLoginInstruction1="@string/qr_code_login_new_device_instruction_1"
+        app:qrCodeLoginInstruction2="@string/qr_code_login_new_device_instruction_2"
+        app:qrCodeLoginInstruction3="@string/qr_code_login_new_device_instruction_3"
+        app:qrCodeLoginInstruction4="@string/qr_code_login_new_device_instruction_4" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vector/src/main/res/layout/view_qr_code_login_instructions.xml b/vector/src/main/res/layout/view_qr_code_login_instructions.xml
new file mode 100644
index 0000000000..4b23c0d73d
--- /dev/null
+++ b/vector/src/main/res/layout/view_qr_code_login_instructions.xml
@@ -0,0 +1,132 @@
+<?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">
+
+    <LinearLayout
+        android:id="@+id/instructions1Layout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:visibility="visible">
+
+        <TextView
+            style="@style/TextAppearance.Vector.Caption"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@drawable/circle_qr_code_login_instruction_with_border"
+            android:padding="6dp"
+            android:text="@string/one"
+            android:textColor="?colorPrimary" />
+
+        <TextView
+            android:id="@+id/instruction1TextView"
+            style="@style/TextAppearance.Vector.Body"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="8dp"
+            tools:text="@string/qr_code_login_new_device_instruction_1" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/instructions2Layout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="12dp"
+        android:orientation="horizontal"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/instructions1Layout"
+        tools:visibility="visible">
+
+        <TextView
+            style="@style/TextAppearance.Vector.Caption"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@drawable/circle_qr_code_login_instruction_with_border"
+            android:padding="6dp"
+            android:text="@string/two"
+            android:textColor="?colorPrimary" />
+
+        <TextView
+            android:id="@+id/instruction2TextView"
+            style="@style/TextAppearance.Vector.Body"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="8dp"
+            tools:text="@string/qr_code_login_new_device_instruction_2" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/instructions3Layout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="12dp"
+        android:orientation="horizontal"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/instructions2Layout"
+        tools:visibility="visible">
+
+        <TextView
+            style="@style/TextAppearance.Vector.Caption"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@drawable/circle_qr_code_login_instruction_with_border"
+            android:padding="6dp"
+            android:text="@string/three"
+            android:textColor="?colorPrimary" />
+
+        <TextView
+            android:id="@+id/instruction3TextView"
+            style="@style/TextAppearance.Vector.Body"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="8dp"
+            tools:text="@string/qr_code_login_new_device_instruction_3" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/instructions4Layout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="12dp"
+        android:orientation="horizontal"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/instructions3Layout"
+        tools:visibility="visible">
+
+        <TextView
+            style="@style/TextAppearance.Vector.Caption"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@drawable/circle_qr_code_login_instruction_with_border"
+            android:padding="6dp"
+            android:text="@string/four"
+            android:textColor="?colorPrimary" />
+
+        <TextView
+            android:id="@+id/instruction4TextView"
+            style="@style/TextAppearance.Vector.Body"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="8dp"
+            tools:text="@string/qr_code_login_new_device_instruction_4" />
+    </LinearLayout>
+
+</merge>

From 0fee37ced7cde0da3ef496d8d7ce2ee9fb479e46 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 27 Sep 2022 18:13:36 +0200
Subject: [PATCH 006/400] API 33

---
 dependencies.gradle | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index ab2a5b4a6f..dc901bfb98 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -1,8 +1,8 @@
 ext.versions = [
 
         'minSdk'            : 21,
-        'compileSdk'        : 32,
-        'targetSdk'         : 32,
+        'compileSdk'        : 33,
+        'targetSdk'         : 33,
         'sourceCompat'      : JavaVersion.VERSION_11,
         'targetCompat'      : JavaVersion.VERSION_11,
 ]

From 4677c474ebf3282de9c1bdfdfe459708145bc982 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 28 Sep 2022 10:05:56 +0200
Subject: [PATCH 007/400] androidx.core:core-ktx:1.9.0

---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index dc901bfb98..41a2ffc4bb 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -54,7 +54,7 @@ ext.libs = [
                 'activity'                : "androidx.activity:activity:1.5.1",
                 'appCompat'               : "androidx.appcompat:appcompat:1.5.1",
                 'biometric'               : "androidx.biometric:biometric:1.1.0",
-                'core'                    : "androidx.core:core-ktx:1.8.0",
+                'core'                    : "androidx.core:core-ktx:1.9.0",
                 'recyclerview'            : "androidx.recyclerview:recyclerview:1.2.1",
                 'exifinterface'           : "androidx.exifinterface:exifinterface:1.3.3",
                 'fragmentKtx'             : "androidx.fragment:fragment-ktx:$fragment",

From 933d84a23091d6ad2df7f0e07450d0e9257ac20e Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 28 Sep 2022 10:06:30 +0200
Subject: [PATCH 008/400] androidx.activity:activity:1.6.0

---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index 41a2ffc4bb..ae66f81ffa 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -51,7 +51,7 @@ ext.libs = [
                 'coroutinesTest'          : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
         ],
         androidx    : [
-                'activity'                : "androidx.activity:activity:1.5.1",
+                'activity'                : "androidx.activity:activity:1.6.0",
                 'appCompat'               : "androidx.appcompat:appcompat:1.5.1",
                 'biometric'               : "androidx.biometric:biometric:1.1.0",
                 'core'                    : "androidx.core:core-ktx:1.9.0",

From 9d02072ad91abea1d2e8cafb50af0c8d08e4a1e1 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 28 Sep 2022 10:11:15 +0200
Subject: [PATCH 009/400] Formatting

---
 .../org/billcarsonfr/jsonviewer/JSonViewerDialog.kt | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt b/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt
index 0ebf539d4d..d1d8126c5a 100644
--- a/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt
+++ b/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt
@@ -40,12 +40,13 @@ class JSonViewerDialog : DialogFragment() {
         if (savedInstanceState == null) {
             childFragmentManager.beginTransaction()
                     .replace(
-                            R.id.fragmentContainer, JSonViewerFragment.newInstance(
-                            args.jsonString,
-                            args.defaultOpenDepth,
-                            true,
-                            args.styleProvider
-                    )
+                            R.id.fragmentContainer,
+                            JSonViewerFragment.newInstance(
+                                    args.jsonString,
+                                    args.defaultOpenDepth,
+                                    true,
+                                    args.styleProvider
+                            )
                     )
                     .commitNow()
         }

From 2d905247631b8eb5e6139a7eb6ed107b9c10d6ba Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 28 Sep 2022 10:23:10 +0200
Subject: [PATCH 010/400] activity-ktx

---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index ae66f81ffa..88e7e53646 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -51,7 +51,7 @@ ext.libs = [
                 'coroutinesTest'          : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
         ],
         androidx    : [
-                'activity'                : "androidx.activity:activity:1.6.0",
+                'activity'                : "androidx.activity:activity-ktx:1.6.0",
                 'appCompat'               : "androidx.appcompat:appcompat:1.5.1",
                 'biometric'               : "androidx.biometric:biometric:1.1.0",
                 'core'                    : "androidx.core:core-ktx:1.9.0",

From 61a4dd2128755dfef34d74736e5081f20e8faa73 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 28 Sep 2022 10:57:37 +0200
Subject: [PATCH 011/400] Avoid using deprecated `readList`. Serialize and
 deserialize a String instead of a char array.

---
 .../app/features/pin/lockscreen/views/LockScreenCodeView.kt  | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/pin/lockscreen/views/LockScreenCodeView.kt b/vector/src/main/java/im/vector/app/features/pin/lockscreen/views/LockScreenCodeView.kt
index eb72d86bf4..d9d6d56737 100644
--- a/vector/src/main/java/im/vector/app/features/pin/lockscreen/views/LockScreenCodeView.kt
+++ b/vector/src/main/java/im/vector/app/features/pin/lockscreen/views/LockScreenCodeView.kt
@@ -142,7 +142,8 @@ class LockScreenCodeView @JvmOverloads constructor(
         var codeLength: Int = 0
 
         constructor(source: Parcel) : super(source) {
-            source.readList(code, null)
+            val codeStr = source.readString().orEmpty()
+            code = codeStr.toMutableList()
             codeLength = source.readInt()
         }
 
@@ -150,7 +151,7 @@ class LockScreenCodeView @JvmOverloads constructor(
 
         override fun writeToParcel(out: Parcel, flags: Int) {
             super.writeToParcel(out, flags)
-            out.writeList(code)
+            out.writeString(String(code.toCharArray()))
             out.writeInt(codeLength)
         }
 

From f2bbdc3906a0291dae98e37015e3edbf4909e122 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Wed, 28 Sep 2022 14:37:59 +0200
Subject: [PATCH 012/400] API 33: fix compilation warnings

---
 .../AttachmentViewerActivity.kt               |  4 --
 .../attachmentviewer/SwipeToDismissHandler.kt |  4 +-
 .../im/vector/lib/core/utils/compat/Compat.kt | 70 +++++++++++++++++++
 .../jsonviewer/JSonViewerDialog.kt            |  3 +-
 .../jsonviewer/JSonViewerFragment.kt          |  3 +-
 library/multipicker/build.gradle              |  5 ++
 .../java/im/vector/lib/multipicker/Picker.kt  | 10 ++-
 .../org/matrix/android/sdk/api/util/Compat.kt | 42 +++++++++++
 .../network/ComputeUserAgentUseCase.kt        |  3 +-
 .../network/ComputeUserAgentUseCaseTest.kt    |  3 +-
 .../core/animations/SimpleAnimatorListener.kt |  8 +--
 .../core/epoxy/LayoutManagerStateRestorer.kt  |  3 +-
 .../app/core/extensions/MavericksViewModel.kt |  1 +
 .../vector/app/core/extensions/ViewPager2.kt  | 10 ++-
 .../app/core/resources/VersionCodeProvider.kt |  3 +-
 .../core/services/BluetoothHeadsetReceiver.kt |  3 +-
 .../app/core/services/CallAndroidService.kt   | 10 +--
 .../app/core/services/VectorAndroidService.kt | 10 +++
 .../im/vector/app/core/utils/SystemUtils.kt   |  3 +-
 .../im/vector/app/features/MainActivity.kt    |  5 +-
 .../features/attachments/AttachmentsHelper.kt |  6 +-
 .../preview/AttachmentsPreviewActivity.kt     |  6 +-
 .../app/features/call/VectorCallActivity.kt   |  3 +-
 .../call/conference/VectorJitsiActivity.kt    |  3 +-
 .../call/transfer/CallTransferActivity.kt     |  3 +-
 .../IncomingVerificationRequestHandler.kt     |  3 +-
 .../VerificationQRWaitingFragment.kt          |  3 +-
 .../vector/app/features/home/HomeActivity.kt  |  7 +-
 .../features/home/HomeActivityViewModel.kt    |  3 +-
 .../home/room/detail/RoomDetailActivity.kt    |  3 +-
 .../home/room/detail/TimelineFragment.kt      | 18 ++---
 .../home/room/detail/search/SearchActivity.kt |  3 +-
 .../home/room/threads/ThreadsActivity.kt      |  5 +-
 .../threads/list/views/ThreadListFragment.kt  |  4 +-
 .../VectorActivityLifecycleCallbacks.kt       |  7 +-
 .../location/LocationSharingActivity.kt       |  3 +-
 .../live/map/LiveLocationMapViewActivity.kt   |  3 +-
 .../tracking/LocationSharingAndroidService.kt |  5 +-
 .../app/features/login/LoginActivity.kt       |  3 +-
 .../media/VectorAttachmentViewerActivity.kt   |  8 ++-
 .../onboarding/ftueauth/FtueAuthVariant.kt    |  3 +-
 .../im/vector/app/features/pin/PinActivity.kt |  3 +-
 .../poll/create/CreatePollActivity.kt         |  3 +-
 .../features/rageshake/BugReportActivity.kt   |  2 +-
 .../reactions/EmojiReactionPickerActivity.kt  |  4 +-
 .../createroom/CreateRoomActivity.kt          |  3 +-
 .../roompreview/RoomPreviewActivity.kt        |  3 +-
 .../RoomMemberProfileActivity.kt              |  3 +-
 .../roomprofile/RoomProfileActivity.kt        |  3 +-
 .../settings/joinrule/RoomJoinRuleActivity.kt |  3 +-
 .../settings/VectorSettingsActivity.kt        |  5 +-
 .../VectorSettingsVoiceVideoFragment.kt       |  3 +-
 .../v2/details/SessionDetailsActivity.kt      |  3 +-
 .../v2/othersessions/OtherSessionsActivity.kt |  3 +-
 .../v2/overview/SessionOverviewActivity.kt    |  3 +-
 .../v2/rename/RenameSessionActivity.kt        |  3 +-
 ...rSettingsNotificationPreferenceFragment.kt |  3 +-
 .../features/spaces/SpaceExploreActivity.kt   |  3 +-
 .../features/spaces/SpacePreviewActivity.kt   |  3 +-
 .../leave/SpaceLeaveAdvancedActivity.kt       |  3 +-
 .../leave/SpaceLeaveAdvancedFragment.kt       |  4 +-
 .../spaces/manage/SpaceManageActivity.kt      |  3 +-
 .../spaces/people/SpacePeopleActivity.kt      |  3 +-
 .../app/features/terms/ReviewTermsActivity.kt |  3 +-
 .../features/webview/VectorWebViewActivity.kt |  3 +-
 .../app/features/widgets/WidgetActivity.kt    |  8 ++-
 .../app/features/widgets/WidgetFragment.kt    |  3 +-
 67 files changed, 287 insertions(+), 104 deletions(-)
 create mode 100644 library/core-utils/src/main/java/im/vector/lib/core/utils/compat/Compat.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Compat.kt

diff --git a/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
index 98398760d1..21d96afb77 100644
--- a/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
+++ b/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt
@@ -316,10 +316,6 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
                     }
                     return false
                 }
-
-                override fun onDoubleTap(e: MotionEvent?): Boolean {
-                    return super.onDoubleTap(e)
-                }
             })
 
     override fun onEvent(event: AttachmentEvents) {
diff --git a/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt b/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt
index 7a83ee28d4..54cd34ecba 100644
--- a/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt
+++ b/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt
@@ -113,11 +113,11 @@ internal fun ViewPropertyAnimator.setAnimatorListener(
         onAnimationStart: ((Animator?) -> Unit)? = null
 ) = this.setListener(
         object : AnimatorListenerAdapter() {
-            override fun onAnimationEnd(animation: Animator?) {
+            override fun onAnimationEnd(animation: Animator) {
                 onAnimationEnd?.invoke(animation)
             }
 
-            override fun onAnimationStart(animation: Animator?) {
+            override fun onAnimationStart(animation: Animator) {
                 onAnimationStart?.invoke(animation)
             }
         })
diff --git a/library/core-utils/src/main/java/im/vector/lib/core/utils/compat/Compat.kt b/library/core-utils/src/main/java/im/vector/lib/core/utils/compat/Compat.kt
new file mode 100644
index 0000000000..8b0ad7767b
--- /dev/null
+++ b/library/core-utils/src/main/java/im/vector/lib/core/utils/compat/Compat.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.lib.core.utils.compat
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcelable
+import java.io.Serializable
+
+inline fun <reified T> Intent.getParcelableExtraCompat(key: String): T? = when {
+    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getParcelableExtra(key, T::class.java)
+    else -> @Suppress("DEPRECATION") getParcelableExtra(key) as? T?
+}
+
+inline fun <reified T : Parcelable> Intent.getParcelableArrayListExtraCompat(key: String): ArrayList<T>? = when {
+    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getParcelableArrayListExtra(key, T::class.java)
+    else -> @Suppress("DEPRECATION") getParcelableArrayListExtra<T>(key)
+}
+
+inline fun <reified T> Bundle.getParcelableCompat(key: String): T? = when {
+    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getParcelable(key, T::class.java)
+    else -> @Suppress("DEPRECATION") getParcelable(key) as? T?
+}
+
+inline fun <reified T : Serializable> Bundle.getSerializableCompat(key: String): T? = when {
+    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializable(key, T::class.java)
+    else -> @Suppress("DEPRECATION") getSerializable(key) as? T?
+}
+
+inline fun <reified T : Serializable> Intent.getSerializableExtraCompat(key: String): T? = when {
+    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getSerializableExtra(key, T::class.java)
+    else -> @Suppress("DEPRECATION") getSerializableExtra(key) as? T?
+}
+
+fun PackageManager.queryIntentActivitiesCompat(data: Intent, flags: Int): List<ResolveInfo> {
+    return when {
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> queryIntentActivities(
+                data,
+                PackageManager.ResolveInfoFlags.of(flags.toLong())
+        )
+        else -> @Suppress("DEPRECATION") queryIntentActivities(data, flags)
+    }
+}
+
+fun PackageManager.resolveActivityCompat(data: Intent, flags: Int): ResolveInfo? {
+    return when {
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> resolveActivity(
+                data,
+                PackageManager.ResolveInfoFlags.of(flags.toLong())
+        )
+        else -> @Suppress("DEPRECATION") resolveActivity(data, flags)
+    }
+}
diff --git a/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt b/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt
index d1d8126c5a..696655a19f 100644
--- a/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt
+++ b/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerDialog.kt
@@ -23,6 +23,7 @@ import android.view.ViewGroup
 import android.view.WindowManager
 import androidx.fragment.app.DialogFragment
 import com.airbnb.mvrx.Mavericks
+import im.vector.lib.core.utils.compat.getParcelableCompat
 
 class JSonViewerDialog : DialogFragment() {
 
@@ -36,7 +37,7 @@ class JSonViewerDialog : DialogFragment() {
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-        val args: JSonViewerFragmentArgs = arguments?.getParcelable(Mavericks.KEY_ARG) ?: return
+        val args: JSonViewerFragmentArgs = arguments?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
         if (savedInstanceState == null) {
             childFragmentManager.beginTransaction()
                     .replace(
diff --git a/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerFragment.kt b/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerFragment.kt
index 719ce29045..f7c7f4d7bc 100644
--- a/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerFragment.kt
+++ b/library/external/jsonviewer/src/main/java/org/billcarsonfr/jsonviewer/JSonViewerFragment.kt
@@ -28,6 +28,7 @@ import com.airbnb.mvrx.Mavericks
 import com.airbnb.mvrx.MavericksView
 import com.airbnb.mvrx.fragmentViewModel
 import com.airbnb.mvrx.withState
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.parcelize.Parcelize
 
 @Parcelize
@@ -53,7 +54,7 @@ class JSonViewerFragment : Fragment(), MavericksView {
             container: ViewGroup?,
             savedInstanceState: Bundle?
     ): View? {
-        val args: JSonViewerFragmentArgs? = arguments?.getParcelable(Mavericks.KEY_ARG)
+        val args: JSonViewerFragmentArgs? = arguments?.getParcelableCompat(Mavericks.KEY_ARG)
         val inflate =
                 if (args?.wrap == true) {
                     inflater.inflate(R.layout.fragment_jv_recycler_view_wrap, container, false)
diff --git a/library/multipicker/build.gradle b/library/multipicker/build.gradle
index 2de99d5c20..58edfcdd5c 100644
--- a/library/multipicker/build.gradle
+++ b/library/multipicker/build.gradle
@@ -35,9 +35,14 @@ android {
         }
     }
 
+    kotlinOptions {
+        jvmTarget = "11"
+    }
 }
 
 dependencies {
+    implementation project(":library:core-utils")
+
     api libs.androidx.activity
     implementation libs.androidx.exifinterface
     implementation libs.androidx.core
diff --git a/library/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt b/library/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt
index 8960f3228b..1cfcba505f 100644
--- a/library/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt
+++ b/library/multipicker/src/main/java/im/vector/lib/multipicker/Picker.kt
@@ -22,6 +22,9 @@ import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.net.Uri
 import androidx.activity.result.ActivityResultLauncher
+import im.vector.lib.core.utils.compat.getParcelableArrayListExtraCompat
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
+import im.vector.lib.core.utils.compat.queryIntentActivitiesCompat
 
 /**
  * Abstract class to provide all types of Pickers.
@@ -45,13 +48,13 @@ abstract class Picker<T> {
 
         val uriList = mutableListOf<Uri>()
         if (data.action == Intent.ACTION_SEND) {
-            (data.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri)?.let { uriList.add(it) }
+            data.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM)?.let { uriList.add(it) }
         } else if (data.action == Intent.ACTION_SEND_MULTIPLE) {
-            val extraUriList: List<Uri>? = data.getParcelableArrayListExtra(Intent.EXTRA_STREAM)
+            val extraUriList: List<Uri>? = data.getParcelableArrayListExtraCompat(Intent.EXTRA_STREAM)
             extraUriList?.let { uriList.addAll(it) }
         }
 
-        val resInfoList: List<ResolveInfo> = context.packageManager.queryIntentActivities(data, PackageManager.MATCH_DEFAULT_ONLY)
+        val resInfoList: List<ResolveInfo> = context.packageManager.queryIntentActivitiesCompat(data, PackageManager.MATCH_DEFAULT_ONLY)
         uriList.forEach {
             for (resolveInfo in resInfoList) {
                 val packageName: String = resolveInfo.activityInfo.packageName
@@ -91,6 +94,7 @@ abstract class Picker<T> {
         } else if (dataUri != null) {
             selectedUriList.add(dataUri)
         } else {
+            @Suppress("DEPRECATION")
             data?.extras?.get(Intent.EXTRA_STREAM)?.let {
                 (it as? List<*>)?.filterIsInstance<Uri>()?.let { uriList ->
                     selectedUriList.addAll(uriList)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Compat.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Compat.kt
new file mode 100644
index 0000000000..b685c49713
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Compat.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 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.api.util
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.Build
+
+fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): ApplicationInfo {
+    return when {
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getApplicationInfo(
+                packageName,
+                PackageManager.ApplicationInfoFlags.of(flags.toLong())
+        )
+        else -> @Suppress("DEPRECATION") getApplicationInfo(packageName, flags)
+    }
+}
+
+fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int): PackageInfo {
+    return when {
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> getPackageInfo(
+                packageName,
+                PackageManager.PackageInfoFlags.of(flags.toLong())
+        )
+        else -> @Suppress("DEPRECATION") getPackageInfo(packageName, flags)
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt
index 6eb4d5b104..6611bd7865 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt
@@ -20,6 +20,7 @@ import android.content.Context
 import android.os.Build
 import org.matrix.android.sdk.BuildConfig
 import org.matrix.android.sdk.api.extensions.tryOrNull
+import org.matrix.android.sdk.api.util.getPackageInfoCompat
 import javax.inject.Inject
 
 class ComputeUserAgentUseCase @Inject constructor(
@@ -44,7 +45,7 @@ class ComputeUserAgentUseCase @Inject constructor(
                     // Use appPackageName instead of appName if appName is null or contains any non-ASCII character
                     appPackageName
                 }
-        val appVersion = tryOrNull { pm.getPackageInfo(context.applicationContext.packageName, 0).versionName } ?: FALLBACK_APP_VERSION
+        val appVersion = tryOrNull { pm.getPackageInfoCompat(context.applicationContext.packageName, 0).versionName } ?: FALLBACK_APP_VERSION
 
         val deviceManufacturer = Build.MANUFACTURER
         val deviceModel = Build.MODEL
diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt
index 9ed6f28d7e..0e7dc5dd32 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt
@@ -27,6 +27,7 @@ import org.amshove.kluent.shouldBeEqualTo
 import org.junit.Before
 import org.junit.Test
 import org.matrix.android.sdk.BuildConfig
+import org.matrix.android.sdk.api.util.getPackageInfoCompat
 import java.lang.Exception
 
 private const val A_PACKAGE_NAME = "org.matrix.sdk"
@@ -50,7 +51,7 @@ class ComputeUserAgentUseCaseTest {
         every { context.packageName } returns A_PACKAGE_NAME
         every { context.packageManager } returns packageManager
         every { packageManager.getApplicationInfo(any(), any()) } returns applicationInfo
-        every { packageManager.getPackageInfo(any<String>(), any()) } returns packageInfo
+        every { packageManager.getPackageInfoCompat(any(), any()) } returns packageInfo
     }
 
     @Test
diff --git a/vector/src/main/java/im/vector/app/core/animations/SimpleAnimatorListener.kt b/vector/src/main/java/im/vector/app/core/animations/SimpleAnimatorListener.kt
index d8ffde20ef..ec20de5914 100644
--- a/vector/src/main/java/im/vector/app/core/animations/SimpleAnimatorListener.kt
+++ b/vector/src/main/java/im/vector/app/core/animations/SimpleAnimatorListener.kt
@@ -19,19 +19,19 @@ package im.vector.app.core.animations
 import android.animation.Animator
 
 open class SimpleAnimatorListener : Animator.AnimatorListener {
-    override fun onAnimationRepeat(animation: Animator?) {
+    override fun onAnimationRepeat(animation: Animator) {
         // No op
     }
 
-    override fun onAnimationEnd(animation: Animator?) {
+    override fun onAnimationEnd(animation: Animator) {
         // No op
     }
 
-    override fun onAnimationCancel(animation: Animator?) {
+    override fun onAnimationCancel(animation: Animator) {
         // No op
     }
 
-    override fun onAnimationStart(animation: Animator?) {
+    override fun onAnimationStart(animation: Animator) {
         // No op
     }
 }
diff --git a/vector/src/main/java/im/vector/app/core/epoxy/LayoutManagerStateRestorer.kt b/vector/src/main/java/im/vector/app/core/epoxy/LayoutManagerStateRestorer.kt
index dfbdfff2b8..b1d641826e 100644
--- a/vector/src/main/java/im/vector/app/core/epoxy/LayoutManagerStateRestorer.kt
+++ b/vector/src/main/java/im/vector/app/core/epoxy/LayoutManagerStateRestorer.kt
@@ -21,6 +21,7 @@ import android.os.Parcelable
 import androidx.recyclerview.widget.RecyclerView
 import im.vector.app.core.platform.DefaultListUpdateCallback
 import im.vector.app.core.platform.Restorable
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import java.util.concurrent.atomic.AtomicReference
 
 private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE"
@@ -44,7 +45,7 @@ class LayoutManagerStateRestorer(layoutManager: RecyclerView.LayoutManager) : Re
     }
 
     override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
-        val parcelable = savedInstanceState?.getParcelable<Parcelable>(LAYOUT_MANAGER_STATE)
+        val parcelable = savedInstanceState?.getParcelableCompat<Parcelable>(LAYOUT_MANAGER_STATE)
         layoutManagerState.set(parcelable)
     }
 
diff --git a/vector/src/main/java/im/vector/app/core/extensions/MavericksViewModel.kt b/vector/src/main/java/im/vector/app/core/extensions/MavericksViewModel.kt
index 6120a84d7c..8201a1b5e8 100644
--- a/vector/src/main/java/im/vector/app/core/extensions/MavericksViewModel.kt
+++ b/vector/src/main/java/im/vector/app/core/extensions/MavericksViewModel.kt
@@ -25,6 +25,7 @@ import com.airbnb.mvrx.MavericksViewModelProvider
 
 inline fun <reified VM : MavericksViewModel<S>, reified S : MavericksState> ComponentActivity.lazyViewModel(): Lazy<VM> {
     return lazy(mode = LazyThreadSafetyMode.NONE) {
+        @Suppress("DEPRECATION")
         MavericksViewModelProvider.get(
                 viewModelClass = VM::class.java,
                 stateClass = S::class.java,
diff --git a/vector/src/main/java/im/vector/app/core/extensions/ViewPager2.kt b/vector/src/main/java/im/vector/app/core/extensions/ViewPager2.kt
index 7d3a678af1..c94352b998 100644
--- a/vector/src/main/java/im/vector/app/core/extensions/ViewPager2.kt
+++ b/vector/src/main/java/im/vector/app/core/extensions/ViewPager2.kt
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator
 import android.view.View
 import android.view.animation.AccelerateDecelerateInterpolator
 import androidx.viewpager2.widget.ViewPager2
+import im.vector.app.core.animations.SimpleAnimatorListener
 
 fun ViewPager2.setCurrentItem(
         item: Int,
@@ -45,19 +46,16 @@ fun ViewPager2.setCurrentItem(
             previousValue = currentValue
         }.onFailure { animator.cancel() }
     }
-    animator.addListener(object : Animator.AnimatorListener {
-        override fun onAnimationStart(animation: Animator?) {
+    animator.addListener(object : SimpleAnimatorListener() {
+        override fun onAnimationStart(animation: Animator) {
             isUserInputEnabled = false
             beginFakeDrag()
         }
 
-        override fun onAnimationEnd(animation: Animator?) {
+        override fun onAnimationEnd(animation: Animator) {
             isUserInputEnabled = true
             endFakeDrag()
         }
-
-        override fun onAnimationCancel(animation: Animator?) = Unit
-        override fun onAnimationRepeat(animation: Animator?) = Unit
     })
     animator.interpolator = interpolator
     animator.duration = duration
diff --git a/vector/src/main/java/im/vector/app/core/resources/VersionCodeProvider.kt b/vector/src/main/java/im/vector/app/core/resources/VersionCodeProvider.kt
index 1e136bbcc2..edb99d472f 100644
--- a/vector/src/main/java/im/vector/app/core/resources/VersionCodeProvider.kt
+++ b/vector/src/main/java/im/vector/app/core/resources/VersionCodeProvider.kt
@@ -19,6 +19,7 @@ package im.vector.app.core.resources
 import android.content.Context
 import android.os.Build
 import androidx.annotation.NonNull
+import org.matrix.android.sdk.api.util.getPackageInfoCompat
 import javax.inject.Inject
 
 class VersionCodeProvider @Inject constructor(private val context: Context) {
@@ -28,7 +29,7 @@ class VersionCodeProvider @Inject constructor(private val context: Context) {
      */
     @NonNull
     fun getVersionCode(): Long {
-        val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
+        val packageInfo = context.packageManager.getPackageInfoCompat(context.packageName, 0)
 
         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
             packageInfo.longVersionCode
diff --git a/vector/src/main/java/im/vector/app/core/services/BluetoothHeadsetReceiver.kt b/vector/src/main/java/im/vector/app/core/services/BluetoothHeadsetReceiver.kt
index 829fec1652..4925adf69d 100644
--- a/vector/src/main/java/im/vector/app/core/services/BluetoothHeadsetReceiver.kt
+++ b/vector/src/main/java/im/vector/app/core/services/BluetoothHeadsetReceiver.kt
@@ -23,6 +23,7 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import java.lang.ref.WeakReference
 
 class BluetoothHeadsetReceiver : BroadcastReceiver() {
@@ -59,7 +60,7 @@ class BluetoothHeadsetReceiver : BroadcastReceiver() {
             else -> return // ignore intermediate states
         }
 
-        val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
+        val device = intent.getParcelableExtraCompat<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
         val deviceName = device?.name
         when (device?.bluetoothClass?.deviceClass) {
             BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE,
diff --git a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt
index 7a078ce1c8..85ea7f1a1b 100644
--- a/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/CallAndroidService.kt
@@ -39,6 +39,8 @@ import im.vector.app.features.home.AvatarRenderer
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.popup.IncomingCallAlert
 import im.vector.app.features.popup.PopupAlertManager
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
+import im.vector.lib.core.utils.compat.getSerializableExtraCompat
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
 import org.matrix.android.sdk.api.util.MatrixItem
@@ -71,7 +73,7 @@ class CallAndroidService : VectorAndroidService() {
     private var mediaSession: MediaSessionCompat? = null
     private val mediaSessionButtonCallback = object : MediaSessionCompat.Callback() {
         override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean {
-            val keyEvent = mediaButtonEvent?.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT) ?: return false
+            val keyEvent = mediaButtonEvent?.getParcelableExtraCompat<KeyEvent>(Intent.EXTRA_KEY_EVENT) ?: return false
             if (keyEvent.keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
                 callManager.headSetButtonTapped()
                 return true
@@ -158,7 +160,7 @@ class CallAndroidService : VectorAndroidService() {
         val incomingCallAlert = IncomingCallAlert(callId,
                 shouldBeDisplayedIn = { activity ->
                     if (activity is VectorCallActivity) {
-                        activity.intent.getParcelableExtra<CallArgs>(Mavericks.KEY_ARG)?.callId != call.callId
+                        activity.intent.getParcelableExtraCompat<CallArgs>(Mavericks.KEY_ARG)?.callId != call.callId
                     } else true
                 }
         ).apply {
@@ -188,7 +190,7 @@ class CallAndroidService : VectorAndroidService() {
 
     private fun handleCallTerminated(intent: Intent) {
         val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
-        val endCallReason = intent.getSerializableExtra(EXTRA_END_CALL_REASON) as EndCallReason
+        val endCallReason = intent.getSerializableExtraCompat<EndCallReason>(EXTRA_END_CALL_REASON)
         val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false)
         alertManager.cancelAlert(callId)
         val terminatedCall = knownCalls.remove(callId)
@@ -202,7 +204,7 @@ class CallAndroidService : VectorAndroidService() {
         startForeground(notificationId, notification)
         if (knownCalls.isEmpty()) {
             Timber.tag(loggerTag.value).v("No more call, stop the service")
-            stopForeground(true)
+            stopForegroundCompat()
             mediaSession?.isActive = false
             myStopSelf()
         }
diff --git a/vector/src/main/java/im/vector/app/core/services/VectorAndroidService.kt b/vector/src/main/java/im/vector/app/core/services/VectorAndroidService.kt
index f30a74e9de..04fc746b14 100644
--- a/vector/src/main/java/im/vector/app/core/services/VectorAndroidService.kt
+++ b/vector/src/main/java/im/vector/app/core/services/VectorAndroidService.kt
@@ -18,6 +18,7 @@ package im.vector.app.core.services
 
 import android.app.Service
 import android.content.Intent
+import android.os.Build
 import android.os.IBinder
 import timber.log.Timber
 
@@ -55,4 +56,13 @@ abstract class VectorAndroidService : Service() {
     override fun onBind(intent: Intent?): IBinder? {
         return null
     }
+
+    protected fun stopForegroundCompat() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            stopForeground(STOP_FOREGROUND_REMOVE)
+        } else {
+            @Suppress("DEPRECATION")
+            stopForeground(true)
+        }
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
index cde4fe2a35..20eda102e2 100644
--- a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
@@ -34,6 +34,7 @@ import androidx.core.content.getSystemService
 import androidx.fragment.app.Fragment
 import im.vector.app.R
 import im.vector.app.features.notifications.NotificationUtils
+import org.matrix.android.sdk.api.util.getApplicationInfoCompat
 
 /**
  * Tells if the application ignores battery optimizations.
@@ -63,7 +64,7 @@ fun Context.isAnimationEnabled(): Boolean {
  */
 fun Context.getApplicationLabel(packageName: String): String {
     return try {
-        val ai = packageManager.getApplicationInfo(packageName, 0)
+        val ai = packageManager.getApplicationInfoCompat(packageName, 0)
         packageManager.getApplicationLabel(ai).toString()
     } catch (e: PackageManager.NameNotFoundException) {
         packageName
diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt
index 1b3a9eb142..0040962b73 100644
--- a/vector/src/main/java/im/vector/app/features/MainActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt
@@ -57,6 +57,7 @@ import im.vector.app.features.start.StartAppViewModel
 import im.vector.app.features.start.StartAppViewState
 import im.vector.app.features.themes.ActivityOtherThemes
 import im.vector.app.features.ui.UiStateRepository
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -181,7 +182,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
     private fun handleAppStarted() {
         if (intent.hasExtra(EXTRA_NEXT_INTENT)) {
             // Start the next Activity
-            val nextIntent = intent.getParcelableExtra<Intent>(EXTRA_NEXT_INTENT)
+            val nextIntent = intent.getParcelableExtraCompat<Intent>(EXTRA_NEXT_INTENT)
             startIntentAndFinish(nextIntent)
         } else if (intent.hasExtra(EXTRA_INIT_SESSION)) {
             setResult(RESULT_OK)
@@ -218,7 +219,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
     }
 
     private fun parseArgs(): MainActivityArgs {
-        val argsFromIntent: MainActivityArgs? = intent.getParcelableExtra(EXTRA_ARGS)
+        val argsFromIntent: MainActivityArgs? = intent.getParcelableExtraCompat(EXTRA_ARGS)
         Timber.w("Starting MainActivity with $argsFromIntent")
 
         return MainActivityArgs(
diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt
index 646758654f..1a8e10d102 100644
--- a/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentsHelper.kt
@@ -26,6 +26,8 @@ import im.vector.app.core.dialogs.PhotoOrVideoDialog
 import im.vector.app.core.platform.Restorable
 import im.vector.app.core.resources.BuildMeta
 import im.vector.app.features.settings.VectorPreferences
+import im.vector.lib.core.utils.compat.getParcelableCompat
+import im.vector.lib.core.utils.compat.getSerializableCompat
 import im.vector.lib.multipicker.MultiPicker
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
 import timber.log.Timber
@@ -66,8 +68,8 @@ class AttachmentsHelper(
     }
 
     override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
-        captureUri = savedInstanceState?.getParcelable(CAPTURE_PATH_KEY) as? Uri
-        pendingType = savedInstanceState?.getSerializable(PENDING_TYPE_KEY) as? AttachmentTypeSelectorView.Type
+        captureUri = savedInstanceState?.getParcelableCompat(CAPTURE_PATH_KEY)
+        pendingType = savedInstanceState?.getSerializableCompat(PENDING_TYPE_KEY)
     }
 
     // Public Methods
diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt
index 7ddba0d229..4a965022b8 100644
--- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewActivity.kt
@@ -24,6 +24,8 @@ import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleBinding
 import im.vector.app.features.themes.ActivityOtherThemes
+import im.vector.lib.core.utils.compat.getParcelableArrayListExtraCompat
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
 
 @AndroidEntryPoint
@@ -41,7 +43,7 @@ class AttachmentsPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
         }
 
         fun getOutput(intent: Intent): List<ContentAttachmentData> {
-            return intent.getParcelableArrayListExtra<ContentAttachmentData>(ATTACHMENTS_PREVIEW_RESULT).orEmpty()
+            return intent.getParcelableArrayListExtraCompat<ContentAttachmentData>(ATTACHMENTS_PREVIEW_RESULT).orEmpty()
         }
 
         fun getKeepOriginalSize(intent: Intent): Boolean {
@@ -57,7 +59,7 @@ class AttachmentsPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
 
     override fun initUiAndData() {
         if (isFirstCreation()) {
-            val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelable(EXTRA_FRAGMENT_ARGS) ?: return
+            val fragmentArgs: AttachmentsPreviewArgs = intent?.extras?.getParcelableCompat(EXTRA_FRAGMENT_ARGS) ?: return
             addFragment(views.simpleFragmentContainer, AttachmentsPreviewFragment::class.java, fragmentArgs)
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
index aa46feeea6..7e226304c1 100644
--- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
@@ -69,6 +69,7 @@ import im.vector.app.features.displayname.getBestName
 import im.vector.app.features.home.AvatarRenderer
 import im.vector.app.features.home.room.detail.RoomDetailActivity
 import im.vector.app.features.home.room.detail.arguments.TimelineArgs
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import io.github.hyuwah.draggableviewlib.DraggableView
 import io.github.hyuwah.draggableviewlib.setupDraggable
 import kotlinx.parcelize.Parcelize
@@ -178,7 +179,7 @@ class VectorCallActivity :
     override fun onNewIntent(intent: Intent?) {
         super.onNewIntent(intent)
         intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) }
-                ?.let { intent.getParcelableExtra<CallArgs>(Mavericks.KEY_ARG) }
+                ?.let { intent.getParcelableExtraCompat<CallArgs>(Mavericks.KEY_ARG) }
                 ?.let {
                     callViewModel.handle(VectorCallViewActions.SwitchCall(it))
                 }
diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt
index 5bf05d353c..4d107ac414 100644
--- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt
@@ -38,6 +38,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityJitsiBinding
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.parcelize.Parcelize
 import org.jitsi.meet.sdk.JitsiMeet
 import org.jitsi.meet.sdk.JitsiMeetActivityDelegate
@@ -200,7 +201,7 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
 
         // Is it a switch to another conf?
         intent?.takeIf { it.hasExtra(Mavericks.KEY_ARG) }
-                ?.let { intent.getParcelableExtra<Args>(Mavericks.KEY_ARG) }
+                ?.let { intent.getParcelableExtraCompat<Args>(Mavericks.KEY_ARG) }
                 ?.let {
                     jitsiViewModel.handle(JitsiCallViewActions.SwitchTo(it, true))
                 }
diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt
index ccda4871ef..25bfb6c0e9 100644
--- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt
@@ -28,6 +28,7 @@ import im.vector.app.R
 import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityCallTransferBinding
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.parcelize.Parcelize
 import javax.inject.Inject
 
@@ -112,7 +113,7 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
         }
 
         fun getCallTransferResult(intent: Intent?): CallTransferResult? {
-            return intent?.extras?.getParcelable(EXTRA_TRANSFER_RESULT)
+            return intent?.extras?.getParcelableCompat(EXTRA_TRANSFER_RESULT)
         }
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
index 3406a86d1e..3a5c7e7eb8 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
@@ -26,6 +26,7 @@ import im.vector.app.features.home.room.detail.RoomDetailActivity
 import im.vector.app.features.home.room.detail.arguments.TimelineArgs
 import im.vector.app.features.popup.PopupAlertManager
 import im.vector.app.features.popup.VerificationVectorAlert
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
 import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
@@ -147,7 +148,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
                     R.drawable.ic_shield_black,
                     shouldBeDisplayedIn = { activity ->
                         if (activity is RoomDetailActivity) {
-                            activity.intent?.extras?.getParcelable<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
+                            activity.intent?.extras?.getParcelableCompat<TimelineArgs>(RoomDetailActivity.EXTRA_ROOM_DETAIL_ARGS)?.let {
                                 it.roomId != pr.roomId
                             } ?: true
                         } else true
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt
index e5402424d0..dc538597db 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt
@@ -27,6 +27,7 @@ import im.vector.app.core.extensions.cleanup
 import im.vector.app.core.extensions.configureWith
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.parcelize.Parcelize
 import javax.inject.Inject
 
@@ -49,7 +50,7 @@ class VerificationQRWaitingFragment :
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         setupRecyclerView()
-        (arguments?.getParcelable(Mavericks.KEY_ARG) as? Args)?.let {
+        (arguments?.getParcelableCompat<Args>(Mavericks.KEY_ARG))?.let {
             controller.update(it)
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 10e8447a2b..689d141023 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -86,6 +86,7 @@ import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
 import im.vector.app.features.themes.ThemeUtils
 import im.vector.app.features.usercode.UserCodeActivity
 import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
@@ -245,7 +246,7 @@ class HomeActivity :
                 }
                 .launchIn(lifecycleScope)
 
-        val args = intent.getParcelableExtra<HomeActivityArgs>(Mavericks.KEY_ARG)
+        val args = intent.getParcelableExtraCompat<HomeActivityArgs>(Mavericks.KEY_ARG)
 
         if (args?.clearNotification == true) {
             notificationDrawerManager.clearAllEvents()
@@ -327,7 +328,7 @@ class HomeActivity :
     private fun migrateThreadsIfNeeded(checkSession: Boolean) {
         if (checkSession) {
             // We should check session to ensure we will only clear cache if needed
-            val args = intent.getParcelableExtra<HomeActivityArgs>(Mavericks.KEY_ARG)
+            val args = intent.getParcelableExtraCompat<HomeActivityArgs>(Mavericks.KEY_ARG)
             if (args?.hasExistingSession == true) {
                 // existingSession --> Will be true only if we came from an existing active session
                 Timber.i("----> Migrating threads from an existing session..")
@@ -538,7 +539,7 @@ class HomeActivity :
 
     override fun onNewIntent(intent: Intent?) {
         super.onNewIntent(intent)
-        val parcelableExtra = intent?.getParcelableExtra<HomeActivityArgs>(Mavericks.KEY_ARG)
+        val parcelableExtra = intent?.getParcelableExtraCompat<HomeActivityArgs>(Mavericks.KEY_ARG)
         if (parcelableExtra?.clearNotification == true) {
             notificationDrawerManager.clearAllEvents()
         }
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
index a08298e402..9157d81333 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
@@ -41,6 +41,7 @@ import im.vector.app.features.raw.wellknown.isSecureBackupRequired
 import im.vector.app.features.raw.wellknown.withElementWellKnown
 import im.vector.app.features.session.coroutineScope
 import im.vector.app.features.settings.VectorPreferences
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collect
@@ -96,7 +97,7 @@ class HomeActivityViewModel @AssistedInject constructor(
     companion object : MavericksViewModelFactory<HomeActivityViewModel, HomeActivityViewState> by hiltMavericksViewModelFactory() {
         override fun initialState(viewModelContext: ViewModelContext): HomeActivityViewState? {
             val activity: HomeActivity = viewModelContext.activity()
-            val args: HomeActivityArgs? = activity.intent.getParcelableExtra(Mavericks.KEY_ARG)
+            val args: HomeActivityArgs? = activity.intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
             return args?.let { HomeActivityViewState(authenticationDescription = it.authenticationDescription) }
                     ?: super.initialState(viewModelContext)
         }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
index 75cda67ce6..6b66d0ec47 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
@@ -46,6 +46,7 @@ import im.vector.app.features.navigation.Navigator
 import im.vector.app.features.room.RequireActiveMembershipAction
 import im.vector.app.features.room.RequireActiveMembershipViewEvents
 import im.vector.app.features.room.RequireActiveMembershipViewModel
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import javax.inject.Inject
@@ -99,7 +100,7 @@ class RoomDetailActivity :
         super.onCreate(savedInstanceState)
         supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
         waitingView = views.waitingView.waitingView
-        val timelineArgs: TimelineArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) ?: return
+        val timelineArgs: TimelineArgs = intent?.extras?.getParcelableCompat(EXTRA_ROOM_DETAIL_ARGS) ?: return
         intent.putExtra(Mavericks.KEY_ARG, timelineArgs)
         currentRoomId = timelineArgs.roomId
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index 7ea837c035..4288ea148d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -774,7 +774,7 @@ class TimelineFragment :
         }
         // We use a custom layout for this menu item, so we need to set a ClickListener
         menu.findItem(R.id.open_matrix_apps)?.let { menuItem ->
-            menuItem.actionView.debouncedClicks {
+            menuItem.actionView?.debouncedClicks {
                 handleMenuItemSelected(menuItem)
             }
         }
@@ -785,7 +785,7 @@ class TimelineFragment :
 
         // Custom thread notification menu item
         menu.findItem(R.id.menu_timeline_thread_list)?.let { menuItem ->
-            menuItem.actionView.debouncedClicks {
+            menuItem.actionView?.debouncedClicks {
                 handleMenuItemSelected(menuItem)
             }
         }
@@ -814,16 +814,16 @@ class TimelineFragment :
                 // icon should be default color no badge
                 val actionView = matrixAppsMenuItem.actionView
                 actionView
-                        .findViewById<ImageView>(R.id.action_view_icon_image)
-                        .setColorFilter(ThemeUtils.getColor(requireContext(), R.attr.vctr_content_secondary))
-                actionView.findViewById<TextView>(R.id.cart_badge).isVisible = false
+                        ?.findViewById<ImageView>(R.id.action_view_icon_image)
+                        ?.setColorFilter(ThemeUtils.getColor(requireContext(), R.attr.vctr_content_secondary))
+                actionView?.findViewById<TextView>(R.id.cart_badge)?.isVisible = false
                 matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
             } else {
                 val actionView = matrixAppsMenuItem.actionView
                 actionView
-                        .findViewById<ImageView>(R.id.action_view_icon_image)
-                        .setColorFilter(colorProvider.getColorFromAttribute(R.attr.colorPrimary))
-                actionView.findViewById<TextView>(R.id.cart_badge).setTextOrHide("$widgetsCount")
+                        ?.findViewById<ImageView>(R.id.action_view_icon_image)
+                        ?.setColorFilter(colorProvider.getColorFromAttribute(R.attr.colorPrimary))
+                actionView?.findViewById<TextView>(R.id.cart_badge)?.setTextOrHide("$widgetsCount")
                 @Suppress("AlwaysShowAction")
                 matrixAppsMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
             }
@@ -895,7 +895,7 @@ class TimelineFragment :
      */
     private fun updateMenuThreadNotificationBadge(menu: Menu, state: RoomDetailViewState) {
         val menuThreadList = menu.findItem(R.id.menu_timeline_thread_list).actionView
-        val badgeFrameLayout = menuThreadList.findViewById<FrameLayout>(R.id.threadNotificationBadgeFrameLayout)
+        val badgeFrameLayout = menuThreadList?.findViewById<FrameLayout>(R.id.threadNotificationBadgeFrameLayout) ?: return
         val badgeTextView = menuThreadList.findViewById<TextView>(R.id.threadNotificationBadgeTextView)
 
         val unreadThreadMessages = state.threadNotificationBadgeState.numberOfLocalUnreadThreads
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt
index eed596cda0..06f438d78e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchActivity.kt
@@ -25,6 +25,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySearchBinding
+import im.vector.lib.core.utils.compat.getParcelableCompat
 
 @AndroidEntryPoint
 class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() {
@@ -46,7 +47,7 @@ class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() {
 
     override fun initUiAndData() {
         if (isFirstCreation()) {
-            val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return
+            val fragmentArgs: SearchArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
             addFragment(views.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG)
         }
         views.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt
index 71a24da5ae..0ee0beb2e6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt
@@ -34,6 +34,7 @@ import im.vector.app.features.home.room.detail.arguments.TimelineArgs
 import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
 import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
 import im.vector.app.features.home.room.threads.list.views.ThreadListFragment
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -129,8 +130,8 @@ class ThreadsActivity : VectorBaseActivity<ActivityThreadsBinding>() {
         return DisplayFragment.ErrorFragment
     }
 
-    private fun getThreadTimelineArgs(): ThreadTimelineArgs? = intent?.extras?.getParcelable(THREAD_TIMELINE_ARGS)
-    private fun getThreadListArgs(): ThreadListArgs? = intent?.extras?.getParcelable(THREAD_LIST_ARGS)
+    private fun getThreadTimelineArgs(): ThreadTimelineArgs? = intent?.extras?.getParcelableCompat(THREAD_TIMELINE_ARGS)
+    private fun getThreadListArgs(): ThreadListArgs? = intent?.extras?.getParcelableCompat(THREAD_LIST_ARGS)
     private fun getEventIdToNavigate(): String? = intent?.extras?.getString(THREAD_EVENT_ID_TO_NAVIGATE)
 
     companion object {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt
index ef07067bac..f91fe9bd91 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/list/views/ThreadListFragment.kt
@@ -78,7 +78,7 @@ class ThreadListFragment :
     override fun handlePostCreateMenu(menu: Menu) {
         // We use a custom layout for this menu item, so we need to set a ClickListener
         menu.findItem(R.id.menu_thread_list_filter)?.let { menuItem ->
-            menuItem.actionView.debouncedClicks {
+            menuItem.actionView?.debouncedClicks {
                 handleMenuItemSelected(menuItem)
             }
         }
@@ -96,7 +96,7 @@ class ThreadListFragment :
 
     override fun handlePrepareMenu(menu: Menu) {
         withState(threadListViewModel) { state ->
-            val filterIcon = menu.findItem(R.id.menu_thread_list_filter).actionView
+            val filterIcon = menu.findItem(R.id.menu_thread_list_filter).actionView ?: return@withState
             val filterBadge = filterIcon.findViewById<View>(R.id.threadListFilterBadge)
             filterBadge.isVisible = state.shouldFilterThreads
             when (threadListViewModel.canHomeserverUseThreading()) {
diff --git a/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt b/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt
index 5bdd92dcf4..26024148f4 100644
--- a/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt
+++ b/vector/src/main/java/im/vector/app/features/lifecycle/VectorActivityLifecycleCallbacks.kt
@@ -34,6 +34,7 @@ import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.matrix.android.sdk.api.extensions.tryOrNull
+import org.matrix.android.sdk.api.util.getPackageInfoCompat
 import timber.log.Timber
 
 class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager: PopupAlertManager) : Application.ActivityLifecycleCallbacks {
@@ -64,16 +65,16 @@ class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager
             val packageManager: PackageManager = context.packageManager
 
             // Get all activities from element android
-            activitiesInfo = packageManager.getPackageInfo(context.packageName, PackageManager.GET_ACTIVITIES).activities
+            activitiesInfo = packageManager.getPackageInfoCompat(context.packageName, PackageManager.GET_ACTIVITIES).activities
 
             // Get all activities from PermissionController module
             // See https://source.android.com/docs/core/architecture/modular-system/permissioncontroller#package-format
             if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
                 activitiesInfo += tryOrNull {
-                    packageManager.getPackageInfo("com.google.android.permissioncontroller", PackageManager.GET_ACTIVITIES).activities
+                    packageManager.getPackageInfoCompat("com.google.android.permissioncontroller", PackageManager.GET_ACTIVITIES).activities
                 } ?: tryOrNull {
                     packageManager.getModuleInfo("com.google.android.permission", 1).packageName?.let {
-                        packageManager.getPackageInfo(it, PackageManager.GET_ACTIVITIES or PackageManager.MATCH_APEX).activities
+                        packageManager.getPackageInfoCompat(it, PackageManager.GET_ACTIVITIES or PackageManager.MATCH_APEX).activities
                     }
                 }.orEmpty()
             }
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt
index 9eddcad649..b4d2a1a565 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingActivity.kt
@@ -24,6 +24,7 @@ import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityLocationSharingBinding
 import im.vector.app.features.location.preview.LocationPreviewFragment
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.parcelize.Parcelize
 
 @Parcelize
@@ -40,7 +41,7 @@ class LocationSharingActivity : VectorBaseActivity<ActivityLocationSharingBindin
     override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
 
     override fun initUiAndData() {
-        val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelable(EXTRA_LOCATION_SHARING_ARGS)
+        val locationSharingArgs: LocationSharingArgs? = intent?.extras?.getParcelableCompat(EXTRA_LOCATION_SHARING_ARGS)
         if (locationSharingArgs == null) {
             finish()
             return
diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewActivity.kt b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewActivity.kt
index 149e80d81a..b6304c0b5a 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/map/LiveLocationMapViewActivity.kt
@@ -25,6 +25,7 @@ import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityLocationSharingBinding
 import im.vector.app.features.MainActivity
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.parcelize.Parcelize
 
 @Parcelize
@@ -38,7 +39,7 @@ class LiveLocationMapViewActivity : VectorBaseActivity<ActivityLocationSharingBi
     override fun getBinding() = ActivityLocationSharingBinding.inflate(layoutInflater)
 
     override fun initUiAndData() {
-        val mapViewArgs: LiveLocationMapViewArgs? = intent?.extras?.getParcelable(EXTRA_LIVE_LOCATION_MAP_VIEW_ARGS)
+        val mapViewArgs: LiveLocationMapViewArgs? = intent?.extras?.getParcelableCompat(EXTRA_LIVE_LOCATION_MAP_VIEW_ARGS)
         if (mapViewArgs == null) {
             finish()
             return
diff --git a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt
index 819ec8634e..545f98f01e 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/tracking/LocationSharingAndroidService.kt
@@ -29,6 +29,7 @@ import im.vector.app.features.location.LocationTracker
 import im.vector.app.features.location.live.GetLiveLocationShareSummaryUseCase
 import im.vector.app.features.redaction.CheckIfEventIsRedactedUseCase
 import im.vector.app.features.session.coroutineScope
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -95,7 +96,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
         startInProgress = true
 
-        val roomArgs = intent?.getParcelableExtra(EXTRA_ROOM_ARGS) as? RoomArgs
+        val roomArgs = intent?.getParcelableExtraCompat(EXTRA_ROOM_ARGS) as? RoomArgs
 
         Timber.i("onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}")
 
@@ -191,7 +192,7 @@ class LocationSharingAndroidService : VectorAndroidService(), LocationTracker.Ca
     }
 
     override fun onNoLocationProviderAvailable() {
-        stopForeground(true)
+        stopForegroundCompat()
         stopSelf()
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
index 763d1eed38..90917dfecb 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
@@ -45,6 +45,7 @@ import im.vector.app.features.login.terms.LoginTermsFragment
 import im.vector.app.features.login.terms.LoginTermsFragmentArgument
 import im.vector.app.features.onboarding.AuthenticationDescription
 import im.vector.app.features.pin.UnlockedActivity
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import org.matrix.android.sdk.api.auth.registration.FlowResult
 import org.matrix.android.sdk.api.auth.registration.Stage
 import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms
@@ -96,7 +97,7 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
         loginViewModel.observeViewEvents { handleLoginViewEvents(it) }
 
         // Get config extra
-        val loginConfig = intent.getParcelableExtra<LoginConfig?>(EXTRA_CONFIG)
+        val loginConfig = intent.getParcelableExtraCompat<LoginConfig?>(EXTRA_CONFIG)
         if (isFirstCreation()) {
             loginViewModel.handle(LoginAction.InitWith(loginConfig))
         }
diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
index 08d87b528c..6177fb5857 100644
--- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
@@ -47,6 +47,8 @@ import im.vector.app.features.themes.ActivityOtherThemes
 import im.vector.app.features.themes.ThemeUtils
 import im.vector.lib.attachmentviewer.AttachmentCommands
 import im.vector.lib.attachmentviewer.AttachmentViewerActivity
+import im.vector.lib.core.utils.compat.getParcelableArrayListExtraCompat
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -105,7 +107,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
                 transitionImageContainer.isVisible = true
 
                 // Postpone transaction a bit until thumbnail is loaded
-                val mediaData: Parcelable? = intent.getParcelableExtra(EXTRA_IMAGE_DATA)
+                val mediaData: Parcelable? = intent.getParcelableExtraCompat(EXTRA_IMAGE_DATA)
                 if (mediaData is ImageContentRenderer.Data) {
                     // will be shown at end of transition
                     pager2.isInvisible = true
@@ -130,7 +132,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
 
         val room = args.roomId?.let { session.getRoom(it) }
 
-        val inMemoryData = intent.getParcelableArrayListExtra<AttachmentData>(EXTRA_IN_MEMORY_DATA)
+        val inMemoryData = intent.getParcelableArrayListExtraCompat<AttachmentData>(EXTRA_IN_MEMORY_DATA)
         val sourceProvider = if (inMemoryData != null) {
             initialIndex = inMemoryData.indexOfFirst { it.eventId == args.eventId }.coerceAtLeast(0)
             dataSourceFactory.createProvider(inMemoryData, room, lifecycleScope)
@@ -227,7 +229,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
         return false
     }
 
-    private fun args() = intent.getParcelableExtra<Args>(EXTRA_ARGS)
+    private fun args() = intent.getParcelableExtraCompat<Args>(EXTRA_ARGS)
 
     private fun scheduleStartPostponedTransition(sharedElement: View) {
         sharedElement.viewTreeObserver.addOnPreDrawListener(
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
index 6fe1b5b7bb..7467104da1 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
@@ -54,6 +54,7 @@ import im.vector.app.features.onboarding.OnboardingViewState
 import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthLegacyStyleTermsFragment
 import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsFragment
 import im.vector.app.features.onboarding.ftueauth.terms.FtueAuthTermsLegacyStyleFragmentArgument
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import org.matrix.android.sdk.api.auth.registration.Stage
 import org.matrix.android.sdk.api.auth.toLocalizedLoginTerms
 import org.matrix.android.sdk.api.extensions.tryOrNull
@@ -107,7 +108,7 @@ class FtueAuthVariant(
         }
 
         // Get config extra
-        val loginConfig = activity.intent.getParcelableExtra<LoginConfig?>(OnboardingActivity.EXTRA_CONFIG)
+        val loginConfig = activity.intent.getParcelableExtraCompat<LoginConfig?>(OnboardingActivity.EXTRA_CONFIG)
         if (isFirstCreation) {
             onboardingViewModel.handle(OnboardingAction.InitWith(loginConfig))
         }
diff --git a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt
index faf15d8006..cef0064fd0 100644
--- a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt
@@ -23,6 +23,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleBinding
+import im.vector.lib.core.utils.compat.getParcelableCompat
 
 @AndroidEntryPoint
 class PinActivity : VectorBaseActivity<ActivitySimpleBinding>(), UnlockedActivity {
@@ -41,7 +42,7 @@ class PinActivity : VectorBaseActivity<ActivitySimpleBinding>(), UnlockedActivit
 
     override fun initUiAndData() {
         if (isFirstCreation()) {
-            val fragmentArgs: PinArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return
+            val fragmentArgs: PinArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
             addFragment(views.simpleFragmentContainer, PinFragment::class.java, fragmentArgs)
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollActivity.kt b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollActivity.kt
index 5a93e19b1a..3dfec3c255 100644
--- a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollActivity.kt
@@ -23,6 +23,7 @@ import android.view.View
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.lib.core.utils.compat.getParcelableCompat
 
 @AndroidEntryPoint
 class CreatePollActivity : SimpleFragmentActivity() {
@@ -31,7 +32,7 @@ class CreatePollActivity : SimpleFragmentActivity() {
         super.onCreate(savedInstanceState)
         views.toolbar.visibility = View.GONE
 
-        val createPollArgs: CreatePollArgs? = intent?.extras?.getParcelable(EXTRA_CREATE_POLL_ARGS)
+        val createPollArgs: CreatePollArgs? = intent?.extras?.getParcelableCompat(EXTRA_CREATE_POLL_ARGS)
 
         if (isFirstCreation()) {
             addFragment(
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt
index c7f549c5cb..b19a98b3ce 100755
--- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt
@@ -128,7 +128,7 @@ class BugReportActivity :
             val isValid = !views.bugReportMaskView.isVisible
 
             it.isEnabled = isValid
-            it.icon.alpha = if (isValid) 255 else 100
+            it.icon?.alpha = if (isValid) 255 else 100
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt
index f9be57b13f..24a45872d5 100644
--- a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt
@@ -146,7 +146,7 @@ class EmojiReactionPickerActivity :
         val searchItem = menu.findItem(R.id.search)
         (searchItem.actionView as? SearchView)?.let { searchView ->
             searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
-                override fun onMenuItemActionExpand(p0: MenuItem?): Boolean {
+                override fun onMenuItemActionExpand(p0: MenuItem): Boolean {
                     searchView.isIconified = false
                     searchView.requestFocusFromTouch()
                     // we want to force the tool bar as visible even if hidden with scroll flags
@@ -154,7 +154,7 @@ class EmojiReactionPickerActivity :
                     return true
                 }
 
-                override fun onMenuItemActionCollapse(p0: MenuItem?): Boolean {
+                override fun onMenuItemActionCollapse(p0: MenuItem): Boolean {
                     // when back, clear all search
                     views.emojiPickerToolbar.minimumHeight = 0
                     searchView.setQuery("", true)
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt
index d2b9b16086..171d51e811 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt
@@ -29,6 +29,7 @@ import im.vector.app.databinding.ActivitySimpleBinding
 import im.vector.app.features.analytics.plan.MobileScreen
 import im.vector.app.features.roomdirectory.RoomDirectorySharedAction
 import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 
@@ -46,7 +47,7 @@ class CreateRoomActivity : VectorBaseActivity<ActivitySimpleBinding>() {
 
     override fun initUiAndData() {
         if (isFirstCreation()) {
-            val fragmentArgs: CreateRoomArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return
+            val fragmentArgs: CreateRoomArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
             addFragment(
                     views.simpleFragmentContainer,
                     CreateRoomFragment::class.java,
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt
index b69788b1ed..a70fd5ef23 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewActivity.kt
@@ -24,6 +24,7 @@ import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleBinding
 import im.vector.app.features.roomdirectory.RoomDirectoryData
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.parcelize.Parcelize
 import org.matrix.android.sdk.api.session.permalinks.PermalinkData
 import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
@@ -83,7 +84,7 @@ class RoomPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
 
     override fun initUiAndData() {
         if (isFirstCreation()) {
-            val args = intent.getParcelableExtra<RoomPreviewData>(ARG)
+            val args = intent.getParcelableExtraCompat<RoomPreviewData>(ARG)
 
             if (args?.worldReadable == true) {
                 // TODO Room preview: Note: M does not recommend to use /events anymore, so for now we just display the room preview
diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt
index 1b55207743..26a2a7de22 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt
@@ -28,6 +28,7 @@ import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleBinding
 import im.vector.app.features.room.RequireActiveMembershipViewEvents
 import im.vector.app.features.room.RequireActiveMembershipViewModel
+import im.vector.lib.core.utils.compat.getParcelableCompat
 
 @AndroidEntryPoint
 class RoomMemberProfileActivity : VectorBaseActivity<ActivitySimpleBinding>() {
@@ -48,7 +49,7 @@ class RoomMemberProfileActivity : VectorBaseActivity<ActivitySimpleBinding>() {
 
     override fun initUiAndData() {
         if (isFirstCreation()) {
-            val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return
+            val fragmentArgs: RoomMemberProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
             addFragment(views.simpleFragmentContainer, RoomMemberProfileFragment::class.java, fragmentArgs)
         }
 
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
index 12f1db1366..2bb3e26384 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
@@ -38,6 +38,7 @@ import im.vector.app.features.roomprofile.notifications.RoomNotificationSettings
 import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
 import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
 import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import javax.inject.Inject
@@ -77,7 +78,7 @@ class RoomProfileActivity :
 
     override fun initUiAndData() {
         sharedActionViewModel = viewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
-        roomProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return
+        roomProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
         if (isFirstCreation()) {
             when (intent?.extras?.getInt(EXTRA_DIRECT_ACCESS, EXTRA_DIRECT_ACCESS_ROOM_ROOT)) {
                 EXTRA_DIRECT_ACCESS_ROOM_SETTINGS -> {
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt
index e7b0087755..205fb86377 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt
@@ -43,6 +43,7 @@ import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRul
 import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedFragment
 import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedState
 import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -58,7 +59,7 @@ class RoomJoinRuleActivity : VectorBaseActivity<ActivitySimpleBinding>() {
     val viewModel: RoomJoinRuleChooseRestrictedViewModel by viewModel()
 
     override fun initUiAndData() {
-        roomProfileArgs = intent?.extras?.getParcelable(Mavericks.KEY_ARG) ?: return
+        roomProfileArgs = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) ?: return
         if (isFirstCreation()) {
             addFragment(
                     views.simpleFragmentContainer,
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
index 261f30ebe3..9a84412482 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
@@ -34,6 +34,7 @@ import im.vector.app.features.navigation.SettingsActivityPayload
 import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment
 import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment
 import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import org.matrix.android.sdk.api.failure.GlobalError
 import org.matrix.android.sdk.api.session.Session
 import timber.log.Timber
@@ -194,8 +195,8 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
     }
 }
 
-private fun <T : Parcelable> Activity.readPayload(default: T): T {
-    return intent.getParcelableExtra(KEY_ACTIVITY_PAYLOAD) ?: default
+private inline fun <reified T : Parcelable> Activity.readPayload(default: T): T {
+    return intent.getParcelableExtraCompat<T>(KEY_ACTIVITY_PAYLOAD) ?: default
 }
 
 private fun <T : Parcelable> Intent.applyPayload(payload: T): Intent {
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt
index 28e167779d..1cb7dcbf28 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsVoiceVideoFragment.kt
@@ -29,6 +29,7 @@ import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.preference.VectorPreference
 import im.vector.app.core.utils.RingtoneUtils
 import im.vector.app.features.analytics.plan.MobileScreen
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -69,7 +70,7 @@ class VectorSettingsVoiceVideoFragment : VectorSettingsBaseFragment() {
 
     private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult ->
         if (activityResult.resultCode == Activity.RESULT_OK) {
-            val callRingtoneUri: Uri? = activityResult.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
+            val callRingtoneUri: Uri? = activityResult.data?.getParcelableExtraCompat(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
             if (callRingtoneUri != null) {
                 ringtoneUtils.setCallRingtoneUri(callRingtoneUri)
                 mCallRingtonePreference.summary = ringtoneUtils.getCallRingtoneName()
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsActivity.kt
index 101bf1da2e..fb4e94b630 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/details/SessionDetailsActivity.kt
@@ -23,6 +23,7 @@ import com.airbnb.mvrx.Mavericks
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 
 /**
  * Display the details info about a Session.
@@ -37,7 +38,7 @@ class SessionDetailsActivity : SimpleFragmentActivity() {
             addFragment(
                     container = views.container,
                     fragmentClass = SessionDetailsFragment::class.java,
-                    params = intent.getParcelableExtra(Mavericks.KEY_ARG)
+                    params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
             )
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsActivity.kt
index f146f77690..81e38e0e9d 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsActivity.kt
@@ -26,6 +26,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 
 @AndroidEntryPoint
 class OtherSessionsActivity : SimpleFragmentActivity() {
@@ -39,7 +40,7 @@ class OtherSessionsActivity : SimpleFragmentActivity() {
             addFragment(
                     container = views.container,
                     fragmentClass = OtherSessionsFragment::class.java,
-                    params = intent.getParcelableExtra(Mavericks.KEY_ARG)
+                    params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
             )
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewActivity.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewActivity.kt
index 015fcccf51..5695d02ae9 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewActivity.kt
@@ -23,6 +23,7 @@ import com.airbnb.mvrx.Mavericks
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 
 /**
  * Display the overview info about a Session.
@@ -37,7 +38,7 @@ class SessionOverviewActivity : SimpleFragmentActivity() {
             addFragment(
                     container = views.container,
                     fragmentClass = SessionOverviewFragment::class.java,
-                    params = intent.getParcelableExtra(Mavericks.KEY_ARG)
+                    params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
             )
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionActivity.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionActivity.kt
index eb0d994ce3..44bc251382 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/rename/RenameSessionActivity.kt
@@ -25,6 +25,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleBinding
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 
 /**
  * Display the screen to rename a Session.
@@ -42,7 +43,7 @@ class RenameSessionActivity : VectorBaseActivity<ActivitySimpleBinding>() {
             addFragment(
                     container = views.simpleFragmentContainer,
                     fragmentClass = RenameSessionFragment::class.java,
-                    params = intent.getParcelableExtra(Mavericks.KEY_ARG)
+                    params = intent.getParcelableExtraCompat(Mavericks.KEY_ARG)
             )
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
index 37d09d02c9..ad12ca1be0 100644
--- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
@@ -53,6 +53,7 @@ import im.vector.app.features.settings.BackgroundSyncModeChooserDialog
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.VectorSettingsBaseFragment
 import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.tryOrNull
@@ -342,7 +343,7 @@ class VectorSettingsNotificationPreferenceFragment :
 
     private val ringtoneStartForActivityResult = registerStartForActivityResult { activityResult ->
         if (activityResult.resultCode == Activity.RESULT_OK) {
-            vectorPreferences.setNotificationRingTone(activityResult.data?.getParcelableExtra<Parcelable>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) as Uri?)
+            vectorPreferences.setNotificationRingTone(activityResult.data?.getParcelableExtraCompat<Uri>(RingtoneManager.EXTRA_RINGTONE_PICKED_URI))
 
             // test if the selected ring tone can be played
             val notificationRingToneName = vectorPreferences.getNotificationRingToneName()
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt
index 6463f6b022..b36dd2df4d 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt
@@ -40,6 +40,7 @@ import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
 import im.vector.app.features.spaces.explore.SpaceDirectoryViewAction
 import im.vector.app.features.spaces.explore.SpaceDirectoryViewEvents
 import im.vector.app.features.spaces.explore.SpaceDirectoryViewModel
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 
 @AndroidEntryPoint
 class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>(), MatrixToBottomSheet.InteractionListener {
@@ -80,7 +81,7 @@ class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>(), Matrix
         supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false)
 
         if (isFirstCreation()) {
-            val args = intent?.getParcelableExtra<SpaceDirectoryArgs>(Mavericks.KEY_ARG)
+            val args = intent?.getParcelableExtraCompat<SpaceDirectoryArgs>(Mavericks.KEY_ARG)
             replaceFragment(
                     views.simpleFragmentContainer,
                     SpaceDirectoryFragment::class.java,
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt
index db4c2cbe65..06d413e0b2 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpacePreviewActivity.kt
@@ -27,6 +27,7 @@ import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleBinding
 import im.vector.app.features.spaces.preview.SpacePreviewArgs
 import im.vector.app.features.spaces.preview.SpacePreviewFragment
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 
@@ -53,7 +54,7 @@ class SpacePreviewActivity : VectorBaseActivity<ActivitySimpleBinding>() {
                 .launchIn(lifecycleScope)
 
         if (isFirstCreation()) {
-            val args = intent?.getParcelableExtra<SpacePreviewArgs>(Mavericks.KEY_ARG)
+            val args = intent?.getParcelableExtraCompat<SpacePreviewArgs>(Mavericks.KEY_ARG)
             replaceFragment(
                     views.simpleFragmentContainer,
                     SpacePreviewFragment::class.java,
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
index f661b92dac..80959c61a1 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
@@ -36,6 +36,7 @@ import im.vector.app.core.extensions.setTextOrHide
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleLoadingBinding
 import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import javax.inject.Inject
 
 @AndroidEntryPoint
@@ -63,7 +64,7 @@ class SpaceLeaveAdvancedActivity : VectorBaseActivity<ActivitySimpleLoadingBindi
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        val args = intent?.getParcelableExtra<SpaceBottomSheetSettingsArgs>(Mavericks.KEY_ARG)
+        val args = intent?.getParcelableExtraCompat<SpaceBottomSheetSettingsArgs>(Mavericks.KEY_ARG)
 
         if (isFirstCreation()) {
             replaceFragment(
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
index ea06a12f08..da6b43aede 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
@@ -150,12 +150,12 @@ class SpaceLeaveAdvancedFragment :
             onTextChanged: (String) -> Unit
     ) {
         setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
-            override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
+            override fun onMenuItemActionExpand(item: MenuItem): Boolean {
                 onExpanded()
                 return true
             }
 
-            override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
+            override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
                 onCollapsed()
                 return true
             }
diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt
index d5d4c86563..a246bd29aa 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt
@@ -40,6 +40,7 @@ import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment
 import im.vector.app.features.roomprofile.RoomProfileArgs
 import im.vector.app.features.roomprofile.alias.RoomAliasFragment
 import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.parcelize.Parcelize
@@ -90,7 +91,7 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() {
                 }
                 .launchIn(lifecycleScope)
 
-        val args = intent?.getParcelableExtra<SpaceManageArgs>(Mavericks.KEY_ARG)
+        val args = intent?.getParcelableExtraCompat<SpaceManageArgs>(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
             withState(sharedViewModel) {
                 when (it.manageType) {
diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt
index f214f77724..0e7a9e4edf 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt
@@ -32,6 +32,7 @@ import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleLoadingBinding
 import im.vector.app.features.analytics.plan.ViewRoom
 import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 
@@ -63,7 +64,7 @@ class SpacePeopleActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        val args = intent?.getParcelableExtra<GenericIdArgs>(Mavericks.KEY_ARG)
+        val args = intent?.getParcelableExtraCompat<GenericIdArgs>(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
             replaceFragment(
                     views.simpleFragmentContainer,
diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt
index 5466ef2339..d261a8d160 100644
--- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt
@@ -25,6 +25,7 @@ import im.vector.app.R
 import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import org.matrix.android.sdk.api.session.terms.TermsService
 import javax.inject.Inject
 
@@ -42,7 +43,7 @@ class ReviewTermsActivity : SimpleFragmentActivity() {
             replaceFragment(views.container, ReviewTermsFragment::class.java)
         }
 
-        reviewTermsViewModel.termsArgs = intent.getParcelableExtra(EXTRA_INFO) ?: error("Missing parameter")
+        reviewTermsViewModel.termsArgs = intent.getParcelableExtraCompat(EXTRA_INFO) ?: error("Missing parameter")
 
         reviewTermsViewModel.observeViewEvents {
             when (it) {
diff --git a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
index 0678d5143d..e21bef193f 100644
--- a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
@@ -24,6 +24,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityVectorWebViewBinding
+import im.vector.lib.core.utils.compat.getSerializableCompat
 import org.matrix.android.sdk.api.session.Session
 import javax.inject.Inject
 
@@ -79,7 +80,7 @@ class VectorWebViewActivity : VectorBaseActivity<ActivityVectorWebViewBinding>()
             setTitle(title)
         }
 
-        val webViewMode = intent.extras?.getSerializable(EXTRA_MODE) as WebViewMode
+        val webViewMode = intent.extras?.getSerializableCompat<WebViewMode>(EXTRA_MODE)!!
         val eventListener = webViewMode.eventListener(this, session)
         views.simpleWebview.webViewClient = VectorWebViewClient(eventListener)
         views.simpleWebview.webChromeClient = object : WebChromeClient() {
diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt
index 91aa3a4e6a..586e4dcc6e 100644
--- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt
@@ -43,6 +43,8 @@ import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
 import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewEvents
 import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel
+import im.vector.lib.core.utils.compat.getParcelableCompat
+import im.vector.lib.core.utils.compat.getSerializableCompat
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.events.model.Content
 import java.io.Serializable
@@ -68,7 +70,7 @@ class WidgetActivity : VectorBaseActivity<ActivityWidgetBinding>() {
 
         @Suppress("UNCHECKED_CAST")
         fun getOutput(intent: Intent): Content? {
-            return intent.extras?.getSerializable(EXTRA_RESULT) as? Content
+            return intent.extras?.getSerializableCompat(EXTRA_RESULT) as? Content
         }
 
         private fun createResultIntent(content: Content): Intent {
@@ -88,7 +90,7 @@ class WidgetActivity : VectorBaseActivity<ActivityWidgetBinding>() {
     override fun getTitleRes() = R.string.room_widget_activity_title
 
     override fun initUiAndData() {
-        val widgetArgs: WidgetArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG)
+        val widgetArgs: WidgetArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG)
         if (widgetArgs == null) {
             finish()
             return
@@ -150,7 +152,7 @@ class WidgetActivity : VectorBaseActivity<ActivityWidgetBinding>() {
 
     override fun onUserLeaveHint() {
         super.onUserLeaveHint()
-        val widgetArgs: WidgetArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG)
+        val widgetArgs: WidgetArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG)
         if (widgetArgs?.kind?.supportsPictureInPictureMode().orFalse()) {
             enterPictureInPicture()
         }
diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt
index 4d94493d17..944ee627d7 100644
--- a/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetFragment.kt
@@ -52,6 +52,7 @@ import im.vector.app.features.webview.WebEventListener
 import im.vector.app.features.widgets.webview.WebviewPermissionUtils
 import im.vector.app.features.widgets.webview.clearAfterWidget
 import im.vector.app.features.widgets.webview.setupForWidget
+import im.vector.lib.core.utils.compat.resolveActivityCompat
 import kotlinx.parcelize.Parcelize
 import org.matrix.android.sdk.api.session.terms.TermsService
 import timber.log.Timber
@@ -264,7 +265,7 @@ class WidgetFragment :
                 val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
                 if (intent != null) {
                     val packageManager: PackageManager = context.packageManager
-                    val info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+                    val info = packageManager.resolveActivityCompat(intent, PackageManager.MATCH_DEFAULT_ONLY)
                     if (info != null) {
                         context.startActivity(intent)
                     } else {

From e76b2d7c61e190cc73844f348367743e2acd89e1 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 28 Sep 2022 14:43:00 +0200
Subject: [PATCH 013/400] Suppress deprecation warning for `onBackPressed()`.
 The required change are too big, this will be handled separately.

---
 .../im/vector/app/core/platform/SimpleFragmentActivity.kt    | 1 +
 .../java/im/vector/app/core/platform/VectorBaseActivity.kt   | 1 +
 .../java/im/vector/app/features/call/VectorCallActivity.kt   | 2 ++
 .../app/features/createdirect/CreateDirectRoomActivity.kt    | 1 +
 .../crypto/keysbackup/restore/KeysBackupRestoreActivity.kt   | 1 +
 .../crypto/keysbackup/settings/KeysBackupManageActivity.kt   | 1 +
 .../crypto/keysbackup/setup/KeysBackupSetupActivity.kt       | 1 +
 .../main/java/im/vector/app/features/home/HomeActivity.kt    | 5 ++++-
 .../app/features/home/room/detail/RoomDetailActivity.kt      | 1 +
 .../vector/app/features/invite/InviteUsersToRoomActivity.kt  | 1 +
 .../im/vector/app/features/login/AbstractLoginFragment.kt    | 2 ++
 .../main/java/im/vector/app/features/login/LoginActivity.kt  | 5 ++++-
 .../app/features/media/VectorAttachmentViewerActivity.kt     | 1 +
 .../im/vector/app/features/onboarding/OnboardingActivity.kt  | 5 ++++-
 .../features/onboarding/ftueauth/AbstractFtueAuthFragment.kt | 2 ++
 .../im/vector/app/features/rageshake/BugReportActivity.kt    | 1 +
 .../features/roomdirectory/createroom/CreateRoomFragment.kt  | 5 ++++-
 .../features/roomprofile/settings/RoomSettingsFragment.kt    | 1 +
 .../app/features/spaces/manage/SpaceSettingsFragment.kt      | 1 +
 .../java/im/vector/app/features/usercode/UserCodeActivity.kt | 5 ++++-
 .../im/vector/app/features/webview/VectorWebViewActivity.kt  | 1 +
 21 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt
index 4cd7da2a4f..794d5c7886 100644
--- a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt
@@ -83,6 +83,7 @@ abstract class SimpleFragmentActivity : VectorBaseActivity<ActivityBinding>() {
             // ignore
             return
         }
+        @Suppress("DEPRECATION")
         super.onBackPressed()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index 0b8d6698d2..7e61958565 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -505,6 +505,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
     private fun onBackPressed(fromToolbar: Boolean) {
         val handled = recursivelyDispatchOnBackPressed(supportFragmentManager, fromToolbar)
         if (!handled) {
+            @Suppress("DEPRECATION")
             super.onBackPressed()
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
index 7e226304c1..a96097a830 100644
--- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt
@@ -194,6 +194,7 @@ class VectorCallActivity :
 
     override fun onBackPressed() {
         if (!enterPictureInPictureIfRequired()) {
+            @Suppress("DEPRECATION")
             super.onBackPressed()
         }
     }
@@ -231,6 +232,7 @@ class VectorCallActivity :
             }
             android.R.id.home -> {
                 // We check here as we want PiP in some cases
+                @Suppress("DEPRECATION")
                 onBackPressed()
                 true
             }
diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt
index 707b78d328..fd28235a51 100644
--- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt
@@ -78,6 +78,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
         sharedActionViewModel
                 .stream()
                 .onEach { action ->
+                    @Suppress("DEPRECATION")
                     when (action) {
                         UserListSharedAction.Close -> finish()
                         UserListSharedAction.GoBack -> onBackPressed()
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
index c6e86f6f6b..f7964cf0ed 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
@@ -52,6 +52,7 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
 
     override fun onBackPressed() {
         hideWaitingView()
+        @Suppress("DEPRECATION")
         super.onBackPressed()
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt
index aabd05d913..32826d013f 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupManageActivity.kt
@@ -114,6 +114,7 @@ class KeysBackupManageActivity : SimpleFragmentActivity() {
             finish()
             return
         }
+        @Suppress("DEPRECATION")
         super.onBackPressed()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
index 077bcc2cf3..8238da8245 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
@@ -183,6 +183,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
                     }
                     .show()
         } else {
+            @Suppress("DEPRECATION")
             super.onBackPressed()
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 689d141023..18c9e7e3b9 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -668,7 +668,10 @@ class HomeActivity :
         if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) {
             views.drawerLayout.closeDrawer(GravityCompat.START)
         } else {
-            validateBackPressed { super.onBackPressed() }
+            validateBackPressed {
+                @Suppress("DEPRECATION")
+                super.onBackPressed()
+            }
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
index 6b66d0ec47..ecbea133df 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt
@@ -178,6 +178,7 @@ class RoomDetailActivity :
         if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) {
             views.drawerLayout.closeDrawer(GravityCompat.START)
         } else {
+            @Suppress("DEPRECATION")
             super.onBackPressed()
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt
index 883c879e90..86f061849b 100644
--- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt
@@ -68,6 +68,7 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
         sharedActionViewModel
                 .stream()
                 .onEach { sharedAction ->
+                    @Suppress("DEPRECATION")
                     when (sharedAction) {
                         UserListSharedAction.Close -> finish()
                         UserListSharedAction.GoBack -> onBackPressed()
diff --git a/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt
index 9512a518e8..6ee276342c 100644
--- a/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt
@@ -133,6 +133,7 @@ abstract class AbstractLoginFragment<VB : ViewBinding> : VectorBaseFragment<VB>(
                         .setMessage(R.string.login_signup_cancel_confirmation_content)
                         .setPositiveButton(R.string.yes) { _, _ ->
                             displayCancelDialog = false
+                            @Suppress("DEPRECATION")
                             vectorBaseActivity.onBackPressed()
                         }
                         .setNegativeButton(R.string.no, null)
@@ -147,6 +148,7 @@ abstract class AbstractLoginFragment<VB : ViewBinding> : VectorBaseFragment<VB>(
                         .setMessage(R.string.login_reset_password_cancel_confirmation_content)
                         .setPositiveButton(R.string.yes) { _, _ ->
                             displayCancelDialog = false
+                            @Suppress("DEPRECATION")
                             vectorBaseActivity.onBackPressed()
                         }
                         .setNegativeButton(R.string.no, null)
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
index 90917dfecb..4e4df5d1aa 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
@@ -317,7 +317,10 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
     }
 
     override fun onBackPressed() {
-        validateBackPressed { super.onBackPressed() }
+        validateBackPressed {
+            @Suppress("DEPRECATION")
+            super.onBackPressed()
+        }
     }
 
     private fun onRegistrationStageNotSupported() {
diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
index 6177fb5857..d523d99d76 100644
--- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
@@ -175,6 +175,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
             transitionImageContainer.isVisible = true
         }
         isAnimatingOut = true
+        @Suppress("DEPRECATION")
         super.onBackPressed()
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingActivity.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingActivity.kt
index 060472a2da..e79250d39d 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingActivity.kt
@@ -48,7 +48,10 @@ class OnboardingActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
     }
 
     override fun onBackPressed() {
-        validateBackPressed { super.onBackPressed() }
+        validateBackPressed {
+            @Suppress("DEPRECATION")
+            super.onBackPressed()
+        }
     }
 
     override fun initUiAndData() {
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
index f3cb326221..9fd0bd08ed 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
@@ -123,6 +123,7 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
                         .setMessage(R.string.login_signup_cancel_confirmation_content)
                         .setPositiveButton(R.string.yes) { _, _ ->
                             displayCancelDialog = false
+                            @Suppress("DEPRECATION")
                             vectorBaseActivity.onBackPressed()
                         }
                         .setNegativeButton(R.string.no, null)
@@ -137,6 +138,7 @@ abstract class AbstractFtueAuthFragment<VB : ViewBinding> : VectorBaseFragment<V
                         .setMessage(R.string.login_reset_password_cancel_confirmation_content)
                         .setPositiveButton(R.string.yes) { _, _ ->
                             displayCancelDialog = false
+                            @Suppress("DEPRECATION")
                             vectorBaseActivity.onBackPressed()
                         }
                         .setNegativeButton(R.string.no, null)
diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt
index b19a98b3ce..78d3347cbe 100755
--- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt
@@ -267,6 +267,7 @@ class BugReportActivity :
         // Ensure there is no crash status remaining, which will be sent later on by mistake
         bugReporter.deleteCrashFile()
 
+        @Suppress("DEPRECATION")
         super.onBackPressed()
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt
index f4c3e515c5..59657aae35 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt
@@ -100,7 +100,10 @@ class CreateRoomFragment :
                 .allowBack(useCross = true)
         viewModel.observeViewEvents {
             when (it) {
-                CreateRoomViewEvents.Quit -> vectorBaseActivity.onBackPressed()
+                CreateRoomViewEvents.Quit -> {
+                    @Suppress("DEPRECATION")
+                    vectorBaseActivity.onBackPressed()
+                }
                 is CreateRoomViewEvents.Failure -> showFailure(it.throwable)
             }
         }
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt
index ba50890db3..26da1a45d2 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt
@@ -106,6 +106,7 @@ class RoomSettingsFragment :
                 RoomSettingsViewEvents.Success -> showSuccess()
                 RoomSettingsViewEvents.GoBack -> {
                     ignoreChanges = true
+                    @Suppress("DEPRECATION")
                     vectorBaseActivity.onBackPressed()
                 }
             }
diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt
index ac3e5e4d05..98c81b19bc 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt
@@ -107,6 +107,7 @@ class SpaceSettingsFragment :
                 RoomSettingsViewEvents.Success -> showSuccess()
                 RoomSettingsViewEvents.GoBack -> {
                     ignoreChanges = true
+                    @Suppress("DEPRECATION")
                     vectorBaseActivity.onBackPressed()
                 }
             }
diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt
index cf7d9c6ddb..4fd8305f29 100644
--- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt
@@ -151,7 +151,10 @@ class UserCodeActivity : VectorBaseActivity<ActivitySimpleBinding>(),
 
     override fun onBackPressed() = withState(sharedViewModel) {
         when (it.mode) {
-            UserCodeState.Mode.SHOW -> super.onBackPressed()
+            UserCodeState.Mode.SHOW -> {
+                @Suppress("DEPRECATION")
+                super.onBackPressed()
+            }
             is UserCodeState.Mode.RESULT,
             UserCodeState.Mode.SCAN -> sharedViewModel.handle(UserCodeActions.SwitchMode(UserCodeState.Mode.SHOW))
         }
diff --git a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
index e21bef193f..c97f87972e 100644
--- a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
@@ -101,6 +101,7 @@ class VectorWebViewActivity : VectorBaseActivity<ActivityVectorWebViewBinding>()
         if (views.simpleWebview.canGoBack()) {
             views.simpleWebview.goBack()
         } else {
+            @Suppress("DEPRECATION")
             super.onBackPressed()
         }
     }

From 15616ecc7cc39b2e447b99ee48d872b3371c4c30 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 5 Oct 2022 11:35:46 +0200
Subject: [PATCH 014/400] Fix compilation issue after rebase

---
 .../android/sdk/internal/network/ComputeUserAgentUseCase.kt    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt
index 6611bd7865..04e8cf8a29 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCase.kt
@@ -20,6 +20,7 @@ import android.content.Context
 import android.os.Build
 import org.matrix.android.sdk.BuildConfig
 import org.matrix.android.sdk.api.extensions.tryOrNull
+import org.matrix.android.sdk.api.util.getApplicationInfoCompat
 import org.matrix.android.sdk.api.util.getPackageInfoCompat
 import javax.inject.Inject
 
@@ -37,7 +38,7 @@ class ComputeUserAgentUseCase @Inject constructor(
         val appPackageName = context.applicationContext.packageName
         val pm = context.packageManager
 
-        val appName = tryOrNull { pm.getApplicationLabel(pm.getApplicationInfo(appPackageName, 0)).toString() }
+        val appName = tryOrNull { pm.getApplicationLabel(pm.getApplicationInfoCompat(appPackageName, 0)).toString() }
                 ?.takeIf {
                     it.matches("\\A\\p{ASCII}*\\z".toRegex())
                 }

From 70383130f56e66aa35f4cd23383472bc02c6a958 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 5 Oct 2022 11:37:43 +0200
Subject: [PATCH 015/400] Fix compilation warning: `'compileDebugJavaWithJavac'
 task (current target is 1.8) and 'compileDebugKotlin' task (current target is
 11) jvm target compatibility should be set to the same Java version.`

---
 library/multipicker/build.gradle | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/library/multipicker/build.gradle b/library/multipicker/build.gradle
index 58edfcdd5c..09fc2a7b50 100644
--- a/library/multipicker/build.gradle
+++ b/library/multipicker/build.gradle
@@ -35,6 +35,11 @@ android {
         }
     }
 
+    compileOptions {
+        sourceCompatibility versions.sourceCompat
+        targetCompatibility versions.targetCompat
+    }
+
     kotlinOptions {
         jvmTarget = "11"
     }

From 84223a221cae0fd8ad108940eb7335940967db38 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 5 Oct 2022 11:38:47 +0200
Subject: [PATCH 016/400] Call super method.

---
 .../main/java/com/android/dialer/dialpadview/DialpadView.java    | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java b/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java
index 5c6ce46257..dbb53cbdca 100644
--- a/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java
+++ b/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java
@@ -103,6 +103,7 @@ public class DialpadView extends LinearLayout {
 
   @Override
   protected void onFinishInflate() {
+    super.onFinishInflate();
     setupKeypad();
     mDigits = (EditText) findViewById(R.id.digits);
     mDelete = (ImageButton) findViewById(R.id.deleteButton);

From 05a1f17e8300ed0650057df7bc882a66219da294 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 5 Oct 2022 14:03:27 +0200
Subject: [PATCH 017/400] Remove unused import

---
 .../VectorSettingsNotificationPreferenceFragment.kt              | 1 -
 1 file changed, 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
index ad12ca1be0..b1e32c05d1 100644
--- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
@@ -22,7 +22,6 @@ import android.content.Intent
 import android.media.RingtoneManager
 import android.net.Uri
 import android.os.Bundle
-import android.os.Parcelable
 import android.widget.Toast
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.distinctUntilChanged

From 3952f732dd94594660021809376e9db8a521ffef Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 5 Oct 2022 14:23:31 +0200
Subject: [PATCH 018/400] Paparazzi have some trouble with Android 13, try to
 workaround it. OK locally, but on the CI?

---
 .../app/screenshot/PaparazziExampleScreenshotTest.kt      | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt b/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt
index fb8bed0034..65f89dcc6a 100644
--- a/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt
+++ b/vector/src/test/java/im/vector/app/screenshot/PaparazziExampleScreenshotTest.kt
@@ -16,11 +16,14 @@
 
 package im.vector.app.screenshot
 
+import android.os.Build
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintLayout
 import app.cash.paparazzi.DeviceConfig.Companion.PIXEL_3
 import app.cash.paparazzi.Paparazzi
+import app.cash.paparazzi.androidHome
+import app.cash.paparazzi.detectEnvironment
 import im.vector.app.R
 import org.junit.Rule
 import org.junit.Test
@@ -29,6 +32,11 @@ class PaparazziExampleScreenshotTest {
 
     @get:Rule
     val paparazzi = Paparazzi(
+            // Apply trick from https://github.com/cashapp/paparazzi/issues/489#issuecomment-1195674603
+            environment = detectEnvironment().copy(
+                    platformDir = "${androidHome()}/platforms/android-32",
+                    compileSdkVersion = Build.VERSION_CODES.S_V2 /* 32 */
+            ),
             deviceConfig = PIXEL_3,
             theme = "Theme.Vector.Light",
             maxPercentDifference = 0.0,

From 9859dab3cc7127ad54a69cc6b39768e70257867e Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 00:51:00 +0300
Subject: [PATCH 019/400] Complete qr code login instructions screen.

---
 .../src/main/res/values/strings.xml           |  3 ++
 .../fragment_qr_code_login_instructions.xml   | 49 +++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index a2642caab3..5099b70c1d 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3336,5 +3336,8 @@
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Link a device\'</string>
     <string name="qr_code_login_new_device_instruction_4">Select \'Show QR code on this device\'</string>
+    <string name="qr_code_login_show_qr_code_button">Show QR code in this device</string>
+    <string name="qr_code_login_need_an_alternative_method">Need an alternative method?</string>
+    <string name="qr_code_login_scan_qr_code_button">Scan QR code</string>
 
 </resources>
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
index 9f87b1b4d4..9a7a97a12b 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
@@ -31,4 +31,53 @@
         app:qrCodeLoginInstruction3="@string/qr_code_login_new_device_instruction_3"
         app:qrCodeLoginInstruction4="@string/qr_code_login_new_device_instruction_4" />
 
+    <Button
+        android:id="@+id/qrCodeLoginInstructionsShowQrCodeButton"
+        style="@style/Widget.Vector.Button.Outlined"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="40dp"
+        android:text="@string/qr_code_login_show_qr_code_button"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <FrameLayout
+        android:id="@+id/qrCodeLoginInstructionsAlternativeLayout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        app:layout_constraintBottom_toTopOf="@id/qrCodeLoginInstructionsShowQrCodeButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_gravity="center_vertical"
+            android:background="@drawable/divider_horizontal" />
+
+        <TextView
+            android:id="@+id/qrCodeLoginInstructionsAlternativeTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:background="?android:colorBackground"
+            android:paddingHorizontal="12dp"
+            android:text="@string/qr_code_login_need_an_alternative_method"
+            app:drawableLeftCompat="@drawable/divider_horizontal"
+            app:drawableTint="@color/alert_default_error_background" />
+    </FrameLayout>
+
+    <Button
+        android:id="@+id/qrCodeLoginInstructionsScanQrCodeButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="20dp"
+        android:text="@string/qr_code_login_scan_qr_code_button"
+        android:textAllCaps="false"
+        app:layout_constraintBottom_toTopOf="@id/qrCodeLoginInstructionsAlternativeLayout"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>

From 5f6c8eebcf6b52334894cffcea29262dcc01bc2d Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 12:16:24 +0300
Subject: [PATCH 020/400] Navigate to the instructions screen.

---
 .../src/main/res/values/strings.xml           |  1 +
 .../features/login/qr/QrCodeLoginActivity.kt  | 19 +++++++++++++++++++
 .../features/navigation/DefaultNavigator.kt   | 10 ++++++++++
 .../app/features/navigation/Navigator.kt      |  6 ++++++
 .../ftueauth/FtueAuthCombinedLoginFragment.kt |  3 +++
 .../layout/fragment_ftue_combined_login.xml   | 17 ++++++++++++++---
 .../fragment_qr_code_login_instructions.xml   |  3 ++-
 7 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 5099b70c1d..25daadb453 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -2168,6 +2168,7 @@
     <string name="login_signin_matrix_id_password_notice">If you don’t know your password, go back to reset it.</string>
     <string name="login_signin_matrix_id_error_invalid_matrix_id">This is not a valid user identifier. Expected format: \'@user:homeserver.org\'</string>
     <string name="autodiscover_well_known_error">Unable to find a valid homeserver. Please check your identifier</string>
+    <string name="login_signin_with_qr_code">Sign in with QR code</string>
 
     <string name="seen_by">Seen by</string>
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index e0a5d244e1..bef9d5a040 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -18,12 +18,31 @@ package im.vector.app.features.login.qr
 
 import android.content.Context
 import android.content.Intent
+import android.os.Bundle
+import android.view.View
 import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 
 @AndroidEntryPoint
 class QrCodeLoginActivity : SimpleFragmentActivity() {
 
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        views.toolbar.visibility = View.GONE
+
+        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(EXTRA_QR_CODE_LOGIN_ARGS)
+        if (isFirstCreation()) {
+            if (qrCodeLoginArgs?.loginType == QrCodeLoginType.LOGIN) {
+                addFragment(
+                        views.container,
+                        QrCodeLoginInstructionsFragment::class.java,
+                        qrCodeLoginArgs
+                )
+            }
+        }
+    }
+
     companion object {
         private const val EXTRA_QR_CODE_LOGIN_ARGS = "EXTRA_QR_CODE_LOGIN_ARGS"
 
diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
index 53ed307da9..3970af385e 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
@@ -70,6 +70,8 @@ import im.vector.app.features.location.live.map.LiveLocationMapViewActivity
 import im.vector.app.features.location.live.map.LiveLocationMapViewArgs
 import im.vector.app.features.login.LoginActivity
 import im.vector.app.features.login.LoginConfig
+import im.vector.app.features.login.qr.QrCodeLoginActivity
+import im.vector.app.features.login.qr.QrCodeLoginArgs
 import im.vector.app.features.matrixto.MatrixToBottomSheet
 import im.vector.app.features.matrixto.OriginOfMatrixTo
 import im.vector.app.features.media.AttachmentData
@@ -604,6 +606,14 @@ class DefaultNavigator @Inject constructor(
         activityResultLauncher.launch(screenCaptureIntent)
     }
 
+    override fun openLoginWithQrCode(context: Context, qrCodeLoginArgs: QrCodeLoginArgs) {
+        QrCodeLoginActivity
+                .getIntent(context, qrCodeLoginArgs)
+                .also {
+                    context.startActivity(it)
+                }
+    }
+
     private fun Intent.start(context: Context) {
         context.startActivity(this)
     }
diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
index 3521a02775..1d67f883a3 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
@@ -31,6 +31,7 @@ import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
 import im.vector.app.features.location.LocationData
 import im.vector.app.features.location.LocationSharingMode
 import im.vector.app.features.login.LoginConfig
+import im.vector.app.features.login.qr.QrCodeLoginArgs
 import im.vector.app.features.matrixto.OriginOfMatrixTo
 import im.vector.app.features.media.AttachmentData
 import im.vector.app.features.pin.PinMode
@@ -201,4 +202,9 @@ interface Navigator {
             screenCaptureIntent: Intent,
             activityResultLauncher: ActivityResultLauncher<Intent>
     )
+
+    fun openLoginWithQrCode(
+            context: Context,
+            qrCodeLoginArgs: QrCodeLoginArgs,
+    )
 }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
index 6877810f0a..301e41be4b 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
@@ -40,6 +40,8 @@ import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.SocialLoginButtonsView
 import im.vector.app.features.login.SsoState
+import im.vector.app.features.login.qr.QrCodeLoginArgs
+import im.vector.app.features.login.qr.QrCodeLoginType
 import im.vector.app.features.login.render
 import im.vector.app.features.onboarding.OnboardingAction
 import im.vector.app.features.onboarding.OnboardingViewEvents
@@ -70,6 +72,7 @@ class FtueAuthCombinedLoginFragment :
             viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content()))
         }
         views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) }
+        views.loginWithQrCode.debouncedClicks { navigator.openLoginWithQrCode(requireActivity(), QrCodeLoginArgs(loginType = QrCodeLoginType.LOGIN)) }
     }
 
     private fun setupSubmitButton() {
diff --git a/vector/src/main/res/layout/fragment_ftue_combined_login.xml b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
index 12943b4dc0..5440fcba15 100644
--- a/vector/src/main/res/layout/fragment_ftue_combined_login.xml
+++ b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
@@ -194,9 +194,9 @@
             android:text="@string/ftue_auth_forgot_password"
             android:textAllCaps="true"
             android:textColor="?colorSecondary"
-            app:layout_constraintHorizontal_bias="1"
             app:layout_constraintBottom_toTopOf="@id/actionSpacing"
             app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintHorizontal_bias="1"
             app:layout_constraintStart_toStartOf="@id/loginGutterStart"
             app:layout_constraintTop_toBottomOf="@id/loginPasswordInput" />
 
@@ -220,6 +220,17 @@
             app:layout_constraintStart_toStartOf="@id/loginGutterStart"
             app:layout_constraintTop_toBottomOf="@id/actionSpacing" />
 
+        <Button
+            android:id="@+id/loginWithQrCode"
+            style="@style/Widget.Vector.Button.Outlined.Login"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:text="@string/login_signin_with_qr_code"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/loginSubmit" />
+
         <androidx.constraintlayout.widget.Group
             android:id="@+id/ssoGroup"
             android:layout_width="wrap_content"
@@ -227,7 +238,7 @@
             android:visibility="gone"
             app:constraint_referenced_ids="ssoButtonsHeader,ssoButtons"
             app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
-            app:layout_constraintTop_toBottomOf="@id/loginSubmit"
+            app:layout_constraintTop_toBottomOf="@id/loginWithQrCode"
             tools:visibility="visible" />
 
         <TextView
@@ -242,7 +253,7 @@
             app:layout_constraintBottom_toTopOf="@id/ssoButtons"
             app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
             app:layout_constraintStart_toStartOf="@id/loginGutterStart"
-            app:layout_constraintTop_toBottomOf="@id/loginSubmit" />
+            app:layout_constraintTop_toBottomOf="@id/loginWithQrCode" />
 
         <im.vector.app.features.login.SocialLoginButtonsView
             android:id="@+id/ssoButtons"
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
index 9a7a97a12b..83ea42d7e9 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
@@ -33,7 +33,7 @@
 
     <Button
         android:id="@+id/qrCodeLoginInstructionsShowQrCodeButton"
-        style="@style/Widget.Vector.Button.Outlined"
+        style="@style/Widget.Vector.Button.Outlined.Login"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginBottom="40dp"
@@ -71,6 +71,7 @@
 
     <Button
         android:id="@+id/qrCodeLoginInstructionsScanQrCodeButton"
+        style="@style/Widget.Vector.Button.Login"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginBottom="20dp"

From 5dfaa2527abf8073d250ed1e833b3bbad13524ae Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 14:54:09 +0300
Subject: [PATCH 021/400] Remove unused session parameter.

---
 .../im/vector/app/features/qrcode/QrCodeScannerViewModel.kt     | 2 --
 1 file changed, 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerViewModel.kt b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerViewModel.kt
index e24c57c6de..3167eebc9f 100644
--- a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerViewModel.kt
@@ -24,11 +24,9 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.platform.VectorDummyViewState
 import im.vector.app.core.platform.VectorViewModel
-import org.matrix.android.sdk.api.session.Session
 
 class QrCodeScannerViewModel @AssistedInject constructor(
         @Assisted initialState: VectorDummyViewState,
-        val session: Session
 ) : VectorViewModel<VectorDummyViewState, QrCodeScannerAction, QrCodeScannerEvents>(initialState) {
 
     @AssistedFactory

From 9b7f6c90034e7e2eabc66c11acec1554c592c9be Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 14:55:18 +0300
Subject: [PATCH 022/400] Navigate to qr code scanner activity.

---
 .../qr/QrCodeLoginInstructionsFragment.kt     | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index 68848fad04..d15d01ec12 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -16,14 +16,52 @@
 
 package im.vector.app.features.login.qr
 
+import android.app.Activity
+import android.os.Bundle
 import android.view.LayoutInflater
+import android.view.View
 import android.view.ViewGroup
+import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginInstructionsBinding
+import im.vector.app.features.qrcode.QrCodeScannerActivity
+import timber.log.Timber
 
 class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginInstructionsBinding>() {
 
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        initScanQrCodeButton()
+    }
+
     override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginInstructionsBinding {
         return FragmentQrCodeLoginInstructionsBinding.inflate(inflater, container, false)
     }
+
+    private fun initScanQrCodeButton() {
+        views.qrCodeLoginInstructionsScanQrCodeButton.debouncedClicks {
+            QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
+        }
+    }
+
+    private val scanActivityResultLauncher = registerStartForActivityResult { activityResult ->
+        if (activityResult.resultCode == Activity.RESULT_OK) {
+            val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
+            val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
+
+            if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
+                onQrCodeScanned(scannedQrCode)
+            } else {
+                onQrCodeScannerFailed()
+            }
+        }
+    }
+
+    private fun onQrCodeScanned(scannedQrCode: String) {
+        Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScanned $scannedQrCode")
+    }
+
+    private fun onQrCodeScannerFailed() {
+        Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
+    }
 }

From 945fa0a390e6b296a28b758d8daa6c41653b7a0b Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 14:55:43 +0300
Subject: [PATCH 023/400] Create qr code login status view layout.

---
 .../src/main/res/values/strings.xml           |  5 +
 .../drawable/ic_qr_code_login_connected.xml   |  9 ++
 .../res/drawable/ic_qr_code_login_failed.xml  | 10 ++
 .../layout/fragment_qr_code_login_status.xml  | 96 +++++++++++++++++++
 4 files changed, 120 insertions(+)
 create mode 100644 vector/src/main/res/drawable/ic_qr_code_login_connected.xml
 create mode 100644 vector/src/main/res/drawable/ic_qr_code_login_failed.xml
 create mode 100644 vector/src/main/res/layout/fragment_qr_code_login_status.xml

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 25daadb453..e0cd8f7919 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3333,6 +3333,8 @@
     <!-- QR Code Login -->
     <string name="qr_code_login_header_scan_qr_code_title">Scan QR code</string>
     <string name="qr_code_login_header_scan_qr_code_description">Use the camera on this device to scan the QR code shown on your other device:</string>
+    <string name="qr_code_login_header_connected_title">Secure connection established</string>
+    <string name="qr_code_login_header_connected_description">Check your signed in device, the code below should be displayed. Confirm that the code below matches with that device:</string>
     <string name="qr_code_login_new_device_instruction_1">Open Element on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Link a device\'</string>
@@ -3340,5 +3342,8 @@
     <string name="qr_code_login_show_qr_code_button">Show QR code in this device</string>
     <string name="qr_code_login_need_an_alternative_method">Need an alternative method?</string>
     <string name="qr_code_login_scan_qr_code_button">Scan QR code</string>
+    <string name="qr_code_login_connecting_to_device">Connecting to device</string>
+    <string name="qr_code_login_signing_in">Signing you in</string>
+    <string name="qr_code_login_status_no_match">No match?</string>
 
 </resources>
diff --git a/vector/src/main/res/drawable/ic_qr_code_login_connected.xml b/vector/src/main/res/drawable/ic_qr_code_login_connected.xml
new file mode 100644
index 0000000000..48f5c6a383
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_qr_code_login_connected.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="54dp"
+    android:height="38dp"
+    android:viewportWidth="54"
+    android:viewportHeight="38">
+  <path
+      android:pathData="M53.667,19C53.667,17.533 52.467,16.333 51,16.333H45.48C44.173,7.293 36.413,0.333 27,0.333C17.587,0.333 9.827,7.293 8.52,16.333H3C1.533,16.333 0.333,17.533 0.333,19C0.333,20.467 1.533,21.667 3,21.667H8.52C9.827,30.707 17.587,37.667 27,37.667C36.413,37.667 44.173,30.707 45.48,21.667H51C52.467,21.667 53.667,20.467 53.667,19ZM35,25.667C35,27.133 33.8,28.333 32.333,28.333H21.667C20.2,28.333 19,27.133 19,25.667V17.667C19,16.2 20.2,15 21.667,15V12.333C21.667,9.107 24.547,6.52 27.907,7.08C30.52,7.507 32.333,9.96 32.333,12.627V15C33.8,15 35,16.2 35,17.667V25.667ZM29,21.667C29,22.76 28.093,23.667 27,23.667C25.907,23.667 25,22.76 25,21.667C25,20.573 25.907,19.667 27,19.667C28.093,19.667 29,20.573 29,21.667ZM29.667,12.333V15H24.333V12.333C24.333,10.867 25.533,9.667 27,9.667C28.467,9.667 29.667,10.867 29.667,12.333Z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/vector/src/main/res/drawable/ic_qr_code_login_failed.xml b/vector/src/main/res/drawable/ic_qr_code_login_failed.xml
new file mode 100644
index 0000000000..f49e07c066
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_qr_code_login_failed.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportWidth="40"
+    android:viewportHeight="40">
+  <path
+      android:pathData="M20,40C31.046,40 40,31.046 40,20C40,8.954 31.046,0 20,0C8.954,0 0,8.954 0,20C0,31.046 8.954,40 20,40ZM18.084,14.688C18.007,13.809 18.655,13.037 19.535,12.976C20.399,12.914 21.17,13.562 21.263,14.441V14.688L20.769,20.86C20.723,21.431 20.244,21.863 19.674,21.863H19.581C19.041,21.816 18.624,21.4 18.578,20.86L18.084,14.688ZM21.015,24.887C21.015,25.637 20.407,26.244 19.657,26.244C18.907,26.244 18.3,25.637 18.3,24.887C18.3,24.137 18.907,23.529 19.657,23.529C20.407,23.529 21.015,24.137 21.015,24.887Z"
+      android:fillColor="#ffffff"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_status.xml b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
new file mode 100644
index 0000000000..7fe8ea6c02
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
@@ -0,0 +1,96 @@
+<?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="match_parent">
+
+    <LinearLayout
+        android:id="@+id/qrCodeLoginStatusLoadingLayout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="100dp"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:visibility="visible">
+
+        <TextView
+            android:id="@+id/qrCodeLoginStatusLoadingTextView"
+            style="@style/TextAppearance.Vector.Subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            tools:text="@string/qr_code_login_connecting_to_device" />
+
+        <include layout="@layout/item_loading" />
+
+    </LinearLayout>
+
+    <im.vector.app.features.login.qr.QrCodeLoginHeaderView
+        android:id="@+id/qrCodeLoginStatusHeaderView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:qrCodeLoginHeaderDescription="@string/qr_code_login_header_connected_description"
+        app:qrCodeLoginHeaderImageBackgroundTint="?colorPrimary"
+        app:qrCodeLoginHeaderImageResource="@drawable/ic_qr_code_login_connected"
+        app:qrCodeLoginHeaderTitle="@string/qr_code_login_header_connected_title" />
+
+    <TextView
+        android:id="@+id/qrCodeLoginStatusSecurityCode"
+        style="@style/TextAppearance.Vector.Title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="80dp"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginStatusHeaderView"
+        tools:text="28E-1B9-D0F-896"
+        tools:visibility="visible" />
+
+    <FrameLayout
+        android:id="@+id/qrCodeLoginStatusAlternativeLayout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        app:layout_constraintBottom_toTopOf="@id/qrCodeLoginStatusCancelButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:layout_gravity="center_vertical"
+            android:background="@drawable/divider_horizontal" />
+
+        <TextView
+            android:id="@+id/qrCodeLoginStatusNoMatchTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:background="?android:colorBackground"
+            android:paddingHorizontal="12dp"
+            android:text="@string/qr_code_login_status_no_match"
+            app:drawableLeftCompat="@drawable/divider_horizontal"
+            app:drawableTint="@color/alert_default_error_background" />
+    </FrameLayout>
+
+
+    <Button
+        android:id="@+id/qrCodeLoginStatusCancelButton"
+        style="@style/Widget.Vector.Button.Outlined.Login"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="40dp"
+        android:text="@string/action_cancel"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

From a66b183bf7498e1560104ea5a5a96c75bb7288bf Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 15:15:35 +0300
Subject: [PATCH 024/400] Add connection status to the view state.

---
 .../login/qr/QrCodeLoginConnectionStatus.kt   | 23 ++++++++++++
 .../login/qr/QrCodeLoginStatusFragment.kt     | 36 +++++++++++++++++++
 .../features/login/qr/QrCodeLoginViewState.kt |  1 +
 3 files changed, 60 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
new file mode 100644
index 0000000000..33c734d2f0
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.login.qr
+
+sealed class QrCodeLoginConnectionStatus {
+    object ConnectingToDevice : QrCodeLoginConnectionStatus()
+    data class Connected(val securityCode: String) : QrCodeLoginConnectionStatus()
+    object SigningIn : QrCodeLoginConnectionStatus()
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
new file mode 100644
index 0000000000..f3b4ae04ab
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.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.login.qr
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
+
+class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
+
+    override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginStatusBinding {
+        return FragmentQrCodeLoginStatusBinding.inflate(inflater, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
index 5ee9ab33d8..2f52cf789d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
@@ -20,6 +20,7 @@ import com.airbnb.mvrx.MavericksState
 
 data class QrCodeLoginViewState(
     val loginType: QrCodeLoginType,
+    val connectionStatus: QrCodeLoginConnectionStatus? = null,
 ) : MavericksState {
 
     constructor(args: QrCodeLoginArgs) : this(

From 4bb60e162841113fa95b0c4e48f2e7afa934e9b0 Mon Sep 17 00:00:00 2001
From: ByeongsuPark <byeongsu@soongsil.ac.kr>
Date: Thu, 6 Oct 2022 23:43:40 +0900
Subject: [PATCH 025/400] Fix wrong mic button direction to cancel on RTL
 languages

---
 .../home/room/detail/composer/voice/DraggableStateProcessor.kt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt
index 1355c89006..1ff937add5 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt
@@ -21,6 +21,7 @@ import android.view.MotionEvent
 import im.vector.app.R
 import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.DraggingState
+import kotlin.math.absoluteValue
 
 class DraggableStateProcessor(
         resources: Resources,
@@ -46,7 +47,7 @@ class DraggableStateProcessor(
     fun process(event: MotionEvent, draggingState: DraggingState): DraggingState {
         val currentX = event.rawX
         val currentY = event.rawY
-        val distanceX = firstX - currentX
+        val distanceX = (firstX - currentX).absoluteValue
         val distanceY = firstY - currentY
         return draggingState.nextDragState(currentX, currentY, distanceX, distanceY).also {
             lastDistanceX = distanceX

From a00afa7a3080f3ec71c82330d4c5fb6cb5e6a83d Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 18:17:02 +0300
Subject: [PATCH 026/400] Simulate qr login states.

---
 .../app/core/di/MavericksViewModelModule.kt   |  6 ++
 .../features/login/qr/QrCodeLoginAction.kt    | 23 +++++
 .../features/login/qr/QrCodeLoginActivity.kt  | 26 +++++-
 .../login/qr/QrCodeLoginHeaderView.kt         | 13 +++
 .../qr/QrCodeLoginInstructionsFragment.kt     |  6 +-
 .../login/qr/QrCodeLoginStatusFragment.kt     | 50 +++++++++++
 .../login/qr/QrCodeLoginViewEvents.kt         |  2 +-
 .../features/login/qr/QrCodeLoginViewModel.kt | 87 +++++++++++++++++++
 .../layout/fragment_qr_code_login_status.xml  |  6 +-
 9 files changed, 212 insertions(+), 7 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt

diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
index 62e7140742..76e15cd5cf 100644
--- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
@@ -60,6 +60,7 @@ import im.vector.app.features.location.LocationSharingViewModel
 import im.vector.app.features.location.live.map.LiveLocationMapViewModel
 import im.vector.app.features.location.preview.LocationPreviewViewModel
 import im.vector.app.features.login.LoginViewModel
+import im.vector.app.features.login.qr.QrCodeLoginViewModel
 import im.vector.app.features.matrixto.MatrixToBottomSheetViewModel
 import im.vector.app.features.media.VectorAttachmentViewerViewModel
 import im.vector.app.features.onboarding.OnboardingViewModel
@@ -659,4 +660,9 @@ interface MavericksViewModelModule {
     @IntoMap
     @MavericksViewModelKey(RenameSessionViewModel::class)
     fun renameSessionViewModelFactory(factory: RenameSessionViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
+
+    @Binds
+    @IntoMap
+    @MavericksViewModelKey(QrCodeLoginViewModel::class)
+    fun qrCodeLoginViewModelFactory(factory: QrCodeLoginViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
new file mode 100644
index 0000000000..948a771118
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.login.qr
+
+import im.vector.app.core.platform.VectorViewModelAction
+
+sealed class QrCodeLoginAction : VectorViewModelAction {
+    data class OnQrCodeScanned(val qrCode: String) : QrCodeLoginAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index bef9d5a040..5e359bfb26 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -20,6 +20,8 @@ import android.content.Context
 import android.content.Intent
 import android.os.Bundle
 import android.view.View
+import com.airbnb.mvrx.Mavericks
+import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
@@ -27,11 +29,13 @@ import im.vector.app.core.platform.SimpleFragmentActivity
 @AndroidEntryPoint
 class QrCodeLoginActivity : SimpleFragmentActivity() {
 
+    private val viewModel: QrCodeLoginViewModel by viewModel()
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         views.toolbar.visibility = View.GONE
 
-        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(EXTRA_QR_CODE_LOGIN_ARGS)
+        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
             if (qrCodeLoginArgs?.loginType == QrCodeLoginType.LOGIN) {
                 addFragment(
@@ -41,14 +45,30 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
                 )
             }
         }
+
+        observeViewEvents()
+    }
+
+    private fun observeViewEvents() {
+        viewModel.observeViewEvents {
+            when (it) {
+                QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
+            }
+        }
+    }
+
+    private fun handleNavigateToStatusScreen() {
+        addFragment(
+                views.container,
+                QrCodeLoginStatusFragment::class.java
+        )
     }
 
     companion object {
-        private const val EXTRA_QR_CODE_LOGIN_ARGS = "EXTRA_QR_CODE_LOGIN_ARGS"
 
         fun getIntent(context: Context, qrCodeLoginArgs: QrCodeLoginArgs): Intent {
             return Intent(context, QrCodeLoginActivity::class.java).apply {
-                putExtra(EXTRA_QR_CODE_LOGIN_ARGS, qrCodeLoginArgs)
+                putExtra(Mavericks.KEY_ARG, qrCodeLoginArgs)
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
index 315369c462..f14d711d3c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
@@ -66,4 +66,17 @@ class QrCodeLoginHeaderView @JvmOverloads constructor(
         binding.qrCodeLoginHeaderImageView.setImageResource(imageResource)
         binding.qrCodeLoginHeaderImageView.backgroundTintList = ColorStateList.valueOf(backgroundTint)
     }
+
+    fun setTitle(title: String) {
+        binding.qrCodeLoginHeaderTitleTextView.text = title
+    }
+
+    fun setDescription(description: String) {
+        binding.qrCodeLoginHeaderDescriptionTextView.text = description
+    }
+
+    fun setImage(imageResource: Int, backgroundTintColor: Int) {
+        binding.qrCodeLoginHeaderImageView.setImageResource(imageResource)
+        binding.qrCodeLoginHeaderImageView.backgroundTintList = ColorStateList.valueOf(backgroundTintColor)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index d15d01ec12..99dea345eb 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -21,6 +21,8 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import com.airbnb.mvrx.activityViewModel
+import com.airbnb.mvrx.fragmentViewModel
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginInstructionsBinding
@@ -29,6 +31,8 @@ import timber.log.Timber
 
 class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginInstructionsBinding>() {
 
+    private val viewModel: QrCodeLoginViewModel by activityViewModel()
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         initScanQrCodeButton()
@@ -58,7 +62,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
     }
 
     private fun onQrCodeScanned(scannedQrCode: String) {
-        Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScanned $scannedQrCode")
+        viewModel.handle(QrCodeLoginAction.OnQrCodeScanned(scannedQrCode))
     }
 
     private fun onQrCodeScannerFailed() {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index f3b4ae04ab..52abfa788c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -20,17 +20,67 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.activityViewModel
+import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
+import im.vector.app.features.themes.ThemeUtils
 
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
 
+    private val viewModel: QrCodeLoginViewModel by activityViewModel()
+
     override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginStatusBinding {
         return FragmentQrCodeLoginStatusBinding.inflate(inflater, container, false)
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
+        observeViewState()
+    }
 
+    private fun observeViewState() {
+        viewModel.onEach {
+            when (it.connectionStatus) {
+                is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(it.connectionStatus)
+                QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice()
+                QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn()
+                null -> TODO()
+            }
+        }
+    }
+
+    private fun handleConnectingToDevice() {
+        views.qrCodeLoginStatusLoadingLayout.isVisible = true
+        views.qrCodeLoginStatusHeaderView.isVisible = false
+        views.qrCodeLoginStatusSecurityCode.isVisible = false
+        views.qrCodeLoginStatusNoMatchLayout.isVisible = false
+        views.qrCodeLoginStatusCancelButton.isVisible = true
+        views.qrCodeLoginStatusLoadingTextView.setText(R.string.qr_code_login_connecting_to_device)
+    }
+
+    private fun handleSigningIn() {
+        views.qrCodeLoginStatusLoadingLayout.isVisible = true
+        views.qrCodeLoginStatusHeaderView.isVisible = false
+        views.qrCodeLoginStatusSecurityCode.isVisible = false
+        views.qrCodeLoginStatusNoMatchLayout.isVisible = false
+        views.qrCodeLoginStatusCancelButton.isVisible = false
+        views.qrCodeLoginStatusLoadingTextView.setText(R.string.qr_code_login_signing_in)
+    }
+
+    private fun handleConnectionEstablished(connectionStatus: QrCodeLoginConnectionStatus.Connected) {
+        views.qrCodeLoginStatusLoadingLayout.isVisible = false
+        views.qrCodeLoginStatusHeaderView.isVisible = true
+        views.qrCodeLoginStatusSecurityCode.isVisible = true
+        views.qrCodeLoginStatusNoMatchLayout.isVisible = true
+        views.qrCodeLoginStatusCancelButton.isVisible = true
+        views.qrCodeLoginStatusSecurityCode.text = connectionStatus.securityCode
+        views.qrCodeLoginStatusHeaderView.setTitle(getString(R.string.qr_code_login_header_connected_title))
+        views.qrCodeLoginStatusHeaderView.setDescription(getString(R.string.qr_code_login_header_connected_description))
+        views.qrCodeLoginStatusHeaderView.setImage(
+                imageResource = R.drawable.ic_qr_code_login_connected,
+                backgroundTintColor = ThemeUtils.getColor(requireContext(), R.attr.colorPrimary)
+        )
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index f5228f1d41..b8ebb65308 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -19,5 +19,5 @@ package im.vector.app.features.login.qr
 import im.vector.app.core.platform.VectorViewEvents
 
 sealed class QrCodeLoginViewEvents : VectorViewEvents {
-
+    object NavigateToStatusScreen : QrCodeLoginViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
new file mode 100644
index 0000000000..ac3b68da15
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.login.qr
+
+import com.airbnb.mvrx.MavericksViewModelFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.core.di.MavericksAssistedViewModelFactory
+import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.platform.VectorViewModel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+class QrCodeLoginViewModel @AssistedInject constructor(
+        @Assisted private val initialState: QrCodeLoginViewState,
+) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
+
+    @AssistedFactory
+    interface Factory : MavericksAssistedViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> {
+        override fun create(initialState: QrCodeLoginViewState): QrCodeLoginViewModel
+    }
+
+    companion object : MavericksViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> by hiltMavericksViewModelFactory()
+
+    override fun handle(action: QrCodeLoginAction) {
+        when (action) {
+            is QrCodeLoginAction.OnQrCodeScanned -> handleOnQrCodeScanned(action)
+        }
+    }
+
+    private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
+        if (isValidQrCode(action.qrCode)) {
+            setState {
+                copy(
+                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
+                )
+            }
+            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+        }
+
+        // TODO. UI test purpose. Fixme remove!
+        viewModelScope.launch {
+            delay(3000)
+            onConnectionEstablished("1234-ABCD-5678-EFGH")
+            delay(3000)
+            onSigningIn()
+        }
+    }
+
+    private fun onConnectionEstablished(securityCode: String) {
+        setState {
+            copy(
+                    connectionStatus = QrCodeLoginConnectionStatus.Connected(securityCode)
+            )
+        }
+    }
+
+    private fun onSigningIn() {
+        setState {
+            copy(
+                    connectionStatus = QrCodeLoginConnectionStatus.SigningIn
+            )
+        }
+    }
+
+    /**
+     * TODO. UI test purpose. Fixme accordingly.
+     */
+    private fun isValidQrCode(qrCode: String): Boolean {
+        return qrCode.startsWith("http")
+    }
+}
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_status.xml b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
index 7fe8ea6c02..0c200eeb83 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_status.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
@@ -3,7 +3,9 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="?android:colorBackground"
+    android:paddingHorizontal="16dp">
 
     <LinearLayout
         android:id="@+id/qrCodeLoginStatusLoadingLayout"
@@ -55,7 +57,7 @@
         tools:visibility="visible" />
 
     <FrameLayout
-        android:id="@+id/qrCodeLoginStatusAlternativeLayout"
+        android:id="@+id/qrCodeLoginStatusNoMatchLayout"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginBottom="12dp"

From 1932edad46e867b184c418a2c110ce1bbcfd4acf Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 6 Oct 2022 18:48:07 +0300
Subject: [PATCH 027/400] Fix instructions view visibility.

---
 .../login/qr/QrCodeLoginInstructionsView.kt         | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
index ddb92b272e..e3ddf608af 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
@@ -22,6 +22,7 @@ import android.util.AttributeSet
 import android.view.LayoutInflater
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.content.res.use
+import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
 import im.vector.app.databinding.ViewQrCodeLoginInstructionsBinding
@@ -53,9 +54,13 @@ class QrCodeLoginInstructionsView @JvmOverloads constructor(
         val instruction2 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction2)
         val instruction3 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction3)
         val instruction4 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction4)
-        binding.instruction1TextView.setTextOrHide(instruction1)
-        binding.instruction2TextView.setTextOrHide(instruction2)
-        binding.instruction3TextView.setTextOrHide(instruction3)
-        binding.instruction4TextView.setTextOrHide(instruction4)
+        binding.instructions1Layout.isVisible = instruction1 != null
+        binding.instructions2Layout.isVisible = instruction2 != null
+        binding.instructions3Layout.isVisible = instruction3 != null
+        binding.instructions4Layout.isVisible = instruction4 != null
+        binding.instruction1TextView.text = instruction1
+        binding.instruction2TextView.text = instruction2
+        binding.instruction3TextView.text = instruction3
+        binding.instruction4TextView.text = instruction4
     }
 }

From 652069d520869f655245c5cf1894b7de52410cfc Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Thu, 6 Oct 2022 18:00:33 +0200
Subject: [PATCH 028/400] Manage new Android 13 notification permission.

---
 .../src/main/res/values/strings.xml           |  4 ++
 tools/adb/notification.sh                     | 25 +++++++
 .../features/debug/DebugPermissionActivity.kt | 19 ++++-
 .../res/layout/activity_debug_permission.xml  | 24 +++----
 .../troubleshoot/TestAutoStartBoot.kt         |  6 +-
 .../TestBackgroundRestrictions.kt             |  4 +-
 .../troubleshoot/TestBatteryOptimization.kt   |  6 +-
 .../troubleshoot/TestFirebaseToken.kt         |  6 +-
 .../settings/troubleshoot/TestPlayServices.kt |  4 +-
 .../troubleshoot/TestTokenRegistration.kt     |  8 +--
 vector/src/main/AndroidManifest.xml           |  3 +
 .../vector/app/features/home/HomeActivity.kt  | 11 +++
 .../features/home/HomeActivityViewEvents.kt   |  1 +
 .../features/home/HomeActivityViewModel.kt    |  4 ++
 .../home/NotificationPermissionManager.kt     | 71 +++++++++++++++++++
 ...rSettingsNotificationPreferenceFragment.kt | 21 ++++++
 ...ttingsNotificationsTroubleshootFragment.kt | 17 ++++-
 .../NotificationTroubleshootTestManager.kt    | 12 ++--
 .../troubleshoot/TestAccountSettings.kt       |  6 +-
 .../TestAvailableUnifiedPushDistributors.kt   |  4 +-
 .../TestCurrentUnifiedPushDistributor.kt      |  4 +-
 .../troubleshoot/TestDeviceSettings.kt        |  6 +-
 .../TestEndpointAsTokenRegistration.kt        |  8 +--
 .../settings/troubleshoot/TestNotification.kt |  6 +-
 .../troubleshoot/TestPushFromPushGateway.kt   |  4 +-
 .../troubleshoot/TestPushRulesSettings.kt     |  4 +-
 .../troubleshoot/TestSystemSettings.kt        | 30 +++++---
 .../troubleshoot/TestUnifiedPushEndpoint.kt   |  4 +-
 .../troubleshoot/TestUnifiedPushGateway.kt    |  4 +-
 .../settings/troubleshoot/TroubleshootTest.kt |  7 +-
 30 files changed, 241 insertions(+), 92 deletions(-)
 create mode 100755 tools/adb/notification.sh
 create mode 100644 vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 71ccf5b234..ef71c10dd5 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -635,6 +635,8 @@
     <string name="permissions_rationale_msg_record_audio">${app_name} needs permission to access your microphone to perform audio calls.</string>
     <!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
     <string name="permissions_rationale_msg_camera_and_audio">${app_name} needs permission to access your camera and your microphone to perform video calls.\n\nPlease allow access on the next pop-ups to be able to make the call.</string>
+    <!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
+    <string name="permissions_rationale_msg_notification">${app_name} needs permission to display notifications. Notifications can display your messages, your invitations, etc.\n\nPlease allow access on the next pop-ups to be able to view notification.</string>
 
     <string name="permissions_denied_qr_code">To scan a QR code, you need to allow camera access.</string>
     <string name="permissions_denied_add_contact">Allow permission to access your contacts.</string>
@@ -854,7 +856,9 @@
     <string name="settings_troubleshoot_test_system_settings_title">System Settings.</string>
     <string name="settings_troubleshoot_test_system_settings_success">Notifications are enabled in the system settings.</string>
     <string name="settings_troubleshoot_test_system_settings_failed">Notifications are disabled in the system settings.\nPlease check system settings.</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} needs the permission to show notifications.\nPlease grant the permission.</string>
     <string name="open_settings">Open Settings</string>
+    <string name="grant_permission">Grant Permission</string>
 
     <string name="settings_troubleshoot_test_account_settings_title">Account Settings.</string>
     <string name="settings_troubleshoot_test_account_settings_success">Notifications are enabled for your account.</string>
diff --git a/tools/adb/notification.sh b/tools/adb/notification.sh
new file mode 100755
index 0000000000..3c3c76c787
--- /dev/null
+++ b/tools/adb/notification.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+## From https://developer.android.com/develop/ui/views/notifications/notification-permission#test
+
+PACKAGE_NAME=im.vector.app.debug
+
+# App is newly installed on a device that runs Android 13 or higher:
+
+adb shell pm revoke                 ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
+adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
+adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
+
+# The user keeps notifications enabled when the app is installed on a device that runs 12L or lower,
+# then the device upgrades to Android 13 or higher:
+
+# adb shell pm grant                  ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
+# adb shell pm set-permission-flags   ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
+# adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
+
+# The user manually disables notifications when the app is installed on a device that runs 12L or lower,
+# then the device upgrades to Android 13 or higher:
+
+# adb shell pm revoke                 ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
+# adb shell pm set-permission-flags   ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
+# adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
index a9be5512e4..2b1b66e672 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/DebugPermissionActivity.kt
@@ -22,6 +22,7 @@ import android.os.Build
 import android.widget.Toast
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.core.utils.checkPermissions
@@ -46,7 +47,15 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
             Manifest.permission.WRITE_EXTERNAL_STORAGE,
             Manifest.permission.READ_EXTERNAL_STORAGE,
             Manifest.permission.READ_CONTACTS
-    )
+    ) + getAndroid13Permissions()
+
+    private fun getAndroid13Permissions(): List<String> {
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            listOf(Manifest.permission.POST_NOTIFICATIONS)
+        } else {
+            emptyList()
+        }
+    }
 
     private var lastPermissions = emptyList<String>()
 
@@ -77,6 +86,14 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
             lastPermissions = listOf(Manifest.permission.READ_CONTACTS)
             checkPerm()
         }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            views.notification.setOnClickListener {
+                lastPermissions = listOf(Manifest.permission.POST_NOTIFICATIONS)
+                checkPerm()
+            }
+        } else {
+            views.notification.isVisible = false
+        }
     }
 
     private fun checkPerm() {
diff --git a/vector-app/src/debug/res/layout/activity_debug_permission.xml b/vector-app/src/debug/res/layout/activity_debug_permission.xml
index 6340d8faa7..0f1fef0b9b 100644
--- a/vector-app/src/debug/res/layout/activity_debug_permission.xml
+++ b/vector-app/src/debug/res/layout/activity_debug_permission.xml
@@ -30,43 +30,43 @@
                 android:id="@+id/camera"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="CAMERA"
-                android:textAllCaps="false" />
+                android:text="CAMERA" />
 
             <Button
                 android:id="@+id/audio"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="RECORD_AUDIO"
-                android:textAllCaps="false" />
+                android:text="RECORD_AUDIO" />
 
             <Button
                 android:id="@+id/camera_audio"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="CAMERA + RECORD_AUDIO"
-                android:textAllCaps="false" />
+                android:text="CAMERA + RECORD_AUDIO" />
 
             <Button
                 android:id="@+id/write"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="WRITE_EXTERNAL_STORAGE"
-                android:textAllCaps="false" />
+                android:text="WRITE_EXTERNAL_STORAGE" />
 
             <Button
                 android:id="@+id/read"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="READ_EXTERNAL_STORAGE"
-                android:textAllCaps="false" />
+                android:text="READ_EXTERNAL_STORAGE" />
 
             <Button
                 android:id="@+id/contact"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="READ_CONTACTS"
-                android:textAllCaps="false" />
+                android:text="READ_CONTACTS" />
+
+            <Button
+                android:id="@+id/notification"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="POST_NOTIFICATIONS" />
 
         </LinearLayout>
 
diff --git a/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
index e028ed0988..ce1263c67b 100644
--- a/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
+++ b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.fdroid.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.features.settings.VectorPreferences
@@ -32,7 +30,7 @@ class TestAutoStartBoot @Inject constructor(
 ) :
         TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         if (vectorPreferences.autoStartOnBoot()) {
             description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
             status = TestStatus.SUCCESS
@@ -42,7 +40,7 @@ class TestAutoStartBoot @Inject constructor(
             quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
                 override fun doFix() {
                     vectorPreferences.setAutoStartOnBoot(true)
-                    manager?.retry(activityResultLauncher)
+                    manager?.retry(testParameters)
                 }
             }
             status = TestStatus.FAILED
diff --git a/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
index ebd8d07540..a14d3b5c8a 100644
--- a/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
+++ b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt
@@ -15,9 +15,7 @@
  */
 package im.vector.app.fdroid.features.settings.troubleshoot
 
-import android.content.Intent
 import android.net.ConnectivityManager
-import androidx.activity.result.ActivityResultLauncher
 import androidx.core.content.getSystemService
 import androidx.core.net.ConnectivityManagerCompat
 import androidx.fragment.app.FragmentActivity
@@ -32,7 +30,7 @@ class TestBackgroundRestrictions @Inject constructor(
 ) :
         TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         context.getSystemService<ConnectivityManager>()!!.apply {
             // Checks if the device is on a metered network
             if (isActiveNetworkMetered) {
diff --git a/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt
index 57bdf721a2..075432c9d2 100644
--- a/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt
+++ b/vector-app/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBatteryOptimization.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.fdroid.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import androidx.fragment.app.FragmentActivity
 import im.vector.app.R
 import im.vector.app.core.resources.StringProvider
@@ -30,7 +28,7 @@ class TestBatteryOptimization @Inject constructor(
         private val stringProvider: StringProvider
 ) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         if (context.isIgnoringBatteryOptimizations()) {
             description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success)
             status = TestStatus.SUCCESS
@@ -39,7 +37,7 @@ class TestBatteryOptimization @Inject constructor(
             description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed)
             quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) {
                 override fun doFix() {
-                    requestDisablingBatteryOptimization(context, activityResultLauncher)
+                    requestDisablingBatteryOptimization(context, testParameters.activityResultLauncher)
                 }
             }
             status = TestStatus.FAILED
diff --git a/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
index d6180a9fe8..8e7e4f43cc 100644
--- a/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
+++ b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.gplay.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import androidx.fragment.app.FragmentActivity
 import com.google.firebase.messaging.FirebaseMessaging
 import im.vector.app.R
@@ -36,7 +34,7 @@ class TestFirebaseToken @Inject constructor(
         private val fcmHelper: FcmHelper,
 ) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         status = TestStatus.RUNNING
         try {
             FirebaseMessaging.getInstance().token
@@ -53,7 +51,7 @@ class TestFirebaseToken @Inject constructor(
                                 "ACCOUNT_MISSING" -> {
                                     quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
                                         override fun doFix() {
-                                            startAddGoogleAccountIntent(context, activityResultLauncher)
+                                            startAddGoogleAccountIntent(context, testParameters.activityResultLauncher)
                                         }
                                     }
                                     stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
diff --git a/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt
index e78132908d..3ebcceb3fb 100644
--- a/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt
+++ b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.gplay.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import androidx.fragment.app.FragmentActivity
 import com.google.android.gms.common.ConnectionResult
 import com.google.android.gms.common.GoogleApiAvailability
@@ -35,7 +33,7 @@ class TestPlayServices @Inject constructor(
 ) :
         TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         val apiAvailability = GoogleApiAvailability.getInstance()
         val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
         if (resultCode == ConnectionResult.SUCCESS) {
diff --git a/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
index 840bde77b1..cafc2d65e6 100644
--- a/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
+++ b/vector-app/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.gplay.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import androidx.fragment.app.FragmentActivity
 import androidx.lifecycle.Observer
 import androidx.work.WorkInfo
@@ -42,7 +40,7 @@ class TestTokenRegistration @Inject constructor(
 ) :
         TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         // Check if we have a registered pusher for this token
         val fcmToken = fcmHelper.getFcmToken() ?: run {
             status = TestStatus.FAILED
@@ -66,9 +64,9 @@ class TestTokenRegistration @Inject constructor(
                     WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
                         if (workInfo != null) {
                             if (workInfo.state == WorkInfo.State.SUCCEEDED) {
-                                manager?.retry(activityResultLauncher)
+                                manager?.retry(testParameters)
                             } else if (workInfo.state == WorkInfo.State.FAILED) {
-                                manager?.retry(activityResultLauncher)
+                                manager?.retry(testParameters)
                             }
                         }
                     })
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index f079d3429e..ad15bf4829 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -17,6 +17,9 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
+    <!-- https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions -->
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
     <!-- Call feature -->
     <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
     <!-- Commented because Google PlayStore does not like we add permission if we are not requiring it. And it was added for future use -->
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 18c9e7e3b9..121436cd22 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -48,6 +48,7 @@ import im.vector.app.core.platform.VectorMenuProvider
 import im.vector.app.core.pushers.FcmHelper
 import im.vector.app.core.pushers.PushersManager
 import im.vector.app.core.pushers.UnifiedPushHelper
+import im.vector.app.core.utils.registerForPermissionsResult
 import im.vector.app.core.utils.startSharePlainTextIntent
 import im.vector.app.databinding.ActivityHomeBinding
 import im.vector.app.features.MainActivity
@@ -143,6 +144,7 @@ class HomeActivity :
     @Inject lateinit var fcmHelper: FcmHelper
     @Inject lateinit var nightlyProxy: NightlyProxy
     @Inject lateinit var disclaimerDialog: DisclaimerDialog
+    @Inject lateinit var notificationPermissionManager: NotificationPermissionManager
 
     private var isNewAppLayoutEnabled: Boolean = false // delete once old app layout is removed
 
@@ -172,6 +174,10 @@ class HomeActivity :
         }
     }
 
+    private val postPermissionLauncher = registerForPermissionsResult { _, _ ->
+        // Nothing to do with the result.
+    }
+
     private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
         override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
             if (f is MatrixToBottomSheet) {
@@ -273,6 +279,7 @@ class HomeActivity :
                 }
                 is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
                 HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn()
+                HomeActivityViewEvents.ShowNotificationDialog -> handleShowNotificationDialog()
                 HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes()
                 HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration()
                 is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession)
@@ -288,6 +295,10 @@ class HomeActivity :
         homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
     }
 
+    private fun handleShowNotificationDialog() {
+        notificationPermissionManager.eventuallyRequestPermission(this, postPermissionLauncher)
+    }
+
     private fun handleShowReleaseNotes() {
         startActivity(Intent(this, ReleaseNotesActivity::class.java))
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt
index 4147cf7186..e548fdb2f3 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt
@@ -31,6 +31,7 @@ sealed interface HomeActivityViewEvents : VectorViewEvents {
     data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
     object PromptToEnableSessionPush : HomeActivityViewEvents
     object ShowAnalyticsOptIn : HomeActivityViewEvents
+    object ShowNotificationDialog : HomeActivityViewEvents
     object ShowReleaseNotes : HomeActivityViewEvents
     object NotifyUserForThreadsMigration : HomeActivityViewEvents
     data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
index 9157d81333..749292924e 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
@@ -143,6 +143,8 @@ class HomeActivityViewModel @AssistedInject constructor(
                     .onEach { didAskUser ->
                         if (!didAskUser) {
                             _viewEvents.post(HomeActivityViewEvents.ShowAnalyticsOptIn)
+                        } else {
+                            _viewEvents.post(HomeActivityViewEvents.ShowNotificationDialog)
                         }
                     }
                     .launchIn(viewModelScope)
@@ -162,6 +164,8 @@ class HomeActivityViewModel @AssistedInject constructor(
                     // do nothing
                 }
             }
+        } else {
+            _viewEvents.post(HomeActivityViewEvents.ShowNotificationDialog)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt b/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
new file mode 100644
index 0000000000..bb75730377
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
@@ -0,0 +1,71 @@
+/*
+ * 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
+
+import android.Manifest
+import android.app.Activity
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.activity.result.ActivityResultLauncher
+import androidx.annotation.RequiresApi
+import androidx.core.content.ContextCompat
+import im.vector.app.R
+import im.vector.app.core.utils.checkPermissions
+import im.vector.app.features.settings.VectorPreferences
+import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
+import javax.inject.Inject
+
+class NotificationPermissionManager @Inject constructor(
+        private val sdkIntProvider: BuildVersionSdkIntProvider,
+        private val vectorPreferences: VectorPreferences,
+) {
+
+    fun isPermissionGranted(activity: Activity): Boolean {
+        return if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
+            ContextCompat.checkSelfPermission(
+                    activity,
+                    Manifest.permission.POST_NOTIFICATIONS
+            ) == PackageManager.PERMISSION_GRANTED
+        } else {
+            // No notification permission management before API 33.
+            true
+        }
+    }
+
+    fun eventuallyRequestPermission(
+            activity: Activity,
+            requestPermissionLauncher: ActivityResultLauncher<Array<String>>,
+            showRationale: Boolean = true,
+            ignorePreference: Boolean = false,
+    ) {
+        if (!sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) return
+        if (!vectorPreferences.areNotificationEnabledForDevice() && !ignorePreference) return
+        checkPermissions(
+                listOf(Manifest.permission.POST_NOTIFICATIONS),
+                activity,
+                activityResultLauncher = requestPermissionLauncher,
+                if (showRationale) R.string.permissions_rationale_msg_notification else 0
+        )
+    }
+
+    @RequiresApi(33)
+    fun askPermission(requestPermissionLauncher: ActivityResultLauncher<Array<String>>) {
+        requestPermissionLauncher.launch(
+                arrayOf(Manifest.permission.POST_NOTIFICATIONS)
+        )
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
index b1e32c05d1..01eb277f27 100644
--- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
@@ -43,9 +43,12 @@ import im.vector.app.core.pushers.UnifiedPushHelper
 import im.vector.app.core.services.GuardServiceStarter
 import im.vector.app.core.utils.combineLatest
 import im.vector.app.core.utils.isIgnoringBatteryOptimizations
+import im.vector.app.core.utils.registerForPermissionsResult
 import im.vector.app.core.utils.requestDisablingBatteryOptimization
+import im.vector.app.core.utils.startNotificationSettingsIntent
 import im.vector.app.features.VectorFeatures
 import im.vector.app.features.analytics.plan.MobileScreen
+import im.vector.app.features.home.NotificationPermissionManager
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.settings.BackgroundSyncMode
 import im.vector.app.features.settings.BackgroundSyncModeChooserDialog
@@ -76,12 +79,24 @@ class VectorSettingsNotificationPreferenceFragment :
     @Inject lateinit var vectorPreferences: VectorPreferences
     @Inject lateinit var guardServiceStarter: GuardServiceStarter
     @Inject lateinit var vectorFeatures: VectorFeatures
+    @Inject lateinit var notificationPermissionManager: NotificationPermissionManager
 
     override var titleRes: Int = R.string.settings_notifications
     override val preferenceXmlRes = R.xml.vector_settings_notifications
 
     private var interactionListener: VectorSettingsFragmentInteractionListener? = null
 
+    private val notificationStartForActivityResult = registerStartForActivityResult { _ ->
+        // No op
+    }
+
+    private val postPermissionLauncher = registerForPermissionsResult { _, deniedPermanently ->
+        if (deniedPermanently) {
+            // Open System setting, to give a chance to the user to enable notification. Sometimes the permission dialog is not displayed
+            startNotificationSettingsIntent(requireContext(), notificationStartForActivityResult)
+        }
+    }
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         analyticsScreenName = MobileScreen.ScreenName.SettingsNotifications
@@ -118,6 +133,12 @@ class VectorSettingsNotificationPreferenceFragment :
                         findPreference<VectorPreference>(VectorPreferences.SETTINGS_NOTIFICATION_METHOD_KEY)
                                 ?.summary = unifiedPushHelper.getCurrentDistributorName()
                     }
+                    notificationPermissionManager.eventuallyRequestPermission(
+                            requireActivity(),
+                            postPermissionLauncher,
+                            showRationale = false,
+                            ignorePreference = true
+                    )
                 } else {
                     unifiedPushHelper.unregister(pushersManager)
                     session.pushersService().refreshPushers()
diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt
index 9fc55d14aa..c5d15e54e5 100644
--- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt
@@ -34,6 +34,8 @@ import im.vector.app.R
 import im.vector.app.core.extensions.cleanup
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.core.utils.registerForPermissionsResult
+import im.vector.app.core.utils.startNotificationSettingsIntent
 import im.vector.app.databinding.FragmentSettingsNotificationsTroubleshootBinding
 import im.vector.app.features.notifications.NotificationActionIds
 import im.vector.app.features.push.NotificationTroubleshootTestManagerFactory
@@ -76,7 +78,7 @@ class VectorSettingsNotificationsTroubleshootFragment :
         }
 
         views.troubleshootRunButton.debouncedClicks {
-            testManager?.retry(testStartForActivityResult)
+            testManager?.retry(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
         }
         startUI()
     }
@@ -125,7 +127,7 @@ class VectorSettingsNotificationsTroubleshootFragment :
             }
         }
         views.troubleshootTestRecyclerView.adapter = testManager?.adapter
-        testManager?.runDiagnostic(testStartForActivityResult)
+        testManager?.runDiagnostic(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
     }
 
     override fun onDestroyView() {
@@ -139,8 +141,17 @@ class VectorSettingsNotificationsTroubleshootFragment :
         }
     }
 
+    private val testStartForPermissionResult = registerForPermissionsResult { allGranted, deniedPermanently ->
+        if (allGranted) {
+            retry()
+        } else if (deniedPermanently) {
+            // Open System setting
+            startNotificationSettingsIntent(requireContext(), testStartForActivityResult)
+        }
+    }
+
     private fun retry() {
-        testManager?.retry(testStartForActivityResult)
+        testManager?.retry(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
     }
 
     override fun onDetach() {
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt
index 7e7ca57243..3419241c39 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt
@@ -15,10 +15,8 @@
  */
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
 import android.os.Handler
 import android.os.Looper
-import androidx.activity.result.ActivityResultLauncher
 import androidx.fragment.app.Fragment
 import kotlin.properties.Delegates
 
@@ -50,7 +48,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
         test.manager = this
     }
 
-    fun runDiagnostic(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    fun runDiagnostic(testParameters: TroubleshootTest.TestParameters) {
         if (isCancelled) return
         currentTestIndex = 0
         val handler = Handler(Looper.getMainLooper())
@@ -69,7 +67,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
                             // Cosmetic: Start with a small delay for UI/UX reason (better animation effect) for non async tests
                             handler.postDelayed({
                                 if (fragment.isAdded) {
-                                    troubleshootTest.perform(activityResultLauncher)
+                                    troubleshootTest.perform(testParameters)
                                 }
                             }, 600)
                         } else {
@@ -81,18 +79,18 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
             }
         }
         if (fragment.isAdded) {
-            testList.firstOrNull()?.perform(activityResultLauncher)
+            testList.firstOrNull()?.perform(testParameters)
         }
     }
 
-    fun retry(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    fun retry(testParameters: TroubleshootTest.TestParameters) {
         testList.forEach {
             it.cancel()
             it.description = null
             it.quickFix = null
             it.status = TroubleshootTest.TestStatus.NOT_STARTED
         }
-        runDiagnostic(activityResultLauncher)
+        runDiagnostic(testParameters)
     }
 
     fun hasQuickFix(): Boolean {
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt
index e8a92c9e3f..5032c97935 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.resources.StringProvider
@@ -38,7 +36,7 @@ class TestAccountSettings @Inject constructor(
 ) :
         TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         val session = activeSessionHolder.getSafeActiveSession() ?: return
         val defaultRule = session.pushRuleService().getPushRules().getAllRules()
                 .find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
@@ -59,7 +57,7 @@ class TestAccountSettings @Inject constructor(
                                 session.pushRuleService().updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled)
                             }
                             withContext(Dispatchers.Main) {
-                                manager?.retry(activityResultLauncher)
+                                manager?.retry(testParameters)
                             }
                         }
                     }
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAvailableUnifiedPushDistributors.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAvailableUnifiedPushDistributors.kt
index 89e7d8c204..e6fa59f597 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAvailableUnifiedPushDistributors.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAvailableUnifiedPushDistributors.kt
@@ -16,8 +16,6 @@
 
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.pushers.FcmHelper
 import im.vector.app.core.pushers.UnifiedPushHelper
@@ -30,7 +28,7 @@ class TestAvailableUnifiedPushDistributors @Inject constructor(
         private val fcmHelper: FcmHelper,
 ) : TroubleshootTest(R.string.settings_troubleshoot_test_distributors_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         val distributors = unifiedPushHelper.getExternalDistributors()
         description = if (distributors.isEmpty()) {
             stringProvider.getString(
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestCurrentUnifiedPushDistributor.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestCurrentUnifiedPushDistributor.kt
index d43fb1bfe3..bc291725fd 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestCurrentUnifiedPushDistributor.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestCurrentUnifiedPushDistributor.kt
@@ -16,8 +16,6 @@
 
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.pushers.UnifiedPushHelper
 import im.vector.app.core.resources.StringProvider
@@ -28,7 +26,7 @@ class TestCurrentUnifiedPushDistributor @Inject constructor(
         private val stringProvider: StringProvider,
 ) : TroubleshootTest(R.string.settings_troubleshoot_test_current_distributor_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         description = stringProvider.getString(
                 R.string.settings_troubleshoot_test_current_distributor,
                 unifiedPushHelper.getCurrentDistributorName()
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt
index e18c6ce4e1..a0be2a7077 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.features.settings.VectorPreferences
@@ -31,7 +29,7 @@ class TestDeviceSettings @Inject constructor(
 ) :
         TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         if (vectorPreferences.areNotificationEnabledForDevice()) {
             description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success)
             quickFix = null
@@ -40,7 +38,7 @@ class TestDeviceSettings @Inject constructor(
             quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) {
                 override fun doFix() {
                     vectorPreferences.setNotificationEnabledForDevice(true)
-                    manager?.retry(activityResultLauncher)
+                    manager?.retry(testParameters)
                 }
             }
             description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed)
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt
index 78eb372fef..3bbff0f2fe 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestEndpointAsTokenRegistration.kt
@@ -16,8 +16,6 @@
 
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import androidx.fragment.app.FragmentActivity
 import androidx.lifecycle.Observer
 import androidx.work.WorkInfo
@@ -38,7 +36,7 @@ class TestEndpointAsTokenRegistration @Inject constructor(
         private val unifiedPushHelper: UnifiedPushHelper,
 ) : TroubleshootTest(R.string.settings_troubleshoot_test_endpoint_registration_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         // Check if we have a registered pusher for this token
         val endpoint = unifiedPushHelper.getEndpointOrToken() ?: run {
             status = TestStatus.FAILED
@@ -66,9 +64,9 @@ class TestEndpointAsTokenRegistration @Inject constructor(
                     WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
                         if (workInfo != null) {
                             if (workInfo.state == WorkInfo.State.SUCCEEDED) {
-                                manager?.retry(activityResultLauncher)
+                                manager?.retry(testParameters)
                             } else if (workInfo.state == WorkInfo.State.FAILED) {
-                                manager?.retry(activityResultLauncher)
+                                manager?.retry(testParameters)
                             }
                         }
                     })
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt
index 22fab0cad4..403c4a02c9 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt
@@ -16,8 +16,6 @@
 package im.vector.app.features.settings.troubleshoot
 
 import android.content.Context
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.core.utils.startNotificationSettingsIntent
@@ -34,14 +32,14 @@ class TestNotification @Inject constructor(
 ) :
         TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         // Display the notification right now
         notificationUtils.displayDiagnosticNotification()
         description = stringProvider.getString(R.string.settings_troubleshoot_test_notification_notice)
 
         quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
             override fun doFix() {
-                startNotificationSettingsIntent(context, activityResultLauncher)
+                startNotificationSettingsIntent(context, testParameters.activityResultLauncher)
             }
         }
 
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushFromPushGateway.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushFromPushGateway.kt
index cf2bf3d5f1..1fec8691d7 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushFromPushGateway.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushFromPushGateway.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.error.ErrorFormatter
@@ -43,7 +41,7 @@ class TestPushFromPushGateway @Inject constructor(
     private var action: Job? = null
     private var pushReceived: Boolean = false
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         pushReceived = false
         action = activeSessionHolder.getActiveSession().coroutineScope.launch {
             val result = runCatching { pushersManager.testPush() }
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt
index 837b4952f0..0fe6ed3ad0 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt
@@ -15,8 +15,6 @@
  */
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.resources.StringProvider
@@ -39,7 +37,7 @@ class TestPushRulesSettings @Inject constructor(
                     RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
             )
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         val session = activeSessionHolder.getSafeActiveSession() ?: return
         val pushRules = session.pushRuleService().getPushRules().getAllRules()
         var oneOrMoreRuleIsOff = false
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt
index 2c73bef701..0f362d5019 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt
@@ -15,34 +15,44 @@
  */
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import androidx.core.app.NotificationManagerCompat
 import androidx.fragment.app.FragmentActivity
 import im.vector.app.R
 import im.vector.app.core.resources.StringProvider
 import im.vector.app.core.utils.startNotificationSettingsIntent
+import im.vector.app.features.home.NotificationPermissionManager
 import javax.inject.Inject
 
 /**
  * Checks if notifications are enable in the system settings for this app.
+ * On Android 13, it will check for the notification permission.
  */
 class TestSystemSettings @Inject constructor(
         private val context: FragmentActivity,
-        private val stringProvider: StringProvider
-) :
-        TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
+        private val stringProvider: StringProvider,
+        private val notificationPermissionManager: NotificationPermissionManager,
+) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
             description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success)
             quickFix = null
             status = TestStatus.SUCCESS
         } else {
-            description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed)
-            quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
-                override fun doFix() {
-                    startNotificationSettingsIntent(context, activityResultLauncher)
+            if (notificationPermissionManager.isPermissionGranted(context)) {
+                description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed)
+                quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
+                    override fun doFix() {
+                        startNotificationSettingsIntent(context, testParameters.activityResultLauncher)
+                    }
+                }
+            } else {
+                // In this case, we can ask for user permission
+                description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_permission_failed)
+                quickFix = object : TroubleshootQuickFix(R.string.grant_permission) {
+                    override fun doFix() {
+                        notificationPermissionManager.askPermission(testParameters.permissionResultLauncher)
+                    }
                 }
             }
             status = TestStatus.FAILED
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushEndpoint.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushEndpoint.kt
index a29d1ad812..632f145c01 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushEndpoint.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushEndpoint.kt
@@ -16,8 +16,6 @@
 
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.pushers.UnifiedPushHelper
 import im.vector.app.core.resources.StringProvider
@@ -28,7 +26,7 @@ class TestUnifiedPushEndpoint @Inject constructor(
         private val unifiedPushHelper: UnifiedPushHelper,
 ) : TroubleshootTest(R.string.settings_troubleshoot_test_current_endpoint_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         val endpoint = unifiedPushHelper.getPrivacyFriendlyUpEndpoint()
         if (endpoint != null) {
             description = stringProvider.getString(R.string.settings_troubleshoot_test_current_endpoint_success, endpoint)
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushGateway.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushGateway.kt
index 19a4fd188f..52d5ad35a3 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushGateway.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestUnifiedPushGateway.kt
@@ -16,8 +16,6 @@
 
 package im.vector.app.features.settings.troubleshoot
 
-import android.content.Intent
-import androidx.activity.result.ActivityResultLauncher
 import im.vector.app.R
 import im.vector.app.core.pushers.UnifiedPushHelper
 import im.vector.app.core.resources.StringProvider
@@ -28,7 +26,7 @@ class TestUnifiedPushGateway @Inject constructor(
         private val stringProvider: StringProvider
 ) : TroubleshootTest(R.string.settings_troubleshoot_test_current_gateway_title) {
 
-    override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
+    override fun perform(testParameters: TestParameters) {
         description = stringProvider.getString(
                 R.string.settings_troubleshoot_test_current_gateway,
                 unifiedPushHelper.getPushGateway()
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt
index 76ba2378a0..1e12f2e314 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt
@@ -22,6 +22,11 @@ import kotlin.properties.Delegates
 
 abstract class TroubleshootTest(@StringRes val titleResId: Int) {
 
+    data class TestParameters(
+            val activityResultLauncher: ActivityResultLauncher<Intent>,
+            val permissionResultLauncher: ActivityResultLauncher<Array<String>>
+    )
+
     enum class TestStatus {
         NOT_STARTED,
         RUNNING,
@@ -40,7 +45,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) {
 
     var manager: NotificationTroubleshootTestManager? = null
 
-    abstract fun perform(activityResultLauncher: ActivityResultLauncher<Intent>)
+    abstract fun perform(testParameters: TestParameters)
 
     fun isFinished(): Boolean = (status == TestStatus.FAILED || status == TestStatus.SUCCESS)
 

From 238b41ce55158ed061df291c75d82a0051217d62 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Fri, 7 Oct 2022 09:58:50 +0200
Subject: [PATCH 029/400] Revoke notification permission (Android 13+) if user
 disables session notification.

---
 .../app/features/home/NotificationPermissionManager.kt     | 7 +++++++
 .../VectorSettingsNotificationPreferenceFragment.kt        | 1 +
 2 files changed, 8 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt b/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
index bb75730377..943c83148f 100644
--- a/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
+++ b/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
@@ -68,4 +68,11 @@ class NotificationPermissionManager @Inject constructor(
                 arrayOf(Manifest.permission.POST_NOTIFICATIONS)
         )
     }
+
+    fun eventuallyRevokePermission(
+            activity: Activity,
+    ) {
+        if (!sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) return
+        activity.revokeSelfPermissionOnKill(Manifest.permission.POST_NOTIFICATIONS)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
index 01eb277f27..405f4ddaf5 100644
--- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt
@@ -142,6 +142,7 @@ class VectorSettingsNotificationPreferenceFragment :
                 } else {
                     unifiedPushHelper.unregister(pushersManager)
                     session.pushersService().refreshPushers()
+                    notificationPermissionManager.eventuallyRevokePermission(requireActivity())
                 }
             }
         }

From 35dcfdd54ba95d783202899217b6c1eb584e0317 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 7 Oct 2022 10:04:00 +0200
Subject: [PATCH 030/400] Fix compilation note (by removing unused fun).

`Note: /home/runner/work/element-android/element-android/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java uses or overrides a deprecated API.`
---
 .../java/com/android/dialer/dialpadview/DialpadView.java  | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java b/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java
index dbb53cbdca..09079235af 100644
--- a/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java
+++ b/library/external/dialpad/src/main/java/com/android/dialer/dialpadview/DialpadView.java
@@ -202,14 +202,6 @@ public class DialpadView extends LinearLayout {
     zero.setLongHoverContentDescription(resources.getText(R.string.description_image_button_plus));
   }
 
-  private Drawable getDrawableCompat(Context context, int id) {
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-      return context.getDrawable(id);
-    } else {
-      return context.getResources().getDrawable(id);
-    }
-  }
-
   public void setShowVoicemailButton(boolean show) {
     View view = findViewById(R.id.dialpad_key_voicemail);
     if (view != null) {

From d4de55980911875d466431756877f7304304350b Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 7 Oct 2022 10:05:48 +0200
Subject: [PATCH 031/400] Fix lint warning.

---
 .../vector/app/features/home/NotificationPermissionManager.kt   | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt b/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
index 943c83148f..33ba1be02b 100644
--- a/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
+++ b/vector/src/main/java/im/vector/app/features/home/NotificationPermissionManager.kt
@@ -21,6 +21,7 @@ import android.app.Activity
 import android.content.pm.PackageManager
 import android.os.Build
 import androidx.activity.result.ActivityResultLauncher
+import androidx.annotation.ChecksSdkIntAtLeast
 import androidx.annotation.RequiresApi
 import androidx.core.content.ContextCompat
 import im.vector.app.R
@@ -34,6 +35,7 @@ class NotificationPermissionManager @Inject constructor(
         private val vectorPreferences: VectorPreferences,
 ) {
 
+    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
     fun isPermissionGranted(activity: Activity): Boolean {
         return if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
             ContextCompat.checkSelfPermission(

From e9e2e5011bccfc60dfa88969ca98a30a054f0dbd Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 7 Oct 2022 10:08:33 +0200
Subject: [PATCH 032/400] Fix test compilation.

---
 .../sdk/internal/network/ComputeUserAgentUseCaseTest.kt        | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt
index 0e7dc5dd32..2170371cde 100644
--- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt
+++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/network/ComputeUserAgentUseCaseTest.kt
@@ -27,6 +27,7 @@ import org.amshove.kluent.shouldBeEqualTo
 import org.junit.Before
 import org.junit.Test
 import org.matrix.android.sdk.BuildConfig
+import org.matrix.android.sdk.api.util.getApplicationInfoCompat
 import org.matrix.android.sdk.api.util.getPackageInfoCompat
 import java.lang.Exception
 
@@ -50,7 +51,7 @@ class ComputeUserAgentUseCaseTest {
         every { context.applicationContext } returns context
         every { context.packageName } returns A_PACKAGE_NAME
         every { context.packageManager } returns packageManager
-        every { packageManager.getApplicationInfo(any(), any()) } returns applicationInfo
+        every { packageManager.getApplicationInfoCompat(any(), any()) } returns applicationInfo
         every { packageManager.getPackageInfoCompat(any(), any()) } returns packageInfo
     }
 

From 04fb31666b0d1900e940fdc00924ef899055a455 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 7 Oct 2022 17:34:41 +0300
Subject: [PATCH 033/400] Implement show qr code screen.

---
 .../src/main/res/values/strings.xml           |  2 +
 .../features/login/qr/QrCodeLoginAction.kt    |  2 +
 .../features/login/qr/QrCodeLoginActivity.kt  | 27 ++++++--
 .../qr/QrCodeLoginInstructionsFragment.kt     | 13 +++-
 .../login/qr/QrCodeLoginShowQrCodeFragment.kt | 61 +++++++++++++++++++
 .../login/qr/QrCodeLoginStatusFragment.kt     |  4 +-
 .../login/qr/QrCodeLoginViewEvents.kt         |  1 +
 .../features/login/qr/QrCodeLoginViewModel.kt | 22 +++++++
 .../features/login/qr/QrCodeLoginViewState.kt |  1 +
 .../fragment_qr_code_login_instructions.xml   |  1 -
 .../fragment_qr_code_login_show_qr_code.xml   | 53 ++++++++++++++++
 11 files changed, 177 insertions(+), 10 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
 create mode 100644 vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index e0cd8f7919..22cddf84ce 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3333,6 +3333,8 @@
     <!-- QR Code Login -->
     <string name="qr_code_login_header_scan_qr_code_title">Scan QR code</string>
     <string name="qr_code_login_header_scan_qr_code_description">Use the camera on this device to scan the QR code shown on your other device:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Sign in with QR code</string>
+    <string name="qr_code_login_header_show_qr_code_description">Use your signed in device to scan the QR code below:</string>
     <string name="qr_code_login_header_connected_title">Secure connection established</string>
     <string name="qr_code_login_header_connected_description">Check your signed in device, the code below should be displayed. Confirm that the code below matches with that device:</string>
     <string name="qr_code_login_new_device_instruction_1">Open Element on your other device</string>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
index 948a771118..d946f744cf 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
@@ -20,4 +20,6 @@ import im.vector.app.core.platform.VectorViewModelAction
 
 sealed class QrCodeLoginAction : VectorViewModelAction {
     data class OnQrCodeScanned(val qrCode: String) : QrCodeLoginAction()
+    object QrCodeViewStarted : QrCodeLoginAction()
+    object ShowQrCode : QrCodeLoginAction()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 5e359bfb26..18efdca4ef 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -23,7 +23,7 @@ import android.view.View
 import com.airbnb.mvrx.Mavericks
 import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
-import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
 
 @AndroidEntryPoint
@@ -38,10 +38,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
             if (qrCodeLoginArgs?.loginType == QrCodeLoginType.LOGIN) {
-                addFragment(
+                addFragmentToBackstack(
                         views.container,
                         QrCodeLoginInstructionsFragment::class.java,
-                        qrCodeLoginArgs
+                        qrCodeLoginArgs,
+                        tag = FRAGMENT_QR_CODE_INSTRUCTIONS_TAG
                 )
             }
         }
@@ -53,19 +54,33 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         viewModel.observeViewEvents {
             when (it) {
                 QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
+                QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen()
             }
         }
     }
 
-    private fun handleNavigateToStatusScreen() {
-        addFragment(
+    private fun handleNavigateToShowQrCodeScreen() {
+        addFragmentToBackstack(
                 views.container,
-                QrCodeLoginStatusFragment::class.java
+                QrCodeLoginShowQrCodeFragment::class.java,
+                tag = FRAGMENT_SHOW_QR_CODE_TAG
+        )
+    }
+
+    private fun handleNavigateToStatusScreen() {
+        addFragmentToBackstack(
+                views.container,
+                QrCodeLoginStatusFragment::class.java,
+                tag = FRAGMENT_QR_CODE_STATUS_TAG
         )
     }
 
     companion object {
 
+        private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG"
+        private const val FRAGMENT_SHOW_QR_CODE_TAG = "FRAGMENT_SHOW_QR_CODE_TAG"
+        private const val FRAGMENT_QR_CODE_STATUS_TAG = "FRAGMENT_QR_CODE_STATUS_TAG"
+
         fun getIntent(context: Context, qrCodeLoginArgs: QrCodeLoginArgs): Intent {
             return Intent(context, QrCodeLoginActivity::class.java).apply {
                 putExtra(Mavericks.KEY_ARG, qrCodeLoginArgs)
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index 99dea345eb..c9cf59a7d5 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -23,23 +23,32 @@ import android.view.View
 import android.view.ViewGroup
 import com.airbnb.mvrx.activityViewModel
 import com.airbnb.mvrx.fragmentViewModel
+import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginInstructionsBinding
 import im.vector.app.features.qrcode.QrCodeScannerActivity
 import timber.log.Timber
 
+@AndroidEntryPoint
 class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginInstructionsBinding>() {
 
     private val viewModel: QrCodeLoginViewModel by activityViewModel()
 
+    override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginInstructionsBinding {
+        return FragmentQrCodeLoginInstructionsBinding.inflate(inflater, container, false)
+    }
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         initScanQrCodeButton()
+        initShowQrCodeButton()
     }
 
-    override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginInstructionsBinding {
-        return FragmentQrCodeLoginInstructionsBinding.inflate(inflater, container, false)
+    private fun initShowQrCodeButton() {
+        views.qrCodeLoginInstructionsShowQrCodeButton.debouncedClicks {
+            viewModel.handle(QrCodeLoginAction.ShowQrCode)
+        }
     }
 
     private fun initScanQrCodeButton() {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
new file mode 100644
index 0000000000..69fd8bde81
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.login.qr
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.airbnb.mvrx.activityViewModel
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.databinding.FragmentQrCodeLoginShowQrCodeBinding
+
+@AndroidEntryPoint
+class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShowQrCodeBinding>() {
+
+    private val viewModel: QrCodeLoginViewModel by activityViewModel()
+
+    override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginShowQrCodeBinding {
+        return FragmentQrCodeLoginShowQrCodeBinding.inflate(inflater, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        initCancelButton()
+        observeViewState()
+        viewModel.handle(QrCodeLoginAction.QrCodeViewStarted)
+    }
+
+    private fun initCancelButton() {
+        views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks {
+            activity?.supportFragmentManager?.popBackStack()
+        }
+    }
+
+    private fun observeViewState() {
+        viewModel.onEach {
+            it.generatedQrCodeData?.let { qrCodeData ->
+                showQrCode(qrCodeData)
+            }
+        }
+    }
+
+    private fun showQrCode(qrCodeData: String) {
+        views.qrCodeLoginSHowQrCodeImageView.setData(qrCodeData)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 52abfa788c..fa9ea1351e 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -22,11 +22,13 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.core.view.isVisible
 import com.airbnb.mvrx.activityViewModel
+import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
 import im.vector.app.features.themes.ThemeUtils
 
+@AndroidEntryPoint
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
 
     private val viewModel: QrCodeLoginViewModel by activityViewModel()
@@ -46,7 +48,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
                 is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(it.connectionStatus)
                 QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice()
                 QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn()
-                null -> TODO()
+                null -> { /* NOOP */ }
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index b8ebb65308..dc258408e7 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewEvents
 
 sealed class QrCodeLoginViewEvents : VectorViewEvents {
     object NavigateToStatusScreen : QrCodeLoginViewEvents()
+    object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index ac3b68da15..29a258823b 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -40,6 +40,21 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     override fun handle(action: QrCodeLoginAction) {
         when (action) {
             is QrCodeLoginAction.OnQrCodeScanned -> handleOnQrCodeScanned(action)
+            QrCodeLoginAction.QrCodeViewStarted -> handleQrCodeViewStarted()
+            QrCodeLoginAction.ShowQrCode -> handleShowQrCode()
+        }
+    }
+
+    private fun handleShowQrCode() {
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToShowQrCodeScreen)
+    }
+
+    private fun handleQrCodeViewStarted() {
+        val qrCodeData = generateQrCodeData()
+        setState {
+            copy(
+                    generatedQrCodeData = qrCodeData
+            )
         }
     }
 
@@ -84,4 +99,11 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     private fun isValidQrCode(qrCode: String): Boolean {
         return qrCode.startsWith("http")
     }
+
+    /**
+     * TODO. UI test purpose. Fixme accordingly.
+     */
+    private fun generateQrCodeData(): String {
+        return "https://element.io"
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
index 2f52cf789d..0c4457c12f 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt
@@ -21,6 +21,7 @@ import com.airbnb.mvrx.MavericksState
 data class QrCodeLoginViewState(
     val loginType: QrCodeLoginType,
     val connectionStatus: QrCodeLoginConnectionStatus? = null,
+    val generatedQrCodeData: String? = null,
 ) : MavericksState {
 
     constructor(args: QrCodeLoginArgs) : this(
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
index 83ea42d7e9..df043e8238 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
@@ -21,7 +21,6 @@
         android:id="@+id/qrCodeLoginInstructionsView"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="8dp"
         android:layout_marginTop="24dp"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml b/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml
new file mode 100644
index 0000000000..060d02498e
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml
@@ -0,0 +1,53 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingHorizontal="16dp"
+    android:background="?android:colorBackground">
+
+    <im.vector.app.features.login.qr.QrCodeLoginHeaderView
+        android:id="@+id/qrCodeLoginShowQrCodeHeaderView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:qrCodeLoginHeaderDescription="@string/qr_code_login_header_show_qr_code_description"
+        app:qrCodeLoginHeaderImageBackgroundTint="?colorPrimary"
+        app:qrCodeLoginHeaderImageResource="@drawable/ic_camera"
+        app:qrCodeLoginHeaderTitle="@string/qr_code_login_header_show_qr_code_title" />
+
+    <im.vector.app.features.login.qr.QrCodeLoginInstructionsView
+        android:id="@+id/qrCodeLoginShowQrCodeView"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="24dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginShowQrCodeHeaderView"
+        app:qrCodeLoginInstruction1="@string/qr_code_login_new_device_instruction_1"
+        app:qrCodeLoginInstruction2="@string/qr_code_login_new_device_instruction_2"
+        app:qrCodeLoginInstruction3="@string/qr_code_login_new_device_instruction_3" />
+
+    <im.vector.app.core.ui.views.QrCodeImageView
+        android:id="@+id/qrCodeLoginSHowQrCodeImageView"
+        android:layout_width="240dp"
+        android:layout_height="240dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginShowQrCodeView"
+        app:layout_constraintBottom_toTopOf="@id/qrCodeLoginShowQrCodeCancelButton"/>
+
+    <Button
+        android:id="@+id/qrCodeLoginShowQrCodeCancelButton"
+        style="@style/Widget.Vector.Button.Outlined.Login"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="40dp"
+        android:text="@string/action_cancel"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

From 2527cab73e6aa198b511550305c6b1ae5096e5d6 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 7 Oct 2022 17:48:40 +0300
Subject: [PATCH 034/400] Fix cancel actions.

---
 .../app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt | 2 +-
 .../app/features/login/qr/QrCodeLoginStatusFragment.kt     | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
index 69fd8bde81..b5437626bc 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
@@ -43,7 +43,7 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
 
     private fun initCancelButton() {
         views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks {
-            activity?.supportFragmentManager?.popBackStack()
+            parentFragmentManager.popBackStack()
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index fa9ea1351e..380db73376 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -39,9 +39,16 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
+        initCancelButton()
         observeViewState()
     }
 
+    private fun initCancelButton() {
+        views.qrCodeLoginStatusCancelButton.debouncedClicks {
+            parentFragmentManager.popBackStack()
+        }
+    }
+
     private fun observeViewState() {
         viewModel.onEach {
             when (it.connectionStatus) {

From 2b452d6fe5f2a50debbdbc8d8a330627aea78c67 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 7 Oct 2022 19:04:27 +0300
Subject: [PATCH 035/400] Implement qr code login failed states.

---
 .../src/main/res/values/strings.xml           |  5 ++++
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  1 +
 .../features/login/qr/QrCodeLoginErrorType.kt | 23 ++++++++++++++++
 .../login/qr/QrCodeLoginStatusFragment.kt     | 27 +++++++++++++++++++
 .../features/login/qr/QrCodeLoginViewModel.kt | 12 +++++++++
 .../layout/fragment_qr_code_login_status.xml  | 12 +++++++++
 6 files changed, 80 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 22cddf84ce..86e1afa471 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3337,6 +3337,10 @@
     <string name="qr_code_login_header_show_qr_code_description">Use your signed in device to scan the QR code below:</string>
     <string name="qr_code_login_header_connected_title">Secure connection established</string>
     <string name="qr_code_login_header_connected_description">Check your signed in device, the code below should be displayed. Confirm that the code below matches with that device:</string>
+    <string name="qr_code_login_header_failed_title">Unsuccessful connection</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Linking with this device is not supported.</string>
+    <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
+    <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
     <string name="qr_code_login_new_device_instruction_1">Open Element on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Link a device\'</string>
@@ -3347,5 +3351,6 @@
     <string name="qr_code_login_connecting_to_device">Connecting to device</string>
     <string name="qr_code_login_signing_in">Signing you in</string>
     <string name="qr_code_login_status_no_match">No match?</string>
+    <string name="qr_code_login_try_again">Try again</string>
 
 </resources>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index 33c734d2f0..f2d50eafc4 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -20,4 +20,5 @@ sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
     data class Connected(val securityCode: String) : QrCodeLoginConnectionStatus()
     object SigningIn : QrCodeLoginConnectionStatus()
+    data class Failed(val errorType: QrCodeLoginErrorType, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
new file mode 100644
index 0000000000..9a6cc13de0
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.login.qr
+
+enum class QrCodeLoginErrorType {
+    DEVICE_IS_NOT_SUPPORTED,
+    TIMEOUT,
+    REQUEST_WAS_DENIED,
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 380db73376..26d24d9ed0 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -55,17 +55,42 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
                 is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(it.connectionStatus)
                 QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice()
                 QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn()
+                is QrCodeLoginConnectionStatus.Failed -> handleFailed(it.connectionStatus)
                 null -> { /* NOOP */ }
             }
         }
     }
 
+    private fun handleFailed(connectionStatus: QrCodeLoginConnectionStatus.Failed) {
+        views.qrCodeLoginStatusLoadingLayout.isVisible = false
+        views.qrCodeLoginStatusHeaderView.isVisible = true
+        views.qrCodeLoginStatusSecurityCode.isVisible = false
+        views.qrCodeLoginStatusNoMatchLayout.isVisible = false
+        views.qrCodeLoginStatusCancelButton.isVisible = true
+        views.qrCodeLoginStatusTryAgainButton.isVisible = connectionStatus.canTryAgain
+        views.qrCodeLoginStatusHeaderView.setTitle(getString(R.string.qr_code_login_header_failed_title))
+        views.qrCodeLoginStatusHeaderView.setDescription(getErrorCode(connectionStatus.errorType))
+        views.qrCodeLoginStatusHeaderView.setImage(
+                imageResource = R.drawable.ic_qr_code_login_failed,
+                backgroundTintColor = ThemeUtils.getColor(requireContext(), R.attr.colorError)
+        )
+    }
+
+    private fun getErrorCode(errorType: QrCodeLoginErrorType): String {
+        return when (errorType) {
+            QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            QrCodeLoginErrorType.TIMEOUT -> getString(R.string.qr_code_login_header_failed_timeout_description)
+            QrCodeLoginErrorType.REQUEST_WAS_DENIED -> getString(R.string.qr_code_login_header_failed_denied_description)
+        }
+    }
+
     private fun handleConnectingToDevice() {
         views.qrCodeLoginStatusLoadingLayout.isVisible = true
         views.qrCodeLoginStatusHeaderView.isVisible = false
         views.qrCodeLoginStatusSecurityCode.isVisible = false
         views.qrCodeLoginStatusNoMatchLayout.isVisible = false
         views.qrCodeLoginStatusCancelButton.isVisible = true
+        views.qrCodeLoginStatusTryAgainButton.isVisible = false
         views.qrCodeLoginStatusLoadingTextView.setText(R.string.qr_code_login_connecting_to_device)
     }
 
@@ -75,6 +100,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         views.qrCodeLoginStatusSecurityCode.isVisible = false
         views.qrCodeLoginStatusNoMatchLayout.isVisible = false
         views.qrCodeLoginStatusCancelButton.isVisible = false
+        views.qrCodeLoginStatusTryAgainButton.isVisible = false
         views.qrCodeLoginStatusLoadingTextView.setText(R.string.qr_code_login_signing_in)
     }
 
@@ -84,6 +110,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         views.qrCodeLoginStatusSecurityCode.isVisible = true
         views.qrCodeLoginStatusNoMatchLayout.isVisible = true
         views.qrCodeLoginStatusCancelButton.isVisible = true
+        views.qrCodeLoginStatusTryAgainButton.isVisible = false
         views.qrCodeLoginStatusSecurityCode.text = connectionStatus.securityCode
         views.qrCodeLoginStatusHeaderView.setTitle(getString(R.string.qr_code_login_header_connected_title))
         views.qrCodeLoginStatusHeaderView.setDescription(getString(R.string.qr_code_login_header_connected_description))
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 29a258823b..c28c9ddc1d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -70,10 +70,22 @@ class QrCodeLoginViewModel @AssistedInject constructor(
 
         // TODO. UI test purpose. Fixme remove!
         viewModelScope.launch {
+            delay(3000)
+            onFailed(QrCodeLoginErrorType.TIMEOUT, false)
             delay(3000)
             onConnectionEstablished("1234-ABCD-5678-EFGH")
             delay(3000)
             onSigningIn()
+            delay(3000)
+            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, true)
+        }
+    }
+
+    private fun onFailed(errorType: QrCodeLoginErrorType, canTryAgain: Boolean) {
+        setState {
+            copy(
+                    connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain)
+            )
         }
     }
 
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_status.xml b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
index 0c200eeb83..240ccaf070 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_status.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
@@ -83,6 +83,18 @@
             app:drawableTint="@color/alert_default_error_background" />
     </FrameLayout>
 
+    <Button
+        android:id="@+id/qrCodeLoginStatusTryAgainButton"
+        style="@style/Widget.Vector.Button.Login"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="12dp"
+        android:text="@string/qr_code_login_try_again"
+        android:visibility="gone"
+        app:layout_constraintBottom_toTopOf="@id/qrCodeLoginStatusNoMatchLayout"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:visibility="visible" />
 
     <Button
         android:id="@+id/qrCodeLoginStatusCancelButton"

From 236b30367fb7eef3c48095e414630156d25515aa Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 7 Oct 2022 19:13:59 +0300
Subject: [PATCH 036/400] Fix ui test case.

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index c28c9ddc1d..a6d991fb1a 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -71,13 +71,13 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         // TODO. UI test purpose. Fixme remove!
         viewModelScope.launch {
             delay(3000)
-            onFailed(QrCodeLoginErrorType.TIMEOUT, false)
+            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
             delay(3000)
             onConnectionEstablished("1234-ABCD-5678-EFGH")
             delay(3000)
             onSigningIn()
             delay(3000)
-            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, true)
+            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
         }
     }
 

From 3c11fdd765606d478cb4ad528c748c41f9ec5532 Mon Sep 17 00:00:00 2001
From: phardyle <bradney_ccea@aleeas.com>
Date: Thu, 6 Oct 2022 07:44:47 +0000
Subject: [PATCH 037/400] Translated using Weblate (Chinese (Simplified))

Currently translated at 97.1% (2374 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hans/
---
 library/ui-strings/src/main/res/values-zh-rCN/strings.xml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
index eba96e82c3..b0a1eb1402 100644
--- a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
@@ -1583,12 +1583,12 @@
     <string name="auth_msisdn_already_defined">此电话号码已定义。</string>
     <string name="settings_phone_number_empty">你的账户尚未添加电话号码</string>
     <string name="settings_emails">电子邮件地址</string>
-    <string name="settings_emails_empty">你的账户尚未添加电子邮件</string>
+    <string name="settings_emails_empty">你的账户尚未添加电子邮件地址</string>
     <string name="settings_phone_numbers">电话号码</string>
     <string name="settings_remove_three_pid_confirmation_content">移除 %s?</string>
     <string name="error_threepid_auth_failed">请确认你已点击我们向你发送的电子邮件中的链接。</string>
     <string name="settings_emails_and_phone_numbers_title">电子邮件和电话号码</string>
-    <string name="settings_emails_and_phone_numbers_summary">管理链接到你的 Matrix 账户的电子邮件和电话号码</string>
+    <string name="settings_emails_and_phone_numbers_summary">管理链接到你的Matrix账户的电子邮件地址和电话号码</string>
     <string name="settings_text_message_sent_hint">代码</string>
     <string name="login_msisdn_notice">请使用国际格式(电话号码必须以“+”开始)</string>
     <string name="confirm_your_identity_quad_s">验证此登录来确认你的身份,授权其访问加密消息。</string>
@@ -2171,7 +2171,7 @@
     <string name="room_settings_space_access_title">空间访问</string>
     <string name="room_settings_access_rules_pref_dialog_title">谁可以访问?</string>
     <string name="settings_notification_emails_enable_for_email">启用 %s 的电邮通知</string>
-    <string name="settings_notification_emails_no_emails">要接收通知邮件,请将一个电子邮箱关联到你的 Matrix 账户</string>
+    <string name="settings_notification_emails_no_emails">要接收通知邮件,请将一个电子邮件地址关联到你的Matrix账户</string>
     <string name="settings_notification_emails_category">电子邮件通知</string>
     <string name="room_permissions_upgrade_the_space">升级空间</string>
     <string name="room_permissions_change_space_name">更改空间名称</string>

From 9f5a1adafe97eeafce126a8100ca419808658c7e Mon Sep 17 00:00:00 2001
From: Jozef Gaal <preklady@mayday.sk>
Date: Thu, 6 Oct 2022 21:01:26 +0000
Subject: [PATCH 038/400] Translated using Weblate (Slovak)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/sk/
---
 fastlane/metadata/android/sk/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/sk/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/sk/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/sk/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/sk/changelogs/40105000.txt b/fastlane/metadata/android/sk/changelogs/40105000.txt
new file mode 100644
index 0000000000..9f48366456
--- /dev/null
+++ b/fastlane/metadata/android/sk/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Hlavné zmeny v tejto verzii: Oneskorené priame správy sú zapnuté ako predvolené.
+Úplný zoznam zmien: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sk/changelogs/40105020.txt b/fastlane/metadata/android/sk/changelogs/40105020.txt
new file mode 100644
index 0000000000..ceec087a30
--- /dev/null
+++ b/fastlane/metadata/android/sk/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Hlavné zmeny v tejto verzii: Nové rozvrhnutie aplikácie je zapnuté ako predvolené!
+Úplný zoznam zmien: https://github.com/vector-im/element-android/releases

From 6a24d34e623901dc2262eaedff24c0ae8ad23abe Mon Sep 17 00:00:00 2001
From: waclaw66 <waclaw66@seznam.cz>
Date: Fri, 7 Oct 2022 05:54:12 +0000
Subject: [PATCH 039/400] Translated using Weblate (Czech)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/cs/
---
 fastlane/metadata/android/cs-CZ/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/cs-CZ/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40105000.txt b/fastlane/metadata/android/cs-CZ/changelogs/40105000.txt
new file mode 100644
index 0000000000..032fa68e48
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Hlavní změny v této verzi: Odložené přímé zprávy jsou ve výchozím nastavení povoleny.
+Úplný seznam změn: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40105020.txt b/fastlane/metadata/android/cs-CZ/changelogs/40105020.txt
new file mode 100644
index 0000000000..b2b7ca7675
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Hlavní změny v této verzi: Nové rozvržení aplikace je povoleno ve výchozím nastavení!
+Úplný seznam změn: https://github.com/vector-im/element-android/releases

From 49e7c7be9aeecc495a6d40a5c9bf60522a99b843 Mon Sep 17 00:00:00 2001
From: Vri <element@vrifox.cc>
Date: Wed, 5 Oct 2022 17:22:49 +0000
Subject: [PATCH 040/400] Translated using Weblate (German)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/de/
---
 fastlane/metadata/android/de-DE/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/de-DE/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/de-DE/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/de-DE/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/de-DE/changelogs/40105000.txt b/fastlane/metadata/android/de-DE/changelogs/40105000.txt
new file mode 100644
index 0000000000..cd3ec93387
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Die wichtigste Änderung in dieser Version: Verzögerte Direktnachrichten standardmäßig aktiviert!
+Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases/tag/v1.2.0
diff --git a/fastlane/metadata/android/de-DE/changelogs/40105020.txt b/fastlane/metadata/android/de-DE/changelogs/40105020.txt
new file mode 100644
index 0000000000..ac08e662db
--- /dev/null
+++ b/fastlane/metadata/android/de-DE/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Die wichtigste Änderung in dieser Version: Neues App-Layout standardmäßig aktiviert!
+Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases/tag/v1.2.0

From 53a0520525a97b47fc8b05d13548610505ebd7eb Mon Sep 17 00:00:00 2001
From: Glandos <bugs-github@antipoul.fr>
Date: Fri, 7 Oct 2022 12:02:41 +0000
Subject: [PATCH 041/400] Translated using Weblate (French)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/fr/
---
 .../src/main/res/values-fr/strings.xml        | 55 +++++++++++++++----
 1 file changed, 44 insertions(+), 11 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-fr/strings.xml b/library/ui-strings/src/main/res/values-fr/strings.xml
index c7100e3a1e..cd7d7e14a8 100644
--- a/library/ui-strings/src/main/res/values-fr/strings.xml
+++ b/library/ui-strings/src/main/res/values-fr/strings.xml
@@ -934,11 +934,11 @@
     <string name="settings_discovery_identity_server_info">Vous utilisez actuellement %1$s pour découvrir et être découvrable par les contacts existants que vous connaissez.</string>
     <string name="settings_discovery_identity_server_info_none">Vous n’utilisez actuellement aucun serveur d’identité. Pour découvrir et être découvrable par les contacts existants que vous connaissez, configurez-en un ci-dessous.</string>
     <string name="settings_discovery_emails_title">Adresses électronique découvrables</string>
-    <string name="settings_discovery_no_mails">Les options de découverte apparaîtront quand vous aurez ajouté un courriel.</string>
+    <string name="settings_discovery_no_mails">Les options de découverte apparaîtront quand vous aurez ajouté une adresse courriel.</string>
     <string name="settings_discovery_no_msisdn">Les options de découverte apparaîtront quand vous aurez ajouté un numéro de téléphone.</string>
     <string name="settings_discovery_disconnect_identity_server_info">La déconnexion du serveur d’identité signifie que vous ne pourrez plus être découvrable par les autres utilisateurs et que vous ne pourrez plus inviter d’autres personnes par courriel ou par téléphone.</string>
     <string name="settings_discovery_msisdn_title">Numéros de téléphone découvrables</string>
-    <string name="settings_discovery_confirm_mail">Nous vous avons envoyé un courriel de confirmation à %s, consultez vos courriels et cliquez sur le lien de confirmation</string>
+    <string name="settings_discovery_confirm_mail">Nous vous avons envoyé un courriel à %s, consultez vos courriels et cliquez sur le lien de confirmation</string>
     <string name="settings_discovery_enter_identity_server">Renseignez l’URL d’un serveur d’identité</string>
     <string name="settings_discovery_bad_identity_server">Impossible de se connecter au serveur d’identité</string>
     <string name="settings_discovery_please_enter_server">Veuillez renseigner l’URL du serveur d’identité</string>
@@ -1066,7 +1066,7 @@
     <string name="login_registration_not_supported">L’application ne peut pas créer de compte sur ce serveur d’accueil.
 \n
 \nVoulez-vous vous inscrire en utilisant un client web \?</string>
-    <string name="login_login_with_email_error">Ce courriel n’est associé à aucun compte.</string>
+    <string name="login_login_with_email_error">Cette adresse de courriel n’est associée à aucun compte.</string>
     <string name="login_reset_password_on">Réinitialiser le mot de passe sur %1$s</string>
     <string name="login_reset_password_notice">Un courriel de vérification sera envoyé à votre adresse pour confirmer la configuration de votre nouveau mot de passe.</string>
     <string name="login_reset_password_submit">Suivant</string>
@@ -1075,7 +1075,7 @@
     <string name="login_reset_password_warning_title">Attention !</string>
     <string name="login_reset_password_warning_content">Le changement de mot de passe réinitialisera toutes les clés de chiffrement sur toutes vos sessions, rendant l’historique des discussions chiffrées illisible. Configurez la sauvegarde de clés ou exportez vos clés de salon depuis une autre session avant de réinitialiser votre mot de passe.</string>
     <string name="login_reset_password_warning_submit">Poursuivre</string>
-    <string name="login_reset_password_error_not_found">Ce courriel n’est lié à aucun compte</string>
+    <string name="login_reset_password_error_not_found">Ce adresse de courriel n’est liée à aucun compte</string>
     <string name="login_reset_password_mail_confirmation_title">Vérifiez votre boîte de réception</string>
     <string name="login_reset_password_mail_confirmation_notice">Un courriel de vérification a été envoyé à %1$s.</string>
     <string name="login_reset_password_mail_confirmation_notice_2">Touchez le lien pour confirmer votre nouveau mot de passe. Après avoir suivi le lien qu’il contient, cliquez ci-dessous.</string>
@@ -1089,7 +1089,7 @@
 \n
 \nArrêter le processus de changement \?</string>
     <string name="login_set_email_title">Définir l’adresse électronique</string>
-    <string name="login_set_email_notice">Définir une adresse électronique pour récupérer votre compte. Plus tard, vous pourrez éventuellement autoriser des personnes à vous retrouver avec votre adresse électronique.</string>
+    <string name="login_set_email_notice">Définir une adresse de courriel pour récupérer votre compte. Plus tard, vous pourrez éventuellement autoriser des personnes à vous retrouver avec cette adresse.</string>
     <string name="login_set_email_mandatory_hint">Courriel</string>
     <string name="login_set_email_optional_hint">Courriel (facultatif)</string>
     <string name="login_set_email_submit">Suivant</string>
@@ -1432,7 +1432,7 @@
     <string name="event_redacted">Message supprimé</string>
     <string name="settings_show_redacted">Afficher les messages supprimés</string>
     <string name="settings_show_redacted_summary">Afficher un remplaçant pour les messages supprimés</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Nous vous avons envoyé un courriel de confirmation à %s, consultez vos courriels et cliquez sur le lien de confirmation</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Nous vous avons envoyé un courriel à %s, consultez vos courriels et cliquez sur le lien de confirmation</string>
     <string name="settings_text_message_sent_wrong_code">Le code de vérification n’est pas correct.</string>
     <string name="uploads_media_title">MÉDIA</string>
     <string name="uploads_media_no_result">Il n’y a aucun média dans ce salon</string>
@@ -1455,7 +1455,7 @@
     <string name="identity_server_error_outdated_home_server">Cette opération n’est pas possible. Le serveur d’accueil est obsolète.</string>
     <string name="identity_server_error_no_identity_server_configured">Veuillez d’abord configurer un serveur d’identité.</string>
     <string name="identity_server_error_terms_not_signed">Veuillez d’abord accepter les termes du serveur d’identité dans les paramètres.</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Pour votre vie privée, ${app_name} prend uniquement en charge l’envoi des adresses électronique et des numéros de téléphone hachés.</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Pour votre vie privée, ${app_name} prend uniquement en charge l’envoi des adresses de courriel et des numéros de téléphone hachés.</string>
     <string name="identity_server_error_binding_error">L’association a échoué.</string>
     <string name="identity_server_error_no_current_binding_error">Il n’y a actuellement aucune association avec cet identifiant.</string>
     <string name="identity_server_set_default_notice">Votre serveur d’accueil (%1$s) propose d’utiliser %2$s comme serveur d’identité</string>
@@ -1669,7 +1669,7 @@
     <string name="error_threepid_auth_failed">Assurez-vous d\'avoir cliqué sur le lien envoyé par courriel.</string>
     <string name="settings_remove_three_pid_confirmation_content">Supprimer %s \?</string>
     <string name="settings_phone_numbers">Numéros de téléphone</string>
-    <string name="settings_emails_empty">Aucune adresse électronique n’a été ajoutée à votre compte</string>
+    <string name="settings_emails_empty">Aucune adresse de courriel n’a été ajoutée à votre compte</string>
     <string name="settings_emails">Adresses électroniques</string>
     <string name="settings_phone_number_empty">Aucun numéro de téléphone n’a été ajouté à votre compte</string>
     <string name="search_banned_user_hint">Filtrer les utilisateurs exclus</string>
@@ -1734,7 +1734,7 @@
     <string name="attachment_viewer_item_x_of_y">%1$d de %2$d</string>
     <string name="settings_discovery_consent_action_give_consent">Autoriser</string>
     <string name="settings_discovery_consent_action_revoke">Révoquer mon autorisation</string>
-    <string name="settings_discovery_consent_notice_on">Vous avez donné votre autorisation pour envoyer des courriels et des numéros de téléphone à ce serveur d’identité pour découvrir d\'autres utilisateurs à partir de vos contacts.</string>
+    <string name="settings_discovery_consent_notice_on">Vous avez donné votre autorisation pour envoyer des adresses de courriel et des numéros de téléphone à ce serveur d’identité pour découvrir d\'autres utilisateurs à partir de vos contacts.</string>
     <string name="settings_discovery_consent_title">Envoyer des courriels et des numéros de téléphone</string>
     <string name="direct_room_user_list_suggestions_title">Suggestions</string>
     <string name="direct_room_user_list_known_title">Utilisateurs connus</string>
@@ -2142,7 +2142,7 @@
     <string name="settings_notification_mentions_and_keywords">Mentions et mots-clés</string>
     <string name="settings_notification_default">Notifications par défaut</string>
     <string name="link_this_email_with_your_account">%s dans les paramètres pour recevoir les invitations directement dans ${app_name}.</string>
-    <string name="link_this_email_settings_link">Lier ce courriel à votre compte</string>
+    <string name="link_this_email_settings_link">Lier cette adresse de courriel à votre compte</string>
     <string name="this_invite_to_this_space_was_sent">Cette invitation à cette espace a été envoyée à %s qui n’est pas associé à votre compte</string>
     <string name="this_invite_to_this_room_was_sent">Cette invitation à ce salon a été envoyée à %s qui n’est pas associé à votre compte</string>
     <string name="all_rooms_youre_in_will_be_shown_in_home">Tous les salons dans lesquels vous vous trouvez seront affichés sur l’Accueil.</string>
@@ -2258,7 +2258,7 @@
     <string name="create_poll_question_title">Question ou sujet du sondage</string>
     <string name="create_poll_title">Créer un sondage</string>
     <string name="attachment_type_poll">Sondage</string>
-    <string name="identity_server_consent_dialog_title_2">Envoyer des courriels et des numéros de téléphone à %s</string>
+    <string name="identity_server_consent_dialog_title_2">Envoyer des adresses de courriel et des numéros de téléphone à %s</string>
     <string name="settings_discovery_consent_notice_off_2">Vos contacts sont personnels et privés. Pour découvrir des utilisateurs à partir de vos contacts, nous avons besoin de votre permission pour envoyer les informations des contacts à votre serveur d’identité.</string>
     <string name="shortcut_disabled_reason_sign_out">La session a été déconnectée !</string>
     <string name="shortcut_disabled_reason_room_left">Le salon a été quitté !</string>
@@ -2710,4 +2710,37 @@
     <string name="labs_enable_deferred_dm_title">Activer les conversations privées différées</string>
     <string name="labs_enable_new_app_layout_summary">Un Element simplifié avec des onglets optionnels</string>
     <string name="labs_enable_new_app_layout_title">Activer la nouvelle présentation</string>
+    <string name="device_manager_learn_more_session_rename">Les autres utilisateurs en conversations privées ou salons qui peuvent vous parler sont capables de voir la liste complète de vos sessions.
+\n
+\nCela leur fournit une preuve de confiance que c’est bien avec vous qu\'ils communiquent, mais cela veut également dire qu’ils peuvent voir le nom de la session que vous saisissez ici.</string>
+    <string name="device_manager_learn_more_session_rename_title">Renommer les sessions</string>
+    <string name="device_manager_learn_more_sessions_verified">Les sessions vérifiées sont celles qui sont identifiées avec votre mot de passe puis vérifiée, soit à l’aide de votre phrase de sécurité, ou bien par la vérification croisée.
+\n
+\nCela signifie qu’elles possèdent les clés de chiffrement de vos messages passés, et certifient aux autres utilisateurs avec qui vous communiquez que ces sessions viennent vraiment de vous.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Sessions vérifiées</string>
+    <string name="device_manager_learn_more_sessions_unverified">Les sessions non vérifiées sont celles qui sont identifiées avec votre mot de passe sans avoir fait de vérification croisée.
+\n
+\nVous devriez tout particulièrement vous assurer de reconnaître ces sessions, car elles pourraient être la preuve d’un usage non autorisé de votre compte.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Sessions non vérifiées</string>
+    <string name="device_manager_learn_more_sessions_inactive">Les sessions inactives sont celles que vous n’avez pas utilisées depuis un certain temps, mais qui continue de recevoir les clés de chiffrements.
+\n
+\nLa suppression des sessions inactives améliore la sécurité et la performance, et vous aide à identifier si une nouvelle session est douteuse.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Sessions inactives</string>
+    <string name="device_manager_session_rename_warning">Soyez conscient que les noms de sessions sont également visibles pour les personnes avec lesquelles vous communiquez.</string>
+    <string name="device_manager_session_rename_description">Les noms de sessions personnalisés peuvent vous aider à reconnaître vos appareils plus facilement.</string>
+    <string name="device_manager_session_rename_edit_hint">Nom de la session</string>
+    <string name="device_manager_session_rename">Renommer la session</string>
+    <string name="device_manager_session_overview_signout">Se déconnecter de cette session</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Non vérifiée · Votre session actuelle</string>
+    <string name="tooltip_attachment_voice_broadcast">Démarrer une diffusion audio</string>
+    <string name="key_authenticity_not_guaranteed">L’authenticité de ce message chiffré ne peut pas être garantie sur cet appareil.</string>
+    <string name="settings_security_incognito_keyboard_summary">Demande au clavier de ne pas mettre à jour les données personnalisées, comme l’historique de la frappe et le dictionnaire composé de ce que vous avez tapé dans vos conversations. Il est possible que certains claviers ne respectent pas ce paramètre.</string>
+    <string name="settings_security_incognito_keyboard_title">Clavier incognito</string>
+    <string name="command_description_table_flip">Préfixe le message par (╯°□°)╯︵ ┻━┻</string>
+    <string name="attachment_type_voice_broadcast">Diffusion audio</string>
+    <string name="command_description_devtools">Ouvrir l’écran des outils développeurs</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Vous avez activé le chiffrement vers les sessions vérifiées uniquement, pour tous les salons, dans les paramètres Sécurité.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Il y a des appareils non vérifiés dans ce salon, ils ne pourront pas déchiffrer vos messages envoyés.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Ne jamais envoyer de messages chiffrés aux sessions non vérifiées dans ce salon.</string>
+    <string name="action_got_it">Compris</string>
 </resources>
\ No newline at end of file

From 7ce2ae22c4dd6b11285193d37aa443656a7464a3 Mon Sep 17 00:00:00 2001
From: "Auri B. P" <auri97@gmail.com>
Date: Thu, 6 Oct 2022 08:21:02 +0000
Subject: [PATCH 042/400] Translated using Weblate (Catalan)

Currently translated at 99.7% (2439 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ca/
---
 .../src/main/res/values-ca/strings.xml        | 56 ++++++++++++++-----
 1 file changed, 42 insertions(+), 14 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ca/strings.xml b/library/ui-strings/src/main/res/values-ca/strings.xml
index 25c490807e..a6a5ec0b8d 100644
--- a/library/ui-strings/src/main/res/values-ca/strings.xml
+++ b/library/ui-strings/src/main/res/values-ca/strings.xml
@@ -891,7 +891,7 @@
     <string name="room_participants_remove_reason">Motiu de l\'expulsió</string>
     <string name="room_participants_remove_title">Expulsa usuari</string>
     <string name="login_signin_sso">Continua amb SSO</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Per a la teva pròpia privadesa, ${app_name} només admet l\'enviament del \"hash\" de correus electrònics i números de telèfon.</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Per a la teva pròpia privadesa, ${app_name} només admet l\'enviament del \'hash\' d\'adreces de correu electrònic i números de telèfon.</string>
     <string name="command_description_discard_session_not_handled">Només admès en sales xifrades</string>
     <string name="encryption_unknown_algorithm_tile_description">El xifrat que utilitza aquesta sala no és compatible</string>
     <string name="login_registration_not_supported">L\'aplicació no ha pogut crear un compte en aquest servidor.
@@ -978,7 +978,7 @@
     <string name="login_error_ssl_other">Error SSL.</string>
     <string name="call_failed_no_connection">La trucada d\'${app_name} ha fallat</string>
     <string name="login_reset_password_on">Restableix la contrasenya a %1$s</string>
-    <string name="login_login_with_email_error">Aquest correu electrònic no està associat amb cap compte.</string>
+    <string name="login_login_with_email_error">Aquesta adreça de correu electrònic no està associada a cap compte.</string>
     <string name="login_registration_disabled">Ho sentim, aquest servidor no accepta comptes nous.</string>
     <string name="login_sso_error_message">S\'ha produït un error en carregar la pàgina: %1$s (%2$d)</string>
     <string name="login_server_url_form_common_notice">Introdueix l\'adreça del servidor que vulguis utilitzar</string>
@@ -1002,10 +1002,10 @@
     <string name="settings_discovery_consent_action_give_consent">Dóna consentiment</string>
     <string name="settings_discovery_consent_action_revoke">Revoca el meu consentiment</string>
     <string name="settings_discovery_consent_title">Envia correus i números de telèfon</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">T\'hem enviat un correu de confirmació a %s, primer revisa el correu i fes clic a l\'enllaç de confirmació</string>
-    <string name="settings_discovery_confirm_mail">T\'hem enviat un correu de confirmació a %s, revisa\'l i fes clic a l\'enllaç de confirmació</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Hem enviat un correu electrònic a %s, primer revisa el correu i fes clic a l\'enllaç de confirmació</string>
+    <string name="settings_discovery_confirm_mail">Hem enviat un correu electrònic a %s, revisa\'l i fes clic a l\'enllaç de confirmació</string>
     <string name="settings_discovery_no_msisdn">Les opcions de descobriment apareixeran quan hagis afegit un número de telèfon.</string>
-    <string name="settings_discovery_no_mails">Les opcions de descobriment apareixeran quan hagis afegit un correu.</string>
+    <string name="settings_discovery_no_mails">Les opcions de descobriment apareixeran quan hagis afegit una adreça de correu electrònic.</string>
     <string name="disconnect_identity_server">Desconnecta el servidor d\'identitat</string>
     <string name="add_identity_server">Configura el servidor d\'identitat</string>
     <string name="change_identity_server">Canvia el servidor d\'identitat</string>
@@ -1100,7 +1100,7 @@
     <string name="room_settings_alias_subtitle">Revisa i gestiona les adreces d\'aquesta sala i la seva visibilitat al directori de sales.</string>
     <string name="room_settings_alias_title">Adreces de la sala</string>
     <string name="room_settings_room_access_title">Accés a la sala</string>
-    <string name="settings_emails_and_phone_numbers_summary">Gestiona els correus i els números de telèfon vinculats amb el teu compte de Matrix</string>
+    <string name="settings_emails_and_phone_numbers_summary">Gestiona les adreces de correu electrònic i els números de telèfon vinculats amb el teu compte de Matrix</string>
     <string name="settings_emails_and_phone_numbers_title">Correus i números de telèfon</string>
     <string name="disabled_integration_dialog_content">Activa \'Permet integracions\' a la configuració per poder fer això.</string>
     <string name="disabled_integration_dialog_title">Les integracions estan desactivades</string>
@@ -1154,7 +1154,7 @@
     <string name="error_threepid_auth_failed">Assegura\'t de que has clicat a l\'enllaç del correu que t\'hem enviat.</string>
     <string name="settings_remove_three_pid_confirmation_content">Elimina %s\?</string>
     <string name="settings_phone_numbers">Números de telèfon</string>
-    <string name="settings_emails_empty">No s\'ha afegit cap correu electrònic al teu compte</string>
+    <string name="settings_emails_empty">No s\'ha afegit cap adreça de correu electrònic al teu compte</string>
     <string name="settings_phone_number_empty">No s\'ha afegit cap número de telèfon al teu compte</string>
     <string name="search_banned_user_hint">Filtra usuaris vetats</string>
     <string name="room_settings_topic_hint">Tema</string>
@@ -1249,7 +1249,7 @@
     <string name="room_list_quick_actions_settings">Configuració</string>
     <string name="message_ignore_user">Ignora usuari</string>
     <string name="rotate_and_crop_screen_title">Gira i retalla</string>
-    <string name="login_set_email_notice">Estableix un correu per a la recuperació del compte. Posterior i opcionalment, pots permetre que els usuaris que coneixes et puguin trobar a partir del correu electrònic.</string>
+    <string name="login_set_email_notice">Estableix una adreça de correu electrònic per recuperar el teu compte. Posterior i opcionalment, pots permetre que els usuaris que coneixes et puguin trobar a partir d\'aquesta adreça.</string>
     <string name="login_set_msisdn_submit">Següent</string>
     <string name="login_set_msisdn_optional_hint">Número de telèfon (opcional)</string>
     <string name="login_set_msisdn_mandatory_hint">Número de telèfon</string>
@@ -1262,7 +1262,7 @@
     <string name="autodiscover_well_known_error">No s\'ha pogut trobar un servidor local vàlid. Comprova l\'identificador</string>
     <string name="settings_agree_to_terms">Accepta els termes de servei del servidor d\'identitat (%s) per poder ser trobat mitjançant l\'adreça de correu electrònic o el número de telèfon.</string>
     <string name="settings_discovery_enter_identity_server">Introdueix l\'URL d\'un servidor d\'identitat</string>
-    <string name="settings_discovery_consent_notice_on">Has donat el teu consentiment per poder enviar correus electrònics i números de telèfon a aquest servidor d\'identitat per trobar altres usuaris dels teus contactes.</string>
+    <string name="settings_discovery_consent_notice_on">Has donat el teu consentiment per poder enviar adreces de correu electrònic i números de telèfon a aquest servidor d\'identitat per trobar altres usuaris dels teus contactes.</string>
     <string name="settings_discovery_msisdn_title">Números de telèfon perquè et trobin</string>
     <string name="settings_discovery_disconnect_identity_server_info">Si et desconnectes del servidor d\'identitat no podràs ser trobat per altres usuaris ni convidar-los mitjançant el correu electrònic o el número de telèfon.</string>
     <string name="settings_discovery_emails_title">Correus electrònics perquè et puguin trobar</string>
@@ -1611,7 +1611,7 @@
     <string name="login_reset_password_mail_confirmation_notice_2">Fes clic a l\'enllaç per confirmar la nova contrasenya. Quan hagis anat a l\'enllaç que conté, fes clic a sota.</string>
     <string name="login_reset_password_mail_confirmation_notice">S\'ha enviat un correu de verificació a %1$s.</string>
     <string name="login_reset_password_mail_confirmation_title">Revisa la teva safata d\'entrada</string>
-    <string name="login_reset_password_error_not_found">Aquest correu no està vinculat amb cap compte</string>
+    <string name="login_reset_password_error_not_found">Aquesta adreça de correu electrònic no està vinculada a cap compte</string>
     <string name="login_reset_password_warning_submit">Continua</string>
     <string name="login_reset_password_warning_title">Atenció!</string>
     <string name="login_reset_password_password_hint">Nova contrasenya</string>
@@ -2120,8 +2120,8 @@
     <string name="create_spaces_invite_public_header_desc">Assegura\'t que les persones adequades tinguin accés a %s. Pots convidar-ne més després.</string>
     <string name="create_spaces_make_sure_access">Assegura\'t que les persones adequades tinguin accés a %s.</string>
     <string name="send_images_and_video_with_original_size">Envia multimèdia a mida real</string>
-    <string name="identity_server_consent_dialog_content_3">Per descobrir contactes existents, s\'ha d\'enviar informació de contacte (correus i números de telèfon) al servidor d\'identitat utilitzat. Es fa un \'hash\' de les dades abans d\'enviar-les per privacitat.</string>
-    <string name="identity_server_consent_dialog_title_2">Envia correus i números de telèfon a %s</string>
+    <string name="identity_server_consent_dialog_content_3">Per descobrir contactes existents, s\'ha d\'enviar informació de contacte (adreces de correu electrònic i números de telèfon) al servidor d\'identitat utilitzat. Es fa un \'hash\' de les dades abans d\'enviar-les per privacitat.</string>
+    <string name="identity_server_consent_dialog_title_2">Envia adreces de correu electrònic i números de telèfon a %s</string>
     <string name="settings_discovery_consent_notice_off_2">Els teus contactes són privats. Per descobrir els usuaris dels teus contactes, necessitem permís per enviar informació dels contactes al servidor d\'identitat que estiguis utilitzant.</string>
     <string name="send_feedback_space_info">Estàs utilitzant una versió beta dels espais. Els teus comentaris ajudaran a les properes versions. S\'anotaran la teva plataforma i nom d\'usuari per poder utilitzar els teus comentaris tant bé com puguem.</string>
     <string name="room_settings_room_access_restricted_description">Qualsevol a un espai amb aquesta sala podrà trobar-la i unir-s\'hi. Només els administradors d\'aquesta sala poden afegir-la a un espai.</string>
@@ -2150,7 +2150,7 @@
     <string name="location_activity_title_static_sharing">Comparteix ubicació</string>
     <string name="restart_the_application_to_apply_changes">Reinicia l\'aplicació per aplicar els canvis.</string>
     <string name="labs_enable_latex_maths">Activa format matemàtic amb LaTeX</string>
-    <string name="link_this_email_settings_link">Enllaça aquest correu amb el teu compte</string>
+    <string name="link_this_email_settings_link">Enllaça aquesta adreça de correu electrònic al teu compte</string>
     <string name="audio_message_file_size">(%1$s)</string>
     <string name="audio_message_reply_content">%1$s (%2$s)</string>
     <string name="error_audio_message_unable_to_play">No s\'ha pogut reproduir %1$s</string>
@@ -2438,7 +2438,7 @@
     <string name="settings_notification_other">Altres</string>
     <string name="settings_notification_mentions_and_keywords">Mencions i paraules clau</string>
     <string name="settings_notification_default">Notificacions per defecte</string>
-    <string name="settings_notification_emails_no_emails">Per rebre notificacions per correu, has d\'associar un correu electrònic amb el teu compte de Matrix</string>
+    <string name="settings_notification_emails_no_emails">Per rebre notificacions per correu, has d\'associar una adreça de correu electrònic al teu compte de Matrix</string>
     <string name="shortcut_disabled_reason_sign_out">S\'ha tancat la sessió!</string>
     <string name="shortcut_disabled_reason_room_left">La sala ha estat abandonada!</string>
     <string name="room_settings_none">Cap</string>
@@ -2710,4 +2710,32 @@
     <string name="device_manager_current_session_title">Sessió actual</string>
     <string name="labs_enable_new_app_layout_summary">Element simplificat amb pestanyes opcionals</string>
     <string name="labs_enable_new_app_layout_title">Activa la nova visualització</string>
+    <string name="device_manager_learn_more_sessions_inactive">Les sessions inactives son sessions que no has utilitzat durant un temps, però continuen rebent claus de xifrat.
+\n
+\nL\'eliminació de sessions inactives millora la seguretat i el rendiment, i et pot ajudar a identificar sessions noves sospitoses.</string>
+    <string name="device_manager_session_overview_signout">Tanca aquesta sessió</string>
+    <string name="command_description_devtools">Obre la pantalla d\'eines per a desenvolupadors</string>
+    <string name="device_manager_learn_more_session_rename">Els usuaris dels xats directes i sales al les quals t\'hagis unit poden veure la llista completa de les teves sessions.
+\n
+\nAixò els pot proporcionar més confiança de que realment parlen amb tu però, poden veure el nom de sessió que introdueixis.</string>
+    <string name="device_manager_learn_more_sessions_verified">Les sessions verificades son sessions en què has iniciat sessió amb les teves credencials i s\'han verificat utilitzant una frase de seguretat o mitjançant la verificació creuada.
+\n
+\nAixò vol dir que contenen claus de xifrat dels teus missatges anteriors i confirmen als altres usuaris amb qui parles, que aquestes sessions son realment teves.</string>
+    <string name="device_manager_learn_more_sessions_unverified">Les sessions no verificades son sessions en què has iniciat sessió amb les teves credencials però s\'hi ha fet una verificació creuada.
+\n
+\nAssegura\'t que reconeixes aquestes sessions especialment, ja que podrien representar un ús no autoritzat del teu compte.</string>
+    <string name="device_manager_learn_more_session_rename_title">Canvi de nom de sessions</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Sessions verificades</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Sessions no verificades</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Sessions inactives</string>
+    <string name="device_manager_session_rename_warning">Tingues en compte que els noms de sessió son visibles per les persones amb qui parlis.</string>
+    <string name="device_manager_session_rename_description">Els noms de sessió personalitzats et permeten identificar els teus dispositius més fàcilment.</string>
+    <string name="device_manager_session_rename_edit_hint">Nom de la sessió</string>
+    <string name="device_manager_session_rename">Canvia el nom de la sessió</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">No verificada · Sessió actual</string>
+    <string name="key_authenticity_not_guaranteed">L\'autenticitat d\'aquest missatge xifrat no ha pogut ser garantida en aquest dispositiu.</string>
+    <string name="command_description_table_flip">Afegeix (╯°□°)╯︵ ┻━┻ abans d\'un missatge de text</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Hi ha dispositius no verificats en aquesta sala, no podran desxifrat els missatges que enviïs.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">No enviïs mai missatges xifrats a sessions no verificades d\'aquesta sala.</string>
+    <string name="action_got_it">D\'acord</string>
 </resources>
\ No newline at end of file

From 27d2803244fe4255b7d3285f26a7fe19b46914b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com>
Date: Fri, 7 Oct 2022 05:08:04 +0000
Subject: [PATCH 043/400] Translated using Weblate (Estonian)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/et/
---
 fastlane/metadata/android/et/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/et/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/et/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/et/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/et/changelogs/40105000.txt b/fastlane/metadata/android/et/changelogs/40105000.txt
new file mode 100644
index 0000000000..2a031d88ac
--- /dev/null
+++ b/fastlane/metadata/android/et/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Põhilised muutused selles versioonis: ajastatud otsesõnumite saatmine on nüüd vaikimisi kasutusel.
+Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/et/changelogs/40105020.txt b/fastlane/metadata/android/et/changelogs/40105020.txt
new file mode 100644
index 0000000000..7a4a6ca253
--- /dev/null
+++ b/fastlane/metadata/android/et/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Põhilised muutused selles versioonis: rakenduse uus kujundus on nüüd vaikimisi kasutusel.
+Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases

From 915489ae1fde80552f4ec249a34a330e20a3c0f9 Mon Sep 17 00:00:00 2001
From: Vri <element@vrifox.cc>
Date: Fri, 7 Oct 2022 12:04:19 +0000
Subject: [PATCH 044/400] Translated using Weblate (German)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 .../src/main/res/values-de/strings.xml        | 225 ++++++++++--------
 1 file changed, 129 insertions(+), 96 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index 27f46160bc..e34effc082 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -40,9 +40,9 @@
     <string name="room_displayname_two_members">%1$s und %2$s</string>
     <string name="room_displayname_empty_room">Leerer Raum</string>
     <string name="notice_room_update">%s hat diesen Raum aufgewertet.</string>
-    <string name="event_status_sending_message">Sende eine Nachricht…</string>
+    <string name="event_status_sending_message">Sende eine Nachricht …</string>
     <string name="initial_sync_start_importing_account">Erste Synchronisation:
-\nImportiere Benutzerkonto…</string>
+\nImportiere Benutzerkonto …</string>
     <string name="initial_sync_start_importing_account_crypto">Erste Synchronisation:
 \nImportiere Kryptoschlüssel</string>
     <string name="initial_sync_start_importing_account_rooms">Erste Synchronisation:
@@ -300,11 +300,11 @@
     <string name="compression_opt_list_large">Groß</string>
     <string name="compression_opt_list_medium">Mittel</string>
     <string name="compression_opt_list_small">Klein</string>
-    <string name="call_connecting">Verbindungsaufbau…</string>
+    <string name="call_connecting">Verbindungsaufbau …</string>
     <string name="call_ended">Anruf beendet</string>
     <string name="incoming_video_call">Eingehender Videoanruf</string>
     <string name="incoming_voice_call">Eingehender Sprachanruf</string>
-    <string name="call_in_progress">Anruf aktiv…</string>
+    <string name="call_in_progress">Anruf aktiv …</string>
     <string name="call_error_user_not_responding">Die Gegenseite hat den Anruf nicht angenommen.</string>
     <string name="permissions_rationale_popup_title">Information</string>
     <string name="permissions_rationale_msg_record_audio">${app_name} benötigt die Berechtigung, auf dein Mikrofon zugreifen zu können, um (Sprach-)Anrufe tätigen zu können.</string>
@@ -329,9 +329,9 @@
     <string name="room_participants_action_mention">Erwähnen</string>
     <string name="room_participants_power_level_prompt">Du wirst diese Änderung nicht rückgängig machen können, da die Person dieselbe Berechtigungsstufe wie du erhalten wird.
 \nBist du sicher\?</string>
-    <string name="room_one_user_is_typing">%s schreibt…</string>
-    <string name="room_two_users_are_typing">%1$s und %2$s schreiben…</string>
-    <string name="room_many_users_are_typing">%1$s, %2$s und andere schreiben…</string>
+    <string name="room_one_user_is_typing">%s tippt …</string>
+    <string name="room_two_users_are_typing">%1$s und %2$s tippen …</string>
+    <string name="room_many_users_are_typing">%1$s, %2$s und andere tippen …</string>
     <string name="room_do_not_have_permission_to_post">Du bist nicht berechtigt, in diesen Raum zu schreiben.</string>
     <string name="ssl_trust">Vertrauen</string>
     <string name="ssl_do_not_trust">Nicht vertrauen</string>
@@ -431,7 +431,7 @@
     <string name="encryption_import_room_keys_summary">Schlüssel aus lokaler Datei importieren</string>
     <string name="encryption_import_import">Importieren</string>
     <string name="encryption_never_send_to_unverified_devices_title">Nur zu verifizierten Sitzungen verschlüsseln</string>
-    <string name="encryption_never_send_to_unverified_devices_summary">Von dieser Sitzung aus keine verschlüsselten Nachrichten an nicht-verifizierte Sitzungen senden.</string>
+    <string name="encryption_never_send_to_unverified_devices_summary">Niemals verschlüsselte Nachrichten von dieser Sitzung zu unverifizierten Sitzungen senden.</string>
     <string name="encryption_information_not_verified">Nicht verifiziert</string>
     <string name="encryption_information_verified">Verifiziert</string>
     <string name="encryption_information_verify">Bestätigen</string>
@@ -495,7 +495,7 @@
     <string name="notification_off">Aus</string>
     <string name="notification_noisy">Laut</string>
     <string name="encrypted_message">Verschlüsselte Nachricht</string>
-    <string name="loading">Lädt…</string>
+    <string name="loading">Lädt …</string>
     <string name="start_voice_call_prompt_msg">Sicher, dass du einen Sprachanruf starten möchtest\?</string>
     <string name="start_video_call_prompt_msg">Sicher, dass du einen Videoanruf starten möchtest\?</string>
     <string name="room_participants_ban_prompt_msg">Die Verbannung einer Person entfernt sie aus diesem Raum und hindert sie am erneuten Beitritt.</string>
@@ -628,7 +628,7 @@
     <string name="action_accept">Akzeptieren</string>
     <string name="auth_accept_policies">Bitte lese und akzeptiere die Richtlinien dieses Homeservers:</string>
     <string name="settings_troubleshoot_diagnostic_run_button_title">Tests ausführen</string>
-    <string name="settings_troubleshoot_diagnostic_running_status">Läuft… (%1$d von %2$d)</string>
+    <string name="settings_troubleshoot_diagnostic_running_status">Läuft … (%1$d von %2$d)</string>
     <string name="settings_troubleshoot_diagnostic_failure_status_with_quickfix">Einer oder mehrere Tests sind fehlgeschlagen. Versuche vorgeschlagene Lösung(en).</string>
     <string name="settings_troubleshoot_diagnostic_failure_status_no_quickfix">Einer oder mehrere Tests sind fehlgeschlagen. Bitte sende einen Fehlerbericht, damit dies untersucht werden kann.</string>
     <string name="settings_troubleshoot_test_system_settings_title">Systemeinstellungen.</string>
@@ -646,7 +646,7 @@
     <string name="settings_troubleshoot_test_device_settings_failed">Benachrichtigungen sind für diese Sitzung nicht aktiviert.
 \nBitte überprüfe die Einstellungen für ${app_name}.</string>
     <string name="settings_troubleshoot_test_device_settings_quickfix">Aktiviere</string>
-    <string name="settings_troubleshoot_test_play_services_failed">${app_name} benutzt Google-Play-Dienste um Push-Nachrichten zu übermitteln, doch scheinen sie nicht korrekt konfiguriert zu sein:
+    <string name="settings_troubleshoot_test_play_services_failed">${app_name} benutzt Google-Play-Dienste, um Push-Nachrichten zu übermitteln, allerdings scheint dies nicht korrekt konfiguriert zu sein:
 \n%1$s</string>
     <string name="settings_troubleshoot_test_play_services_quickfix">Repariere Play-Dienste</string>
     <string name="settings_troubleshoot_test_fcm_title">Firebase-Token</string>
@@ -679,7 +679,7 @@
     <string name="settings_troubleshoot_test_battery_failed">Wenn ein Benutzer ein abgestecktes Gerät mit ausgeschaltetem Bildschirm eine Weile nicht bewegt, wechselt es in den Bereitschaftsmodus. Dies hindert Apps daran, auf das Netzwerk zuzugreifen und verzögert die Ausführung von Aufgaben, Synchronisierungen und Standard-Alarmen.</string>
     <string name="settings_troubleshoot_test_battery_quickfix">Ignoriere Optimierungen</string>
     <string name="no_valid_google_play_services_apk">Keine validen Google-Play-Dienste gefunden. Benachrichtigungen könnten nicht richtig funktionieren.</string>
-    <string name="video_call_in_progress">Videogespräch aktiv…</string>
+    <string name="video_call_in_progress">Videogespräch aktiv …</string>
     <string name="title_activity_keys_backup_setup">Schlüsselsicherung</string>
     <string name="title_activity_keys_backup_restore">Schlüsselsicherung verwenden</string>
     <string name="action_skip">Überspringen</string>
@@ -692,7 +692,7 @@
     <string name="settings_noisy_notifications_preferences">Laute Benachrichtigungen einstellen</string>
     <string name="settings_call_notifications_preferences">Anrufbenachrichtigung einstellen</string>
     <string name="settings_silent_notifications_preferences">Stumme Benachrichtigungen einstellen</string>
-    <string name="settings_system_preferences_summary">LED-Farbe, Vibration, Ton usw. wählen</string>
+    <string name="settings_system_preferences_summary">Wähle LED-Farbe, Vibration, Ton …</string>
     <string name="notification_silent">Stumm</string>
     <string name="passphrase_empty_error_message">Bitte eine Passphrase eingeben</string>
     <string name="passphrase_passphrase_too_weak">Passphrase ist zu schwach</string>
@@ -703,7 +703,7 @@
     <string name="keys_backup_setup_step3_copy_button_title">Wiederherstellungsschlüssel speichern</string>
     <string name="keys_backup_setup_step3_save_button_title">Sichere als Datei</string>
     <string name="keys_backup_setup_step3_please_make_copy">Bitte mache eine Kopie</string>
-    <string name="keys_backup_setup_step3_share_intent_chooser_title">Wiederherstellungsschlüssel teilen mit…</string>
+    <string name="keys_backup_setup_step3_share_intent_chooser_title">Wiederherstellungsschlüssel teilen mit …</string>
     <string name="recovery_key">Wiederherstellungsschlüssel</string>
     <string name="unexpected_error">Unerwarteter Fehler</string>
     <string name="keys_backup_setup_skip_title">Bist du sicher\?</string>
@@ -712,7 +712,7 @@
     <string name="keys_backup_unlock_button">Historie entschlüsseln</string>
     <string name="keys_backup_settings_restore_backup_button">Von Sicherung wiederherstellen</string>
     <string name="keys_backup_settings_delete_backup_button">Sicherung löschen</string>
-    <string name="keys_backup_settings_deleting_backup">Lösche Sicherung…</string>
+    <string name="keys_backup_settings_deleting_backup">Lösche Sicherung …</string>
     <string name="keys_backup_settings_delete_confirm_title">Lösche Sicherung</string>
     <string name="settings_notification_by_event">Präferenz der Benachrichtigungen nach Ereignis</string>
     <string name="settings_troubleshoot_test_fcm_failed_too_many_registration">[%1$s]
@@ -728,7 +728,7 @@
 \nSichere deine Schlüssel, um sie nicht zu verlieren.</string>
     <string name="keys_backup_setup_step3_generating_key_status">Wiederherstellungsschlüssel aus Passphrase generieren. Dies kann mehrere Sekunden brauchen.</string>
     <string name="keys_backup_setup_skip_msg">Du verlierst möglicherweise den Zugang zu deinen Nachrichten, wenn du dich abmeldest oder das Gerät verlierst.</string>
-    <string name="keys_backup_restore_is_getting_backup_version">Rufe Backup-Version ab…</string>
+    <string name="keys_backup_restore_is_getting_backup_version">Rufe Sicherungsversion ab …</string>
     <string name="keys_backup_restore_with_passphrase">Nutze deine Wiederherstellungs-Passphrase, um deinen verschlüsselten Nachrichtenverlauf lesen zu können</string>
     <string name="keys_backup_restore_use_recovery_key">nutze deinen Wiederherstellungsschlüssel</string>
     <string name="keys_backup_restore_with_passphrase_helper_with_link">Wenn du deine Wiederherstellungspassphrase nicht weist, kannst du %s.</string>
@@ -762,7 +762,7 @@
     <string name="sign_out_bottom_sheet_warning_backing_up">Schlüssel-Sicherung wird durchgeführt. Wenn du dich jetzt abmeldest, gehen deine verschlüsselten Nachrichten verloren.</string>
     <string name="sign_out_bottom_sheet_warning_backup_not_active">Schlüsselsicherung sollte bei allen Sitzungen aktiviert sein, um den Verlust verschlüsselter Nachrichten zu verhindern.</string>
     <string name="sign_out_bottom_sheet_dont_want_secure_messages">Ich möchte meine verschlüsselten Nachrichten nicht</string>
-    <string name="sign_out_bottom_sheet_backing_up_keys">Sichere Schlüssel…</string>
+    <string name="sign_out_bottom_sheet_backing_up_keys">Sichere Schlüssel …</string>
     <string name="are_you_sure">Sicher\?</string>
     <string name="backup">Sicherung</string>
     <string name="sign_out_bottom_sheet_will_lose_secure_messages">Alle verschlüsselten Nachrichten gehen verloren, wenn Du dich abmeldest ohne die Schlüssel gesichert zu haben.</string>
@@ -790,18 +790,18 @@
     <string name="keys_backup_banner_recover_line2">Benutze Schlüsselsicherung</string>
     <string name="keys_backup_banner_update_line1">Neue sichere Schlüssel für Nachrichten</string>
     <string name="keys_backup_banner_update_line2">Verwalte Schlüsselsicherung</string>
-    <string name="keys_backup_banner_in_progress">Sichere Schlüssel…</string>
+    <string name="keys_backup_banner_in_progress">Sichere deine Schlüssel. Dies könnte einige Minuten dauern …</string>
     <string name="keys_backup_info_keys_all_backup_up">Alle Schlüssel sind gesichert</string>
     <plurals name="keys_backup_info_keys_backing_up">
-        <item quantity="one">Sichere %d Schlüssel…</item>
-        <item quantity="other">Sichere %d Schlüssel…</item>
+        <item quantity="one">Sichere einen Schlüssel …</item>
+        <item quantity="other">Sichere %d Schlüssel …</item>
     </plurals>
     <string name="keys_backup_info_title_version">Version</string>
     <string name="keys_backup_info_title_algorithm">Algorithmus</string>
     <string name="keys_backup_info_title_signature">Signatur</string>
-    <string name="keys_backup_restoring_computing_key_waiting_message">Berechne Wiederherstellungsschlüssel…</string>
-    <string name="keys_backup_restoring_downloading_backup_waiting_message">Lade Schlüssel herunter…</string>
-    <string name="keys_backup_restoring_importing_keys_waiting_message">Importiere Schlüssel…</string>
+    <string name="keys_backup_restoring_computing_key_waiting_message">Berechne Wiederherstellungsschlüssel …</string>
+    <string name="keys_backup_restoring_downloading_backup_waiting_message">Lade Schlüssel herunter …</string>
+    <string name="keys_backup_restoring_importing_keys_waiting_message">Importiere Schlüssel …</string>
     <string name="action_ignore">Ignorieren</string>
     <string name="auth_login_sso">Mit Single-Sign-On anmelden</string>
     <string name="settings_send_message_with_enter">Nachricht mit Eingabetaste senden</string>
@@ -870,7 +870,7 @@
     <string name="error_no_network">Kein Netzwerk. Bitte überprüfe deine Internetverbindung.</string>
     <string name="action_change">Ändern</string>
     <string name="change_room_directory_network">Netzwerk wechseln</string>
-    <string name="please_wait">Bitte warten…</string>
+    <string name="please_wait">Bitte warten …</string>
     <string name="room_preview_no_preview">Für diesen Raum kann keine Vorschau angezeigt werden</string>
     <string name="fab_menu_create_room">Räume</string>
     <string name="fab_menu_create_chat">Direktnachrichten</string>
@@ -902,9 +902,9 @@
     <string name="send_suggestion_report_placeholder">Beschreibe hier deine Anmerkung</string>
     <string name="settings_labs_show_hidden_events_in_timeline">Versteckte Ereignisse in der Zeitleiste anzeigen</string>
     <string name="bottom_action_people_x">Direktnachrichten</string>
-    <string name="send_file_step_idle">Warten…</string>
-    <string name="send_file_step_encrypting_thumbnail">Miniaturbild wird verschlüsselt…</string>
-    <string name="send_file_step_encrypting_file">Datei wird verschlüsselt…</string>
+    <string name="send_file_step_idle">Warten …</string>
+    <string name="send_file_step_encrypting_thumbnail">Vorschaubild wird verschlüsselt …</string>
+    <string name="send_file_step_encrypting_file">Verschlüssle Datei …</string>
     <string name="edited_suffix">(bearbeitet)</string>
     <string name="message_edits">Nachrichtenbearbeitung</string>
     <string name="no_message_edits_found">Keine Änderungen gefunden</string>
@@ -912,7 +912,7 @@
     <string name="room_filtering_footer_create_new_direct_message">Sende eine neue Direktnachricht</string>
     <string name="room_filtering_footer_open_room_directory">Das Raumverzeichnis anzeigen</string>
     <string name="link_copied_to_clipboard">Link in die Zwischenablage kopiert</string>
-    <string name="creating_direct_room">Raum erstellen…</string>
+    <string name="creating_direct_room">Erstelle Raum …</string>
     <string name="message_view_edit_history">Bearbeitungsverlauf anzeigen</string>
     <string name="import_e2e_keys_from_file">E2E-Schlüssel aus der Datei \"%1$s\" importieren.</string>
     <string name="send_suggestion_sent">Vielen Dank, der Vorschlag wurde erfolgreich gesendet</string>
@@ -947,7 +947,7 @@
     <string name="add_identity_server">Identitäts-Server konfigurieren</string>
     <string name="change_identity_server">Identitäts-Server ändern</string>
     <string name="settings_discovery_emails_title">Auffindbare E-Mail-Adressen</string>
-    <string name="settings_discovery_no_mails">Erkennungsoptionen werden angezeigt, sobald du eine E-Mail hinzugefügt hast.</string>
+    <string name="settings_discovery_no_mails">Entdeckungsoptionen werden angezeigt, sobald du eine E-Mail-Adresse hinzugefügt hast.</string>
     <string name="settings_discovery_enter_identity_server">Gib eine Identitäts-Server-Adresse ein</string>
     <string name="settings_discovery_bad_identity_server">Konnte keine Verbindung zum Homeserver herstellen</string>
     <string name="login_error_no_homeserver_found">Dies ist keine Adresse eines Matrixservers</string>
@@ -997,13 +997,13 @@
     <string name="error_terms_not_accepted">Bitte erneut versuchen, nachdem du die Nutzungsbedingungen deines Heim-Servers akzeptiert hast.</string>
     <string name="room_widget_permission_webview_shared_info_title">Bei Benutzung könnten Cookies gesetzt werden und es könnten Daten mit %s geteilt werden:</string>
     <string name="room_widget_permission_shared_info_title">Bei Benutzung könnten Daten mit %s geteilt werden:</string>
-    <string name="settings_discovery_no_msisdn">Optionen zum Finden werden erscheinen, sobald du eine Telefonnummer hinzugefügt hast.</string>
-    <string name="settings_discovery_confirm_mail">Wir haben dir eine Bestätigungsmail an %s gesendet. Prüfe dein Postfach und klicke auf den Bestätigungslink</string>
+    <string name="settings_discovery_no_msisdn">Entdeckungsoptionen werden angezeigt, sobald du eine Telefonnummer hinzugefügt hast.</string>
+    <string name="settings_discovery_confirm_mail">Wir haben eine E-Mail an %s gesendet. Prüfe deine E-Mails und klicke auf den Bestätigungslink</string>
     <string name="error_network_timeout">Es sieht aus, als würde der Server zu viel Zeit benötigen, um zu antworten. Der Grund kann eine schlechte Verbindung oder ein Fehler mit dem Server sein. Bitte versuche es später erneut.</string>
     <string name="send_attachment">Anhang senden</string>
     <string name="a11y_open_drawer">Navigationsmenü öffnen</string>
     <string name="a11y_create_menu_open">Raumerstellungsmenü öffnen</string>
-    <string name="a11y_create_menu_close">Schließe das Raumerstellungsmenü…</string>
+    <string name="a11y_create_menu_close">Schließe das Raumerstellungsmenü …</string>
     <string name="a11y_create_direct_message">Erstelle eine neue Direktnachricht</string>
     <string name="a11y_create_room">Erstelle einen neuen Raum</string>
     <string name="a11y_close_keys_backup_banner">Schließe Key-Backup-Einblendung</string>
@@ -1022,7 +1022,7 @@
     <string name="attachment_type_sticker">Sticker</string>
     <string name="report_content_spam">Es ist Spam</string>
     <string name="report_content_inappropriate">Es ist unangebracht</string>
-    <string name="report_content_custom">Benutzerdefinierte Meldung…</string>
+    <string name="report_content_custom">Benutzerdefinierte Meldung …</string>
     <string name="report_content_custom_title">Diesen Inhalt melden</string>
     <string name="report_content_custom_hint">Meldegrund</string>
     <string name="report_content_custom_submit">MELDEN</string>
@@ -1067,7 +1067,7 @@
     <string name="settings_discovery_disconnect_with_bound_pid">Du teilst deine E-Mail-Adressen oder Telefonnummern momentan auf dem Identitäts-Server %1$s. Du wirst dich erneut mit %2$s verbinden müssen, um mit dem Teilen aufzuhören.</string>
     <string name="settings_agree_to_terms">Stimme den Nutzungsbedingungen des Identitäts-Servers (%s) zu, um per E-Mail-Adresse oder Telefonnummer auffindbar zu sein zu können.</string>
     <string name="error_handling_incoming_share">Zu teilende Daten nicht verarbeitbar</string>
-    <string name="login_splash_text3">Erweitere und individualisiere dein Benutzererlebnis</string>
+    <string name="login_splash_text3">Erweitere und personalisiere deine Erfahrung</string>
     <string name="login_connect_to">Mit %1$s verbinden</string>
     <string name="login_connect_to_modular">Mit Element Matrix Services verbinden</string>
     <string name="login_connect_to_a_custom_server">Mit einem anderen Server verbinden</string>
@@ -1082,14 +1082,14 @@
     <string name="login_registration_not_supported">Die Anwendung kann kein neues Benutzerkonto auf diesem Server erstellen.
 \n
 \nMöchtest du dich mit einer Web-Anwendung anmelden\?</string>
-    <string name="login_login_with_email_error">Diese E-Mail-Adresse ist mit keinem Benutzerkonto verknüpft.</string>
+    <string name="login_login_with_email_error">Diese E-Mail-Adresse ist mit keinem Konto verknüpft.</string>
     <string name="login_reset_password_on">Passwort auf %1$s zurücksetzen</string>
     <string name="login_reset_password_email_hint">E-Mail</string>
     <string name="login_reset_password_password_hint">Neues Passwort</string>
     <string name="login_reset_password_warning_title">Achtung!</string>
     <string name="login_reset_password_warning_content">Eine Änderung deines Passworts wird alle Ende-zu-Ende-Schlüssel zurücksetzen. Dein verschlüsselter Verlauf wird dadurch unlesbar. Richte die Schlüsselsicherung ein oder exportiere deine Raumschlüssel aus einer anderen Sitzung, bevor du dein Passwort zurücksetzt.</string>
     <string name="login_reset_password_warning_submit">Fortfahren</string>
-    <string name="login_reset_password_error_not_found">Diese E-Mail-Adresse ist mit keinem Benutzerkonto verknüpft</string>
+    <string name="login_reset_password_error_not_found">Diese E-Mail-Adresse ist mit keinem Konto verknüpft</string>
     <string name="login_reset_password_mail_confirmation_title">Prüfe deinen Posteingang</string>
     <string name="login_reset_password_mail_confirmation_notice">Eine Bestätigungsmail wurde an %1$s versendet.</string>
     <string name="login_reset_password_mail_confirmation_notice_2">Klicke auf den Link um dein neues Passwort zu bestätigen. Sobald du dem enthaltenen Link gefolgt bist, klicke unten.</string>
@@ -1133,7 +1133,7 @@
     <string name="login_reset_password_submit">Weiter</string>
     <string name="login_reset_password_success_notice_2">Du wurdest von allen Sitzungen abgemeldet und erhältst keine Push-Benachrichtigungen mehr. Um Benachrichtigungen wieder zu aktivieren, melde dich auf jedem Gerät erneut an.</string>
     <string name="login_reset_password_cancel_confirmation_title">Warnung</string>
-    <string name="login_set_email_notice">Lege eine E-Mail-Adresse fest, um dein Konto wiederherzustellen. Später kannst du optional zulassen, dass Personen dich anhand dieser E-Mail-Adresse entdecken.</string>
+    <string name="login_set_email_notice">Lege eine E-Mail-Adresse fest, um dein Konto wiederherzustellen. Später kannst du optional zulassen, dass Personen dich anhand dieser E-Mail-Adresse entdecken können.</string>
     <string name="login_set_email_submit">Weiter</string>
     <string name="login_set_msisdn_title">Lege Telefonnummer fest</string>
     <string name="login_set_msisdn_notice">Lege eine Telefonnummer fest, damit Personen dich anhand dieser entdecken können.</string>
@@ -1149,8 +1149,8 @@
     <string name="login_a11y_captcha_container">Bitte löse das Captcha</string>
     <string name="login_error_outdated_homeserver_title">Veralteter Homeserver</string>
     <plurals name="login_error_limit_exceeded_retry_after">
-        <item quantity="one">Es wurden zu viele Anfragen gesendet. Versuche es erneut in %1$d Sekunde…</item>
-        <item quantity="other">Es wurden zu viele Anfragen gesendet. Versuche es erneut in %1$d Sekunden…</item>
+        <item quantity="one">Es wurden zu viele Anfragen gesendet. Versuche es erneut in %1$d Sekunde …</item>
+        <item quantity="other">Es wurden zu viele Anfragen gesendet. Versuche es erneut in %1$d Sekunden …</item>
     </plurals>
     <string name="seen_by">Gesehen von</string>
     <string name="signed_out_title">Du bist abgemeldet</string>
@@ -1181,7 +1181,7 @@
 \nBitte zuerst die Daten löschen und dann erneut anmelden.</string>
     <string name="permalink_malformed">matrix.to-Link fehlerhaft</string>
     <string name="bug_report_error_too_short">Die Beschreibung ist zu kurz</string>
-    <string name="notification_initial_sync">Initiale Synchronisierung…</string>
+    <string name="notification_initial_sync">Initiale Synchronisierung …</string>
     <string name="settings_advanced_settings">Erweiterte Einstellungen</string>
     <string name="settings_developer_mode">Entwicklermodus</string>
     <string name="settings_developer_mode_summary">Der Entwicklermodus aktiviert versteckte Funktionen und kann die Anwendung weniger stabil machen. Nur für Entwickler!</string>
@@ -1192,7 +1192,7 @@
     <string name="settings">Einstellungen</string>
     <string name="devices_current_device">Aktuelle Sitzung</string>
     <string name="devices_other_devices">Andere Sitzungen</string>
-    <string name="autocomplete_limited_results">Zeigt nur die ersten Ergebnisse, gib mehr Buchstaben ein…</string>
+    <string name="autocomplete_limited_results">Zeigt nur die ersten Ergebnisse, gib weitere Zeichen ein …</string>
     <string name="settings_developer_mode_fail_fast_title">Ausfallsicher</string>
     <string name="settings_developer_mode_fail_fast_summary">${app_name} kann häufiger abstürzen, wenn ein unerwarteter Fehler auftritt</string>
     <string name="command_description_shrug">Stellt einer Klartextnachricht ¯\\_(ツ)_/¯ voran</string>
@@ -1213,9 +1213,9 @@
     <string name="sent_an_image">Bild.</string>
     <string name="sent_an_audio_file">Audio</string>
     <string name="sent_a_file">Datei</string>
-    <string name="verification_request_waiting">Warten…</string>
+    <string name="verification_request_waiting">Warte …</string>
     <string name="verification_request_other_cancelled">%s brach ab</string>
-    <string name="verification_request_you_cancelled">Du hast abgebrochen</string>
+    <string name="verification_request_you_cancelled">Du brachst ab</string>
     <string name="verification_request_other_accepted">%s hat akzeptiert</string>
     <string name="verification_request_you_accepted">Du hast akzeptiert</string>
     <string name="verification_sent">Verifizierung gesendet</string>
@@ -1228,7 +1228,7 @@
     <string name="verification_no_scan_emoji_title">Verifizieren via Emoji-Vergleich</string>
     <string name="verification_verify_user">%s verifizieren</string>
     <string name="verification_verified_user">%s verifiziert</string>
-    <string name="verification_request_waiting_for">Warte auf %s…</string>
+    <string name="verification_request_waiting_for">Warte auf %s …</string>
     <string name="room_profile_not_encrypted_subtitle">Nachrichten in diesem Raum sind nicht Ende-zu-Ende-verschlüsselt.</string>
     <string name="room_profile_encrypted_subtitle">Nachrichten in diesem Raum sind Ende-zu-Ende-verschlüsselt.
 \n
@@ -1244,7 +1244,7 @@
     </plurals>
     <string name="room_profile_section_more_uploads">Hochgeladene Dateien</string>
     <string name="room_profile_section_more_leave">Raum verlassen</string>
-    <string name="room_profile_leaving_room">Verlasse den Raum…</string>
+    <string name="room_profile_leaving_room">Verlasse den Raum …</string>
     <string name="room_member_power_level_admins">Administratoren</string>
     <string name="room_member_power_level_moderators">Moderatoren</string>
     <string name="room_member_power_level_custom">Benutzerdefiniert</string>
@@ -1262,7 +1262,7 @@
     <string name="command_description_rainbow_emote">Sendet das angegebene Emote in Regenbogenfarben</string>
     <string name="settings_category_timeline">Zeitleiste</string>
     <string name="settings_category_composer">Nachrichteneditor</string>
-    <string name="room_settings_enable_encryption">Ende-zu-Ende-Verschlüsselung aktivieren…</string>
+    <string name="room_settings_enable_encryption">Aktiviere Ende-zu-Ende-Verschlüsselung …</string>
     <string name="room_settings_enable_encryption_dialog_title">Verschlüsselung aktivieren\?</string>
     <string name="room_settings_enable_encryption_dialog_content">Nach der Aktivierung kann die Verschlüsselung für den Raum nicht deaktiviert werden. Nachrichten können nicht vom Server gesehen werden, nur von den Teilnehmenden des Raums. Möglicherweise funktionieren danach einige Bots und Bridges nicht mehr ordnungsgemäß.</string>
     <string name="room_settings_enable_encryption_dialog_submit">Verschlüsselung aktivieren</string>
@@ -1284,7 +1284,7 @@
     <string name="settings_active_sessions_list">Aktive Sitzungen</string>
     <string name="settings_active_sessions_show_all">Alle Sitzungen anzeigen</string>
     <string name="settings_active_sessions_manage">Sitzungen verwalten</string>
-    <string name="settings_active_sessions_signout_device">Diese Sitzung abmelden</string>
+    <string name="settings_active_sessions_signout_device">Von dieser Sitzung abmelden</string>
     <string name="settings_failed_to_get_crypto_device_info">Keine kryptografischen Informationen verfügbar</string>
     <string name="settings_active_sessions_verified_device_desc">Diese Sitzung ist für sichere Kommunikation vertrauenswürdig, da du sie überprüft hast:</string>
     <string name="settings_active_sessions_unverified_device_desc">Verifiziere diese Sitzung, um sie als vertrauenswürdig zu markieren, und gewähren ihr Zugriff auf verschlüsselte Nachrichten. Wenn du dich nicht bei dieser Sitzung angemeldet hast, ist dein Konto möglicherweise gefährdet:</string>
@@ -1316,7 +1316,7 @@
     <string name="verification_cannot_access_other_session">Nutze eine Wiederherstellungsmethode</string>
     <string name="verification_use_passphrase">Wenn du auf keine existierende Sitzung zugreifen kannst</string>
     <string name="enter_secret_storage_invalid">Kann keine Geheimnisse im Speicher finden</string>
-    <string name="message_action_item_redact">Entfernen…</string>
+    <string name="message_action_item_redact">Entferne …</string>
     <string name="share_confirm_room">Möchtest du diesen Anhang an %1$s senden\?</string>
     <plurals name="send_images_with_original_size">
         <item quantity="one">Sende Bild in Originalgröße</item>
@@ -1385,13 +1385,13 @@
     <string name="encryption_enabled">Verschlüsselung aktiviert</string>
     <string name="encryption_enabled_tile_description">Nachrichten in diesem Raum sind Ende-zu-Ende-verschlüsselt. Erfahre mehr und verifiziere Benutzer in deren Profil.</string>
     <string name="encryption_unknown_algorithm_tile_description">Die Verschlüsselung in diesem Raum wird nicht unterstützt</string>
-    <string name="qr_code_scanned_verif_waiting">Warte auf %s…</string>
+    <string name="qr_code_scanned_verif_waiting">Warte auf %s …</string>
     <string name="settings_troubleshoot_title">Fehlerbehebung</string>
     <string name="room_created_summary_item">%s hat den Raum erstellt und konfiguriert.</string>
     <string name="qr_code_scanned_self_verif_notice">Fast geschafft! Zeigt das andere Gerät ein Häkchen an\?</string>
-    <string name="qr_code_scanned_verif_waiting_notice">Fast geschafft! Warte auf Bestätigung…</string>
+    <string name="qr_code_scanned_verif_waiting_notice">Fast geschafft! Warte auf Bestätigung …</string>
     <string name="settings_messages_in_e2e_one_to_one">Verschlüsselte Direktnachrichten</string>
-    <string name="room_message_placeholder">Nachricht…</string>
+    <string name="room_message_placeholder">Nachricht …</string>
     <string name="security_prompt_text">Verifiziere dich und andere, um eure Unterhaltungen zu schützen</string>
     <string name="bootstrap_enter_recovery">Gib zum Fortfahren deinen %s ein</string>
     <string name="use_file">Datei benutzen</string>
@@ -1411,7 +1411,7 @@
     <string name="bootstrap_migration_backup_recovery_key">Schlüsselbackup-Wiederherstellungsschlüssel</string>
     <string name="settings_security_prevent_screenshots_title">Bildschirmfotos der Anwendung verhindern</string>
     <string name="settings_security_prevent_screenshots_summary">Das Aktivieren dieser Einstellung setzt FLAG_SECURE in allen Aktivitäten. Starte die Anwendung neu, damit die Änderung wirksam wird.</string>
-    <string name="change_password_summary">Neues Benutzerpasswort festlegen…</string>
+    <string name="change_password_summary">Lege ein neues Kontopasswort fest …</string>
     <string name="use_other_session_content_description">Nutze die neueste Version von ${app_name} auf deinen anderen Geräten, ${app_name} Web, ${app_name} Desktop, ${app_name} iOS, ${app_name} für Android oder eine andere Matrix-Anwendung, die Quersignierung unterstützt</string>
     <string name="app_desktop_web">${app_name} Web
 \n${app_name} Desktop</string>
@@ -1445,7 +1445,7 @@
     <string name="notification_ticker_text_group">%1$s: %2$s %3$s</string>
     <string name="add_members_to_room">Mitglieder hinzufügen</string>
     <string name="invite_users_to_room_action_invite">EINLADEN</string>
-    <string name="inviting_users_to_room">Benutzer werden eingeladen…</string>
+    <string name="inviting_users_to_room">Lade Benutzer ein …</string>
     <string name="invite_users_to_room_title">Personen einladen</string>
     <string name="invitation_sent_to_one_user">Einladung gesendet an %1$s</string>
     <string name="invitations_sent_to_two_users">Einladungen gesendet an %1$s und %2$s</string>
@@ -1519,7 +1519,7 @@
     <string name="settings_show_redacted">Gelöschte Nachrichten zeigen</string>
     <string name="settings_show_redacted_summary">Zeigt einen Platzhalter für gelöschte Nachrichten an</string>
     <string name="labs_show_unread_notifications_as_tab">Dedizierten Tab für ungelesene Nachrichten zur Hauptansicht hinzufügen</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Wir haben dir eine Bestätigungsmail an %s gesendet. Bitte prüfe deine E-Mails und klicke auf den Bestätigungslink</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Wir haben eine E-Mail an %s gesendet. Bitte prüfe deine E-Mails und klicke auf den Bestätigungslink</string>
     <string name="settings_text_message_sent_wrong_code">Der Verifizierungscode ist nicht korrekt.</string>
     <string name="uploads_media_title">MEDIEN</string>
     <string name="uploads_media_no_result">Es gibt in diesem Raum keine Medien</string>
@@ -1553,14 +1553,14 @@
     <string name="error_saving_media_file">Konnte Mediendatei nicht speichern</string>
     <string name="choose_locale_current_locale_title">Aktuelle Sprache</string>
     <string name="choose_locale_other_locales_title">Andere verfügbare Sprachen</string>
-    <string name="choose_locale_loading_locales">Lade verfügbare Sprachen…</string>
+    <string name="choose_locale_loading_locales">Lade verfügbare Sprachen …</string>
     <string name="open_terms_of">Öffne AGBs von %s</string>
     <string name="disconnect_identity_server_dialog_content">Verbindung zu Identitäts-Server %s trennen\?</string>
     <string name="identity_server_error_outdated_identity_server">Dieser Identitäts-Server ist veraltet. ${app_name} unterstützt nur API V2.</string>
     <string name="identity_server_error_outdated_home_server">Diese Operation ist nicht möglich. Der Homeserver ist veraltet.</string>
     <string name="identity_server_error_no_identity_server_configured">Bitte konfiguriere zuerst einen Identitäts-Server.</string>
     <string name="identity_server_error_terms_not_signed">Bitte akzeptiere zuerst die AGB des Identitäts-Servers in den Einstellungen.</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Deiner Privatsphäre wegen unterstützt ${app_name} nur das Senden gehashter E-Mail-Adressen und Telefonnummern.</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Deiner Privatsphäre wegen unterstützt ${app_name} nur das Senden von E-Mail-Adressen und Telefonnummern als Streuwert (Hash).</string>
     <string name="identity_server_error_binding_error">Die Assoziierung ist fehlgeschlagen.</string>
     <string name="identity_server_error_no_current_binding_error">Für diese Kennung gibt es aktuell keine Zuordnung.</string>
     <string name="identity_server_set_default_notice">Dein Heim-Server (%1$s) schlägt %2$s als Identitäts-Server vor</string>
@@ -1602,7 +1602,7 @@
     <string name="disclaimer_negative_button">VERSTANDEN</string>
     <string name="disclaimer_positive_button">MEHR ERFAHREN</string>
     <string name="save_recovery_key_chooser_hint">Speichere Wiederherstellungsschlüssel in</string>
-    <string name="loading_contact_book">Ermittle deine Kontakte…</string>
+    <string name="loading_contact_book">Ermittle deine Kontakte …</string>
     <string name="empty_contact_book">Deine Kontaktliste ist leer</string>
     <string name="contacts_book_title">Kontaktliste</string>
     <string name="three_pid_revoke_invite_dialog_title">Einladung zurücknehmen</string>
@@ -1654,12 +1654,12 @@
     <string name="auth_msisdn_already_defined">Diese Telefonnummer ist bereits registriert.</string>
     <string name="settings_phone_number_empty">Deinem Konto wurde keine Telefonnummer hinzugefügt</string>
     <string name="settings_emails">E-Mail-Adressen</string>
-    <string name="settings_emails_empty">Deinem Konto wurde keine E-Mail hinzugefügt</string>
+    <string name="settings_emails_empty">Deinem Konto wurde keine E-Mail-Adresse hinzugefügt</string>
     <string name="settings_phone_numbers">Telefonnummern</string>
     <string name="settings_remove_three_pid_confirmation_content">%s entfernen\?</string>
     <string name="error_threepid_auth_failed">Stelle sicher, dass du auf den Link in der E-Mail geklickt hast, die wir dir gesendet haben.</string>
     <string name="settings_emails_and_phone_numbers_title">E-Mail und Telefon</string>
-    <string name="settings_emails_and_phone_numbers_summary">Verwalte E-Mails und Telefonnummern, die mit deinem Matrix-Konto verknüpft sind</string>
+    <string name="settings_emails_and_phone_numbers_summary">Verwalte E-Mail-Adressen und Telefonnummern, die mit deinem Matrix-Konto verknüpft sind</string>
     <string name="settings_text_message_sent_hint">Code</string>
     <string name="login_msisdn_notice">Verwende das internationale Format (Telefonnummer muss mit \'+\' beginnen)</string>
     <string name="confirm_your_identity_quad_s">Bestätige deine Identität, indem du dieses Login verifizierst, um Zugriff auf verschlüsselte Nachrichten zu erhalten.</string>
@@ -1790,7 +1790,7 @@
     <string name="this_is_the_beginning_of_room_no_name">Das ist der Anfang dieser Konversation.</string>
     <string name="this_is_the_beginning_of_room">Das ist der Anfang von %s.</string>
     <string name="room_settings_enable_encryption_no_permission">Du hast nicht die nötigen Berechtigungen, um die Verschlüsselung in diesem Raum zu aktivieren.</string>
-    <string name="create_room_in_progress">Erstelle Raum…</string>
+    <string name="create_room_in_progress">Erstelle Raum …</string>
     <string name="create_room_alias_invalid">Manche Zeichen sind nicht zulässig</string>
     <string name="create_room_alias_empty">Bitte gib eine Raumadresse an</string>
     <string name="create_room_alias_already_in_use">Diese Adresse ist bereits vergeben</string>
@@ -1926,9 +1926,9 @@
     <string name="room_settings_room_version_title">Raum-Version</string>
     <string name="dialog_edit_hint">Neuer Wert</string>
     <string name="initial_sync_start_downloading">Erste Synchronisation:
-\nLade Daten herunter…</string>
+\nLade Daten herunter …</string>
     <string name="initial_sync_start_server_computing">Erste Synchronisation:
-\nWarte auf Serverantwort…</string>
+\nWarte auf Antwort vom Server …</string>
     <string name="event_status_a11y_sent">Gesendet</string>
     <string name="settings_category_room_directory">Raumverzeichnis</string>
     <string name="action_switch">Wechseln</string>
@@ -1988,13 +1988,13 @@
     <string name="space_type_public_desc">Offen für Jeden. Am Besten für Communities</string>
     <string name="create_spaces_private_teammates">Ein privater Space für meine Teamkameraden und mich</string>
     <string name="create_spaces_me_and_teammates">Meine Teamkameraden und ich</string>
-    <string name="create_spaces_organise_rooms">Ein privater Space um deine Räume zu organisieren</string>
+    <string name="create_spaces_organise_rooms">Ein privater Space zum Organisieren deiner Räume</string>
     <string name="create_spaces_join_info_help">Um einem bereits existierenden Space beizutreten, benötigst du eine Einladung.</string>
     <string name="your_private_space">Dein privater Space</string>
     <string name="your_public_space">Dein öffentlicher Space</string>
     <string name="command_description_join_space">Betrete einen Space mit der angegebenen ID</string>
     <string name="create_space_topic_hint">Beschreibung</string>
-    <string name="create_spaces_loading_message">Erzeuge Space …</string>
+    <string name="create_spaces_loading_message">Erstelle Space …</string>
     <string name="create_spaces_default_public_random_room_name">Ohne Thema</string>
     <string name="create_spaces_default_public_room_name">Allgemein</string>
     <string name="activity_create_space_title">Einen Space erstellen</string>
@@ -2028,7 +2028,7 @@
     </plurals>
     <string name="error_file_too_big_simple">Die Datei ist zu groß.</string>
     <string name="send_file_step_compressing_video">Video wird komprimiert (%d%%)</string>
-    <string name="send_file_step_compressing_image">Bild wird komprimiert…</string>
+    <string name="send_file_step_compressing_image">Komprimiere Bild …</string>
     <string name="use_as_default_and_do_not_ask_again">Als Standard festsetzen und nicht mehr fragen</string>
     <string name="option_always_ask">Jedes Mal fragen</string>
     <string name="directory_add_a_new_server_prompt">Gib den Namen eines neuen Servers ein, den du erkunden möchtest.</string>
@@ -2129,7 +2129,7 @@
         <item quantity="one">Verpasster Sprachanruf</item>
         <item quantity="other">%d verpasste Sprachanrufe</item>
     </plurals>
-    <string name="hs_client_url">Heim-Server API URL</string>
+    <string name="hs_client_url">Heim-Server-API-Adresse</string>
     <string name="denied_permission_voice_message">Um Sprachnachrichten zu senden, erlaube bitte Zugriff aufs Mikrofon.</string>
     <string name="denied_permission_camera">Um fortzufahren, erlaube bitte in den Systemeinstellungen Zugriff auf die Kamera.</string>
     <string name="denied_permission_generic">Für diese Aktion fehlen einige Berechtigungen, bitte erlaube diese in den Systemeinstellungen.</string>
@@ -2140,7 +2140,7 @@
     <string name="other_spaces_or_rooms_you_might_not_know">Andere Spaces oder Räume die du kennst</string>
     <string name="space_you_know_that_contains_this_room">Spaces mit diesem Raum und dir als Mitglied</string>
     <string name="room_settings_mention_and_keyword_only">Nur Erwähnungen und Schlüsselwörter</string>
-    <string name="call_remove_jitsi_widget_progress">Auflegen…</string>
+    <string name="call_remove_jitsi_widget_progress">Auflegen …</string>
     <string name="audio_call_with_participant">Sprachanruf mit %s</string>
     <string name="video_call_with_participant">Videoanruf mit %s</string>
     <string name="all_rooms_youre_in_will_be_shown_in_home">Alle beigetretenen Räume werden auf der Startseite angezeigt.</string>
@@ -2163,7 +2163,7 @@
     <string name="call_ended_invite_timeout_title">Nicht erreicht</string>
     <string name="call_ended_user_busy_description">Die angerufene Person ist beschäftigt.</string>
     <string name="call_ended_user_busy_title">Person beschäftigt</string>
-    <string name="call_ringing">Klingeln…</string>
+    <string name="call_ringing">Klingeln …</string>
     <string name="spaces">Spaces</string>
     <string name="call_tile_video_missed">Verpasster Videoanruf</string>
     <string name="call_tile_voice_missed">Verpasster Sprachanruf</string>
@@ -2184,7 +2184,7 @@
     <string name="space_leave_prompt_msg_with_name">Willst du %s wirklich verlassen\?</string>
     <string name="invite_by_username_or_mail">Mit Benutzername oder E-Mail einladen</string>
     <string name="command_description_add_to_space">Zum ausgewählten Space hinzufügen</string>
-    <string name="create_space_in_progress">Erstelle Space…</string>
+    <string name="create_space_in_progress">Erstelle Space …</string>
     <string name="settings_developer_mode_show_info_on_screen_summary">Hilfreiche Informationen zur Fehlersuche anzeigen</string>
     <string name="settings_developer_mode_show_info_on_screen_title">Debug-Info anzeigen</string>
     <string name="does_not_look_like_valid_email">Das schaut nicht nach einer gültigen E-Mail-Adresse aus</string>
@@ -2193,7 +2193,7 @@
     <string name="room_settings_space_access_title">Zugriff</string>
     <string name="room_settings_access_rules_pref_dialog_title">Wer hat Zugriff\?</string>
     <string name="settings_notification_emails_enable_for_email">Benachrichtigungen per Email für %s aktivieren</string>
-    <string name="settings_notification_emails_no_emails">Um Benachrichtigungen per E-Mail zu empfangen, musst du einen E-Mail-Adresse hinzufügen</string>
+    <string name="settings_notification_emails_no_emails">Um Benachrichtigungen per E-Mail zu empfangen, musst du eine E-Mail-Adresse hinzufügen</string>
     <string name="settings_notification_emails_category">Emailbenachrichtigungen</string>
     <string name="room_permissions_upgrade_the_space">Space upgraden</string>
     <string name="room_permissions_change_space_name">Namen vom Space ändern</string>
@@ -2247,7 +2247,7 @@
     <string name="login_error_homeserver_from_url_not_found">Es konnte kein Heim-Server mit der Adresse %s gefunden werden. Bitte überprüfe die Adresse oder wähle den Heim-Server manuell.</string>
     <string name="space_add_space_to_any_space_you_manage">Untergeordneten Space hinzufügen.</string>
     <string name="identity_server_consent_dialog_content_question">Bist du dir wirklich sicher, dass du diese Informationen senden willst\?</string>
-    <string name="identity_server_consent_dialog_title_2">E-Mail-Adressen und Telefonnummern an %s senden</string>
+    <string name="identity_server_consent_dialog_title_2">E-Mail-Adressen und Telefonnummern an %s übermitteln</string>
     <string name="action_not_now">Nicht jetzt</string>
     <string name="notification_listening_for_notifications">Auf Benachrichtigungen warten</string>
     <string name="legals_third_party_notices">Externe Bibliotheken</string>
@@ -2294,13 +2294,13 @@
     <string name="labs_auto_report_uisi_desc">Das System sendet automatisch Protokolle, wenn ein Fehler bei der Entschlüsselung auftritt</string>
     <string name="labs_auto_report_uisi">Entschlüsselungsfehler automatisch melden.</string>
     <string name="discovery_section">Auffindbarkeit (%s)</string>
-    <string name="discovery_invite">Per E-Mail einladen, finde deine Kontakte und mehr…</string>
+    <string name="discovery_invite">Lade per E-Mail ein, finde deine Kontakte und mehr …</string>
     <string name="finish_setting_up_discovery">Schließe die Konfiguration des Auffindbarkeitsdienstes ab.</string>
     <string name="create_space_identity_server_info_none">Du verwendest derzeit keinen Identitäts-Server. Um Team-Mitglieder einzuladen und für sie auffindbar zu sein, konfiguriere zunächst einen.</string>
     <string name="login_splash_already_have_account">Ich habe bereits ein Konto</string>
     <string name="ftue_auth_carousel_encrypted_title">Sichere Kommunikation.</string>
     <string name="ftue_auth_carousel_secure_title">Besitze deine Unterhaltungen.</string>
-    <string name="identity_server_consent_dialog_content_3">Um bestehende Kontakte ermitteln zu können, musst du Kontaktinformationen (E-Mail-Adressen und Telefonnummern) an deinen Identitäts-Server übermitteln. Wir verschlüsseln deine Daten vor der Übermittlung, um den Datenschutz gewährleisten zu können.</string>
+    <string name="identity_server_consent_dialog_content_3">Um bestehende Kontakte ermitteln zu können, musst du Kontaktinformationen (E-Mail-Adressen und Telefonnummern) an deinen Identitäts-Server übermitteln. Deine Daten werden als Streuwert (Hash) übermittelt, um den Datenschutz zu gewährleisten.</string>
     <string name="settings_discovery_consent_notice_off_2">Deine Kontakte sind privat. Um unter deinen Kontakten Matrix-Nutzer finden zu können, benötigen wir deine Erlaubnis, Kontaktinformationen an deinen Identitäts-Server zu übermitteln.</string>
     <string name="legals_no_policy_provided">Dieser Server stellt keine Richtlinie bereit.</string>
     <string name="legals_identity_server_title">Richtlinie deines Identitäts-Servers</string>
@@ -2399,13 +2399,13 @@
         <item quantity="other">%d Server-ACLs geändert</item>
     </plurals>
     <string name="location_share_live_stop">Beenden</string>
-    <string name="location_share_live_enabled">Live-Standort aktiviert</string>
+    <string name="location_share_live_enabled">Echtzeit-Standort aktiviert</string>
     <string name="a11y_location_share_option_pinned_icon">Standort teilen</string>
     <string name="location_share_option_pinned">Diesen Standort teilen</string>
     <string name="location_share_option_user_current">Meinen Standort teilen</string>
     <string name="a11y_location_share_option_user_current_icon">Meinen Standort teilen</string>
-    <string name="a11y_location_share_option_user_live_icon">Live-Standort teilen</string>
-    <string name="location_share_option_user_live">Live-Standort teilen</string>
+    <string name="a11y_location_share_option_user_live_icon">Echtzeit-Standort freigeben</string>
+    <string name="location_share_option_user_live">Echtzeit-Standort teilen</string>
     <string name="threads_notice_migration_title">Threads nähern sich der Beta 🎉</string>
     <string name="action_disable">Deaktivieren</string>
     <string name="beta">BETA</string>
@@ -2415,14 +2415,14 @@
     <string name="threads_beta_enable_notice_title">Threads Beta</string>
     <string name="call_start_screen_sharing">Bildschirm teilen</string>
     <string name="action_try_it_out">Probiere es aus</string>
-    <string name="location_share_live_until">Live bis %1$s</string>
+    <string name="location_share_live_until">Echtzeit bis %1$s</string>
     <string name="unifiedpush_getdistributors_dialog_title">Wähle Deine Benachrichtigungsmethode</string>
-    <string name="labs_enable_live_location_summary">Vorläufige Implementierung: Standorte bleiben im Nachrichtenverlauf von Räumen erhalten</string>
+    <string name="labs_enable_live_location_summary">Vorläufige Implementierung: Standorte verbleiben im Raumverlauf</string>
     <string name="push_gateway_item_profile_tag">Profil-Tag:</string>
     <string name="time_unit_hour_short">h</string>
-    <string name="live_location_labs_promotion_switch_title">Standortfreigabe aktivieren</string>
+    <string name="live_location_labs_promotion_switch_title">Aktiviere Standortfreigabe</string>
     <string name="live_location_labs_promotion_description">Bitte beachte: Dies ist eine experimentelle Funktion, die eine temporäre Implementierung nutzt. Das bedeutet, dass du deinen Standortverlauf nicht löschen kannst und erfahrene Nutzer ihn sehen können, selbst wenn du deinen Live-Standort nicht mehr mit diesem Raum teilst.</string>
-    <string name="live_location_labs_promotion_title">Live-Standortfreigabe</string>
+    <string name="live_location_labs_promotion_title">Echtzeit-Standortfreigabe</string>
     <string name="settings_troubleshoot_test_current_gateway">Aktuelles Gateway: %s</string>
     <string name="settings_troubleshoot_test_current_gateway_title">Gateway</string>
     <string name="settings_troubleshoot_test_current_endpoint_failed">Kann den Endpunkt nicht finden.</string>
@@ -2443,17 +2443,17 @@
     <string name="screen_sharing_notification_description">Bildschirmfreigabe ist in Arbeit</string>
     <string name="screen_sharing_notification_title">${app_name} Bildschirmfreigabe</string>
     <string name="live_location_bottom_sheet_last_updated_at">Aktualisiert vor %1$s</string>
-    <string name="labs_enable_live_location">Aktiviere Live-Standortfreigabe</string>
+    <string name="labs_enable_live_location">Aktiviere Echtzeit-Standortfreigabe</string>
     <string name="live_location_sharing_notification_description">Standortfreigabe ist in Arbeit</string>
-    <string name="live_location_sharing_notification_title">${app_name} Live-Standort</string>
+    <string name="live_location_sharing_notification_title">${app_name} Echtzeit-Standort</string>
     <string name="location_share_live_remaining_time">%1$s übrig</string>
-    <string name="location_share_live_view">Live-Standort anzeigen</string>
-    <string name="location_share_live_ended">Live-Standort beendet</string>
-    <string name="location_share_live_started">Live-Standort laden…</string>
+    <string name="location_share_live_view">Echtzeit-Standort anzeigen</string>
+    <string name="location_share_live_ended">Echtzeit-Standort beendet</string>
+    <string name="location_share_live_started">Lade Echtzeit-Standort …</string>
     <string name="location_share_live_select_duration_option_3">8 Stunden</string>
     <string name="location_share_live_select_duration_option_2">1 Stunde</string>
     <string name="location_share_live_select_duration_option_1">15 Minuten</string>
-    <string name="location_share_live_select_duration_title">Teile Deinen Live-Standort für</string>
+    <string name="location_share_live_select_duration_title">Gebe deinen Echtzeit-Standort frei für</string>
     <string name="a11y_location_share_locate_button">Auf aktuellen Standort zoomen</string>
     <string name="a11y_location_share_pin_on_map">Pin des ausgewählten Ortes auf der Karte</string>
     <string name="audio_message_file_size">(%1$s)</string>
@@ -2507,7 +2507,7 @@
     <string name="settings_security_pin_code_use_biometrics_error">Die biometrische Authentifizierung konnte nicht aktiviert werden.</string>
     <string name="auth_biometric_key_invalidated_message">Die biometrische Authentifizierung wurde deaktiviert, weil kürzlich eine neue biometrische Authentifizierungsmethode hinzugefügt wurde. Du kannst sie in den Einstellungen wieder aktivieren.</string>
     <string name="error_forbidden_digits_only_username">Der Heim-Server akzeptiert keine Benutzernamen, die nur aus Ziffern bestehen.</string>
-    <string name="sent_live_location">teilten ihren Live-Standort</string>
+    <string name="sent_live_location">Teilte den eigenen Echtzeit-Standort</string>
     <string name="ftue_personalize_skip_this_step">Schritt überspringen</string>
     <string name="ftue_personalize_submit">Speichern und fortfahren</string>
     <string name="ftue_personalize_complete_subtitle">Öffne die Einstellungen jederzeit um dein Profil zu aktualisieren</string>
@@ -2545,7 +2545,7 @@
     <string name="permalink_unsupported_groups">Kann Link nicht öffnen: Communities wurden durch Spaces ersetzt</string>
     <string name="labs_enable_msc3061_share_history">MSC3061: Raumschlüssel für vorherige Nachrichten teilen</string>
     <string name="labs_enable_msc3061_share_history_desc">Beim Einladen in einen Raum mit sichtbarem Verlauf wird der verschlüsselte Verlauf sichtbar sein.</string>
-    <string name="live_location_description">Live-Standort</string>
+    <string name="live_location_description">Echtzeit-Standort</string>
     <plurals name="room_removed_messages">
         <item quantity="one">%d Nachricht gelöscht</item>
         <item quantity="other">%d Nachrichten gelöscht</item>
@@ -2553,10 +2553,10 @@
     <string name="labs_enable_element_call_permission_shortcuts">Keine Element-Call-Berechtigungsabfragen</string>
     <string name="labs_enable_element_call_permission_shortcuts_summary">Bestätige automatisch Element-Call-Widgets und erlaube Kamera- und Mikrofonzugriff</string>
     <string name="create_room_action_go">Los</string>
-    <string name="ftue_auth_create_account_edit_server_selection">ändern</string>
-    <string name="ftue_auth_create_account_sso_section_header">oder</string>
-    <string name="ftue_auth_sign_in_choose_server_header">Der Ort, an dem deine Gespräche stattfinden</string>
-    <string name="ftue_auth_create_account_choose_server_header">Das zukünftige Zuhause für deine Gespräche</string>
+    <string name="ftue_auth_create_account_edit_server_selection">Bearbeiten</string>
+    <string name="ftue_auth_create_account_sso_section_header">Oder</string>
+    <string name="ftue_auth_sign_in_choose_server_header">Der zukünftige Ort deiner Gespräche</string>
+    <string name="ftue_auth_create_account_choose_server_header">Der zukünftige Ort deiner Gespräche</string>
     <string name="font_size_use_system">Systemstandard nutzen</string>
     <string name="font_size_section_auto">Automatisch festlegen</string>
     <string name="font_size_title">Schriftgröße wählen</string>
@@ -2568,7 +2568,7 @@
     <string name="ftue_auth_login_username_entry">Nutzername / E-Mail-Adresse / Telefonnummer</string>
     <string name="ftue_auth_create_account_title">Erstelle dein Konto</string>
     <string name="ftue_auth_choose_server_entry_hint">Server-URL</string>
-    <string name="ftue_auth_choose_server_subtitle">Wie lautet die Adresse deines Servers\? Das wird eine Art Zuhause für deine Daten</string>
+    <string name="ftue_auth_choose_server_subtitle">Wie lautet die Adresse deines Servers\? Dies ist eine Art Zuhause für all deine Daten</string>
     <string name="ftue_auth_choose_server_sign_in_subtitle">Wie lautet die Adresse deines Servers\?</string>
     <string name="ftue_auth_create_account_password_entry_footer">Muss 8 oder mehr Zeichen umfassen</string>
     <string name="ftue_auth_choose_server_title">Wähle deinen Server</string>
@@ -2601,9 +2601,9 @@
     <string name="crosssigning_cannot_verify_this_session_desc">Du wirst deinen verschlüsselten Nachrichtenverlauf nicht abrufen können. Um neu zu beginnen, setze deine Sicherung und Verifizierungsschlüssel zurück.</string>
     <string name="crosssigning_cannot_verify_this_session">Verifizierung dieses Gerätes nicht möglich</string>
     <string name="updating_your_data">Aktualisiere deine Daten …</string>
-    <string name="live_location_share_location_item_share">Standort teilen</string>
-    <string name="live_location_not_enough_permission_dialog_description">Du musst die Berechtigung erhalten, um deinen Live-Standort mit diesem Raum zu teilen.</string>
-    <string name="live_location_not_enough_permission_dialog_title">Dir fehlt die Berechtigung, deinen Live-Standort teilen zu dürfen</string>
+    <string name="live_location_share_location_item_share">Standort freigeben</string>
+    <string name="live_location_not_enough_permission_dialog_description">Du benötigst die entsprechenden Berechtigungen, um deinen Echtzeit-Standort in diesem Raum freizugeben.</string>
+    <string name="live_location_not_enough_permission_dialog_title">Dir fehlt die Berechtigung, deinen Echtzeit-Standort freigeben zu dürfen</string>
     <string name="ftue_auth_password_reset_confirmation">Passwort zurückgesetzt</string>
     <string name="ftue_auth_phone_confirmation_resend_code">Code erneut schicken</string>
     <string name="ftue_auth_phone_confirmation_subtitle">Ein Code wurde an %s gesendet</string>
@@ -2709,4 +2709,37 @@
     <string name="device_manager_view_details">Details anzeigen</string>
     <string name="device_manager_verification_status_detail_other_session_unverified">Für bestmögliche Sicherheit und Zuverlässigkeit verifiziere diese Sitzungen oder melde dich von ihr ab.</string>
     <string name="device_manager_other_sessions_recommendation_description_verified">Für die bestmögliche Sicherheit, melde dich von allen Sitzungen ab, die du nicht erkennst oder nicht mehr benutzt.</string>
+    <string name="device_manager_learn_more_session_rename">Andere Nutzer in Direktnachrichten und Räumen, in denen du dich befindest, können eine vollständige Liste deiner Sitzungen einsehen.
+\n
+\nDies gibt ihnen die Sicherheit, dass sie auch wirklich mit dir kommunizieren. Allerdings bedeutet es auch, dass sie die Sitzungsnamen sehen können, die du hier angibst.</string>
+    <string name="device_manager_learn_more_sessions_verified">Verifizierte Sitzungen wurden mit deinen Daten angemeldet und anschließend mit deiner Sicherheitspassphrase oder durch Quersignierung verifiziert.
+\n
+\nDies bedeutet, dass sie die Verschlüsselungs-Schlüssel für deine bisherigen Nachrichten besitzen und anderen Nutzern bestätigen können, dass diese Sitzungen tatsächlich von dir stammen.</string>
+    <string name="device_manager_learn_more_session_rename_title">Sitzungen umbenennen</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Verifizierte Sitzungen</string>
+    <string name="device_manager_learn_more_sessions_unverified">Nicht verifizierte Sitzungen sind jene, die angemeldet, aber nicht quer signiert sind.
+\n
+\nDu solltest besonders sicherstellen, dass du diese Sitzungen erkennst, da sie auf eine nicht autorisierte Nutzung deines Kontos hindeuten könnten.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Nicht verifizierte Sitzungen</string>
+    <string name="device_manager_learn_more_sessions_inactive">Inaktive Sitzungen sind jene, die du einige Zeit nicht mehr verwendet hast, die aber weiterhin Verschlüsselungs-Schlüssel erhalten.
+\n
+\nDas Löschen von inaktiven Sitzungen verbessert Sicherheit und Leistung, und hilft dir zu erkennen, ob eine neue Sitzung verdächtig ist.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Inaktive Sitzungen</string>
+    <string name="device_manager_session_rename_warning">Sei dir bitte bewusst, dass Sitzungsnamen auch für Personen, mit denen du kommunizierst, sichtbar sind.</string>
+    <string name="device_manager_session_rename_description">Individuelle Sitzungsnamen können dir helfen, deine Geräte einfacher zu erkennen.</string>
+    <string name="device_manager_session_rename_edit_hint">Sitzungsname</string>
+    <string name="device_manager_session_rename">Sitzung umbenennen</string>
+    <string name="device_manager_session_overview_signout">Von dieser Sitzung abmelden</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Nicht verifiziert · Deine aktuelle Sitzung</string>
+    <string name="tooltip_attachment_voice_broadcast">Beginne eine Sprachübertragung</string>
+    <string name="key_authenticity_not_guaranteed">Die Echtheit dieser verschlüsselten Nachricht kann auf diesem Gerät nicht garantiert werden.</string>
+    <string name="settings_security_incognito_keyboard_summary">Tastatur auffordern, keine personalisierten Daten, z. B. den Tippverlauf und Wörterbücher, basierend auf deinen Konversationen zu aktualisieren. Beachte, dass einige Tastaturen diese Einstellung ignorieren werden.</string>
+    <string name="settings_security_incognito_keyboard_title">Inkognito-Tastatur</string>
+    <string name="command_description_table_flip">Stellt (╯°□°)╯︵ ┻━┻ einer Klartextnachricht voran</string>
+    <string name="attachment_type_voice_broadcast">Sprachübertragung</string>
+    <string name="command_description_devtools">Öffne die Entwicklungswerkzeuge</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Du hast in den Sicherheitseinstellungen aktiviert, dass Verschlüsselung in allen Räumen ausschließlich für verifizierte Sitzungen erlaubt ist.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Es befinden sich nicht verifizierte Geräte in diesem Raum. Sie werden deine Nachrichten nicht entschlüsseln können.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Niemals verschlüsselte Nachrichten zu unverifizierten Sitzungen in diesem Raum senden.</string>
+    <string name="action_got_it">Verstanden</string>
 </resources>
\ No newline at end of file

From 8bff9cc2935b03016d2da934933a514e6583a6bf Mon Sep 17 00:00:00 2001
From: random <dictionary@tutamail.com>
Date: Thu, 6 Oct 2022 12:39:13 +0000
Subject: [PATCH 045/400] Translated using Weblate (Italian)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/it/
---
 fastlane/metadata/android/it-IT/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/it-IT/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/it-IT/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/it-IT/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/it-IT/changelogs/40105000.txt b/fastlane/metadata/android/it-IT/changelogs/40105000.txt
new file mode 100644
index 0000000000..9132fea7b9
--- /dev/null
+++ b/fastlane/metadata/android/it-IT/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Modifiche principali in questa versione: messaggi diretti differiti attivati in modo predefinito.
+Cronologia completa: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/it-IT/changelogs/40105020.txt b/fastlane/metadata/android/it-IT/changelogs/40105020.txt
new file mode 100644
index 0000000000..accee9e36d
--- /dev/null
+++ b/fastlane/metadata/android/it-IT/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Modifiche principali in questa versione: nuova interfaccia dell'app attivata in modo predefinito!
+Cronologia completa: https://github.com/vector-im/element-android/releases

From f522c4ba062ba62e76b8619c787a4a57742908a0 Mon Sep 17 00:00:00 2001
From: Nui Harime <harime.nui@yandex.ru>
Date: Fri, 7 Oct 2022 21:53:07 +0000
Subject: [PATCH 046/400] Translated using Weblate (Russian)

Currently translated at 96.7% (2365 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ru/
---
 .../src/main/res/values-ru/strings.xml        | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml
index c8eee49d96..409cea7b13 100644
--- a/library/ui-strings/src/main/res/values-ru/strings.xml
+++ b/library/ui-strings/src/main/res/values-ru/strings.xml
@@ -446,8 +446,8 @@
     <string name="encryption_import_import">Импорт</string>
     <string name="encryption_never_send_to_unverified_devices_title">Шифровать только для проверенных сессий</string>
     <string name="encryption_never_send_to_unverified_devices_summary">Не отправлять зашифрованные сообщения непроверенным сессиям с этой сессии.</string>
-    <string name="encryption_information_not_verified">Не проверено</string>
-    <string name="encryption_information_verified">Проверено</string>
+    <string name="encryption_information_not_verified">Не заверено</string>
+    <string name="encryption_information_verified">Заверено</string>
     <string name="encryption_information_verify">Подтвердить</string>
     <string name="encryption_information_verify_device_warning">Чтобы убедиться, что этой сессии можно доверять, обратитесь к ее владельцу, используя другие способы (например, лично или по телефону), и спросите, соответствует ли ключ, который он видит в настройках для этой сессии:</string>
     <string name="encryption_information_verify_device_warning2">Если они не совпадают, безопасность вашего общения может быть поставлена под угрозу.</string>
@@ -1430,7 +1430,7 @@
     <string name="verification_code_notice">Сравните код с тем, который отображается на экране другого пользователя.</string>
     <string name="verification_conclusion_ok_notice">Сообщения от этого пользователя зашифрованы сквозным шифрованием и не смогут быть прочитаны третьими лицами.</string>
     <string name="verification_conclusion_ok_self_notice">Ваша новая сессия подтверждена. У нее есть доступ к вашим зашифрованным сообщениям, а другие пользователи увидят его как доверенное.</string>
-    <string name="encryption_information_cross_signing_state">Перекрестная подпись</string>
+    <string name="encryption_information_cross_signing_state">Перекрёстная подпись</string>
     <string name="encryption_information_dg_xsigning_complete">Перекрестная подпись включена
 \nЛичные ключи хранятся на устройстве.</string>
     <string name="encryption_information_dg_xsigning_trusted">Перекрестная подпись включена
@@ -2721,7 +2721,7 @@
     <string name="home_empty_space_no_rooms_title">%s
 \nвыглядит слегка пустовато.</string>
     <string name="onboarding_new_app_layout_button_try">Попробовать</string>
-    <string name="device_manager_session_details_description">Сведения о приложении, устройстве и активности.</string>
+    <string name="device_manager_session_details_description">Информация о приложении, устройстве и активности.</string>
     <string name="device_manager_verification_status_detail_current_session_unverified">Подтвердите текущую сессию для более безопасного обмена сообщениями.</string>
     <string name="space_list_empty_title">Пока нет пространств.</string>
     <string name="device_manager_other_sessions_recommendation_description_unverified">Подтвердите свои сессии для более безопасного обмена сообщениями или выйдите из тех, которые более не признаёте или не используете.</string>
@@ -2742,4 +2742,15 @@
     </plurals>
     <string name="device_manager_filter_option_unverified">Незаверенные</string>
     <string name="device_manager_filter_bottom_sheet_title">Фильтр</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Незаверенная · Текущая сессия</string>
+    <string name="device_manager_session_rename">Переименовать сессию</string>
+    <string name="device_manager_session_rename_edit_hint">Название сессии</string>
+    <string name="device_manager_other_sessions_recommendation_title_verified">Заверенные</string>
+    <string name="device_manager_session_overview_signout">Выйти из этой сессии</string>
+    <string name="device_manager_other_sessions_recommendation_title_inactive">Неактивные</string>
+    <string name="device_manager_other_sessions_recommendation_title_unverified">Незаверенные</string>
+    <string name="device_manager_session_rename_warning">Пожалуйста, имейте в виду, что названия сессий также видны людям, с которыми вы общаетесь.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Заверенные сессии</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Незаверенные сессии</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Неактивные сессии</string>
 </resources>
\ No newline at end of file

From 453b4070ad334e351b11a0c36bb698ce12feb963 Mon Sep 17 00:00:00 2001
From: Ihor Hordiichuk <igor_ck@outlook.com>
Date: Wed, 5 Oct 2022 18:20:13 +0000
Subject: [PATCH 047/400] Translated using Weblate (Ukrainian)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/uk/
---
 fastlane/metadata/android/uk/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/uk/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/uk/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/uk/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/uk/changelogs/40105000.txt b/fastlane/metadata/android/uk/changelogs/40105000.txt
new file mode 100644
index 0000000000..ab8964b99a
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Основні зміни в цій версії: відкладені особисті повідомлення увімкнено типово.
+Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.2.0
diff --git a/fastlane/metadata/android/uk/changelogs/40105020.txt b/fastlane/metadata/android/uk/changelogs/40105020.txt
new file mode 100644
index 0000000000..90a62209dc
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Основні зміни в цій версії: новий вигляд застосунку типово увімкнено!
+Журнал усіх змін: https://github.com/vector-im/element-android/releases

From bfb80d20837ce35641e5dfd9cc1a26708d9e665e Mon Sep 17 00:00:00 2001
From: Glandos <bugs-github@antipoul.fr>
Date: Fri, 7 Oct 2022 12:04:03 +0000
Subject: [PATCH 048/400] Translated using Weblate (French)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/fr/
---
 fastlane/metadata/android/fr-FR/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/fr-FR/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/fr-FR/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/fr-FR/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/fr-FR/changelogs/40105000.txt b/fastlane/metadata/android/fr-FR/changelogs/40105000.txt
new file mode 100644
index 0000000000..707cc20fd3
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Principaux changements pour cette version : Création des conversations privées différée activée par défaut.
+Intégralité des changements : https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/fr-FR/changelogs/40105020.txt b/fastlane/metadata/android/fr-FR/changelogs/40105020.txt
new file mode 100644
index 0000000000..fec750141c
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Principaux changements pour cette version : Nouvelle disposition de l’application activée par défaut !
+Intégralité des changements : https://github.com/vector-im/element-android/releases

From 06c8d24c9515e93cbabda06465a12c22499f3f64 Mon Sep 17 00:00:00 2001
From: Ihor Hordiichuk <igor_ck@outlook.com>
Date: Wed, 5 Oct 2022 21:02:29 +0000
Subject: [PATCH 049/400] Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/
---
 .../src/main/res/values-uk/strings.xml        | 59 +++++++++++++++----
 1 file changed, 46 insertions(+), 13 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-uk/strings.xml b/library/ui-strings/src/main/res/values-uk/strings.xml
index c4f1658f6b..9b5cff2140 100644
--- a/library/ui-strings/src/main/res/values-uk/strings.xml
+++ b/library/ui-strings/src/main/res/values-uk/strings.xml
@@ -675,8 +675,8 @@
     <string name="uploads_files_no_result">У цій кімнаті немає файлів</string>
     <string name="uploads_files_subtitle">%1$s о %2$s</string>
     <string name="settings_phone_number_empty">Жодного номера телефону не додано до вашого облікового запису</string>
-    <string name="settings_emails_empty">У ваш обліковий запис не додано жодної електронної адреси</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Для вашої приватності ${app_name} підтримує лише надсилання хешованих електронних адрес користувачів та номера телефону.</string>
+    <string name="settings_emails_empty">У ваш обліковий запис не додано жодної адреси електронної адреси</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Для вашої приватності ${app_name} підтримує лише надсилання хешованих електронних адрес користувачів та номерів телефону.</string>
     <string name="settings_discovery_consent_notice_on">Ви погодилися надіслати електронні адреси та телефонні номери на цей сервер ідентифікації для виявлення інших користувачів із ваших контактів.</string>
     <string name="settings_discovery_consent_title">Надіслати електронні адреси та номери телефонів</string>
     <string name="settings_emails_and_phone_numbers_summary">Керування електронними адресами та номерами телефонів, пов’язаними з вашим обліковим записом Matrix</string>
@@ -1814,8 +1814,8 @@
     <string name="error_network_timeout">Схоже, що відповідь сервера надто тривала, це може бути спричинено або поганим з’єднанням, або помилкою сервера. Повторіть спробу через деякий час.</string>
     <string name="error_terms_not_accepted">Повторіть спробу, коли погодитесь з умовами свого домашнього сервера.</string>
     <string name="settings_text_message_sent">Текстове повідомлення надіслано на %s. Введіть код підтвердження, який воно містить.</string>
-    <string name="settings_discovery_confirm_mail">Ми надіслали вам електронний лист для підтвердження на %s, відкрийте свою е-пошту та клацніть на посилання для підтвердження</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Ми надіслали вам електронний лист для підтвердження на %s, відкрийте свою е-пошту та клацніть на посилання для підтвердження</string>
+    <string name="settings_discovery_confirm_mail">Ми надіслали вам електронний лист на %s, відкрийте свою е-пошту та клацніть на посилання для підтвердження</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Ми надіслали вам електронний лист на %s, відкрийте свою е-пошту та клацніть на посилання для підтвердження</string>
     <string name="sent_a_poll">Опитування</string>
     <string name="sent_a_file">Файл</string>
     <string name="sent_a_voice_message">Голосове</string>
@@ -1877,7 +1877,7 @@
     <string name="login_signup_submit">Далі</string>
     <string name="login_reset_password_submit">Далі</string>
     <string name="login_reset_password_on">Скинути пароль на %1$s</string>
-    <string name="login_login_with_email_error">Ця е-пошта не пов\'язана з жодним обліковим записом.</string>
+    <string name="login_login_with_email_error">Ця адреса е-пошти не пов\'язана з жодним обліковим записом.</string>
     <string name="login_registration_disabled">Перепрошуємо, сервер не приймає нових облікових записів.</string>
     <string name="login_sso_error_message">Сталася помилка під час завантаження сторінки: %1$s (%2$d)</string>
     <string name="login_server_url_form_common_notice">Введіть адресу сервера, який ви хочете використовувати</string>
@@ -1895,7 +1895,7 @@
     <string name="login_connect_to_modular">Під\'єднатися до Element Matrix Services</string>
     <string name="login_connect_to">Під\'єднатися до %1$s</string>
     <string name="login_reset_password_mail_confirmation_title">Перевірти вхідні</string>
-    <string name="login_reset_password_error_not_found">Ця е-пошта не пов\'язана з жодним обліковим записом</string>
+    <string name="login_reset_password_error_not_found">Ця адреса е-пошти не пов\'язана з жодним обліковим записом</string>
     <string name="login_reset_password_warning_submit">Продовжити</string>
     <string name="login_continue">Продовжити</string>
     <string name="login_social_sso">єдиний вхід</string>
@@ -1948,7 +1948,7 @@
     <string name="finish_setting_up_discovery">Завершити налаштування виявності.</string>
     <string name="settings_discovery_msisdn_title">Виявні номери телефону</string>
     <string name="settings_discovery_no_msisdn">Опції виявності з\'являться після додавання номера телефону.</string>
-    <string name="settings_discovery_no_mails">Опції виявності з\'являться після додавання е-пошти.</string>
+    <string name="settings_discovery_no_mails">Опції виявності з\'являться після додавання адреси е-пошти.</string>
     <string name="open_discovery_settings">Відкрити налаштування виявності</string>
     <string name="user_directory_search_hint_2">Пошук за іменем, ID або е-поштою</string>
     <string name="create_new_space">Створити новий простір</string>
@@ -1956,7 +1956,7 @@
     <string name="room_settings_space_access_title">Доступ до простору</string>
     <string name="room_settings_access_rules_pref_dialog_title">Хто може мати доступ\?</string>
     <string name="settings_notification_emails_enable_for_email">Увімкнути сповіщення е-поштою для %s</string>
-    <string name="settings_notification_emails_no_emails">Щоб отримувати сповіщення е-поштою, пов’яжіть її зі своїм обліковим записом Matrix</string>
+    <string name="settings_notification_emails_no_emails">Щоб отримувати сповіщення е-поштою, пов’яжіть її адресу зі своїм обліковим записом Matrix</string>
     <string name="settings_notification_emails_category">Сповіщення е-поштою</string>
     <string name="room_permissions_upgrade_the_space">Оновити простір</string>
     <string name="room_permissions_change_space_name">Змінити назву простору</string>
@@ -2197,7 +2197,7 @@
     <string name="create_spaces_room_private_header">Над чим ви працюєте\?</string>
     <string name="create_spaces_invite_public_header">Хто учасники вашої команди\?</string>
     <string name="create_space_error_empty_field_space_name">Назвіть його, щоб продовжити.</string>
-    <string name="create_spaces_organise_rooms">Приватний простір для організації ваших кімнат</string>
+    <string name="create_spaces_organise_rooms">Приватний простір для впорядкування ваших кімнат</string>
     <string name="room_error_not_found">Не вдалося знайти таку кімнату. Переконайтеся, що вона існує.</string>
     <string name="settings_security_pin_code_notifications_title">Показувати вміст у сповіщеннях</string>
     <plurals name="wrong_pin_message_remaining_attempts">
@@ -2233,7 +2233,7 @@
         <item quantity="many">Надіслано забагато запитів. Спробуйте знову за %1$d секунд…</item>
         <item quantity="other">Надіслано забагато запитів. Спробуйте знову за %1$d секунд…</item>
     </plurals>
-    <string name="login_set_email_notice">Вкажіть е-пошту для відновлення облікового запису. Згодом ви зможете дозволити знайомим знаходити вас за е-поштою.</string>
+    <string name="login_set_email_notice">Вкажіть адресу е-пошти для відновлення облікового запису. Згодом ви зможете дозволити знайомим знаходити вас за цією адресою.</string>
     <string name="login_reset_password_success_notice_2">Ви вийшли з усіх сеансів і більше не отримуватимете сповіщень. Щоб отримувати сповіщення знову, ввійдіть на кожному пристрої заново.</string>
     <string name="login_reset_password_warning_content">Зміна пароля скине всі ключі наскрізного шифрування всіх ваших сеансів, унеможливлюючи читання історії шифрованих чатів. Налаштуйте резервне копіювання ключів чи експортуйте ключі кімнат з іншого сеансу, перш ніж скинути пароль.</string>
     <string name="login_registration_not_supported">Застосунку не вдалося створити обліковий запис на цьому домашньому сервері.
@@ -2242,7 +2242,7 @@
     <string name="login_mode_not_supported">Застосунку не вдається зайти до вашого домашнього сервера. Домашній сервер підтримує такі типи входу: %1$s.
 \n
 \nБажаєте зайти через вебклієнт\?</string>
-    <string name="identity_server_consent_dialog_content_3">Щоб знайти наявні контакти, надішліть дані контактів (е-пошти й номери телефонів) серверу ідентифікації. Ми хешуємо ваші дані перед надсиланням для приватності.</string>
+    <string name="identity_server_consent_dialog_content_3">Щоб знайти наявні контакти, надішліть дані контактів (адреси е-пошти й номери телефонів) серверу ідентифікації. Ми хешуємо ваші дані перед надсиланням для приватності.</string>
     <string name="settings_discovery_consent_notice_off_2">Ваші контакти приватні. Щоб дізнаватись про користувачів, відповідних вашим контактам, дозвольте нам надсилати дані ваших контактів серверу ідентифікації.</string>
     <string name="identity_server_consent_dialog_title_2">Надіслати електронні адреси та номери телефонів %s</string>
     <string name="shortcut_disabled_reason_sign_out">Сеанс завершено!</string>
@@ -2340,7 +2340,7 @@
 \nВвімкнути захищене резервне копіювання й керувати своїми ключами можна в налаштуваннях.</string>
     <string name="verify_cancel_other">Скасування залишить %1$s (%2$s) без звірки. У їхньому користувацькому профілі можна почати заново.</string>
     <string name="verify_new_session_notice">Звірте цим сеансом свій новий. Це надасть йому доступ до зашифрованих повідомлень.</string>
-    <string name="verification_profile_device_untrust_info">Надіслані цьому сеансу й цим сеансом повідомлення позначатимуться застереженнями, поки цей користувач йому не довірить. Або ви можете власноруч звірити сеанс.</string>
+    <string name="verification_profile_device_untrust_info">Поки цей користувач не довіряє цьому сеансу, повідомлення, що надсилаються до нього і від нього, позначаються попередженнями. Крім того, ви можете звірити його вручну.</string>
     <string name="room_settings_enable_encryption_dialog_content">Якщо ви увімкнете шифрування для кімнати, його неможливо буде вимкнути. Надіслані у зашифровану кімнату повідомлення будуть прочитними тільки для учасників кімнати, натомість для сервера вони будуть непрочитними. Увімкнення шифрування може унеможливити роботу ботів та мостів.</string>
     <string name="verify_cannot_cross_sign">Не вдалося поширити звірку цього сеансу з вашими іншими.
 \nЗвірка збережеться локально, її поширить майбутня версія застосунку.</string>
@@ -2756,7 +2756,7 @@
     </plurals>
     <string name="device_manager_inactive_sessions_title">Неактивні сеанси</string>
     <string name="device_manager_unverified_sessions_description">Звірити або вийти з не звірених сеансів.</string>
-    <string name="device_manager_unverified_sessions_title">Не звірений сеанс</string>
+    <string name="device_manager_unverified_sessions_title">Не звірені сеанси</string>
     <string name="device_manager_header_section_security_recommendations_description">Удоскональте безпеку свого облікового запису, дотримуючись цих порад.</string>
     <string name="device_manager_header_section_security_recommendations_title">Поради щодо безпеки</string>
     <plurals name="device_manager_other_sessions_description_inactive">
@@ -2818,4 +2818,37 @@
     <string name="labs_enable_deferred_dm_title">Увімкнути відкладені приватні повідомлення</string>
     <string name="labs_enable_new_app_layout_summary">Спрощений Element з опціональними вкладками</string>
     <string name="labs_enable_new_app_layout_title">Увімкнути новий вигляд</string>
+    <string name="device_manager_learn_more_session_rename">Інші користувачі в особистих повідомленнях і кімнатах, до яких ви приєдналися, можуть переглядати повний список ваших сеансів.
+\n
+\nЦе дає їм впевненість у тому, що вони дійсно розмовляють з вами, а також означає, що вони можуть бачити назву сеансу, яку ви ввели тут.</string>
+    <string name="device_manager_learn_more_session_rename_title">Перейменування сеансів</string>
+    <string name="device_manager_learn_more_sessions_verified">Звірені сеанси — ті, до яких ви ввійшли за допомогою своїх облікових даних, а потім пройшли перевірку, використовуючи вашу захищену парольну фразу або шляхом перехресної перевірки.
+\n
+\nЦе означає, що вони мають ключі шифрування для ваших попередніх повідомлень і підтверджують іншим користувачам, з якими ви спілкуєтеся, що ці сеанси — це дійсно ви.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Звірені сеанси</string>
+    <string name="device_manager_learn_more_sessions_unverified">Не звірені сеанси — це сеанси, до яких ви ввійшли в за допомогою своїх облікових даних, але не пройшли перехресну перевірку.
+\n
+\nВи повинні бути особливо впевнені, що розпізнаєте ці сеанси, оскільки вони можуть означати несанкціоноване використання вашого облікового запису.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Не звірені сеанси</string>
+    <string name="device_manager_learn_more_sessions_inactive">Неактивні сеанси — це сеанси, які ви не використовували протягом певного часу, але вони продовжують отримувати ключі шифрування.
+\n
+\nВилучення неактивних сеансів поліпшує безпеку і швидкодію, а також полегшує визначення підозрілих нових сеансів.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Неактивні сеанси</string>
+    <string name="device_manager_session_rename_warning">Зауважте, що назви сеансів також видно людям, з якими ви спілкуєтесь.</string>
+    <string name="device_manager_session_rename_description">Власні назви сеансів допоможуть вам легше розпізнавати ваші пристрої.</string>
+    <string name="device_manager_session_rename_edit_hint">Назва сеансу</string>
+    <string name="device_manager_session_rename">Перейменувати сеанс</string>
+    <string name="device_manager_session_overview_signout">Вийти з цього сеансу</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Не звірений - Ваш поточний сеанс</string>
+    <string name="tooltip_attachment_voice_broadcast">Розпочати голосове мовлення</string>
+    <string name="key_authenticity_not_guaranteed">Справжність цього зашифрованого повідомлення не може бути гарантована на цьому пристрої.</string>
+    <string name="settings_security_incognito_keyboard_summary">Заборонити клавіатурі оновлювати будь-які персоналізовані дані, як-от історію набору тексту та словник, на основі того, що ви набрали в розмовах. Зверніть увагу, що деякі клавіатури можуть не дотримуватися цього налаштування.</string>
+    <string name="settings_security_incognito_keyboard_title">Клавіатура інкогніто</string>
+    <string name="command_description_table_flip">Надсилає (╯°□°)╯︵ ┻━┻ на початку текстового повідомлення</string>
+    <string name="attachment_type_voice_broadcast">Голосове мовлення</string>
+    <string name="command_description_devtools">Відкрийте інструменти розробника</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Ви увімкнули шифрування лише для перевірених сеансів для всіх кімнат у налаштуваннях безпеки.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ У цій кімнаті є неперевірені пристрої, вони не зможуть розшифрувати повідомлення, які ви надсилаєте.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Ніколи не надсилати зашифровані повідомлення на неперевірені сеанси в цій кімнаті.</string>
+    <string name="action_got_it">Зрозуміло</string>
 </resources>
\ No newline at end of file

From 92194892e5c1150e8702a808ba1dd8764c06935f Mon Sep 17 00:00:00 2001
From: Jeff Huang <s8321414@gmail.com>
Date: Thu, 6 Oct 2022 01:46:52 +0000
Subject: [PATCH 050/400] Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hant/
---
 fastlane/metadata/android/zh-TW/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/zh-TW/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/zh-TW/changelogs/40105000.txt b/fastlane/metadata/android/zh-TW/changelogs/40105000.txt
new file mode 100644
index 0000000000..7ab6a7a7bf
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+此版本中的主要變動:預設啟用延遲直接訊息。
+完整的變更紀錄:https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/zh-TW/changelogs/40105020.txt b/fastlane/metadata/android/zh-TW/changelogs/40105020.txt
new file mode 100644
index 0000000000..d83fd08a53
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+此版本中的主要變動:預設啟用新的應用程式佈局!
+完整的變更紀錄:https://github.com/vector-im/element-android/releases

From d4d9e8d93d91531c67a3896cf9fb8d41f2a66476 Mon Sep 17 00:00:00 2001
From: Danial Behzadi <dani.behzi@ubuntu.com>
Date: Fri, 7 Oct 2022 17:11:18 +0000
Subject: [PATCH 051/400] Translated using Weblate (Persian)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/fa/
---
 fastlane/metadata/android/fa/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/fa/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/fa/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/fa/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/fa/changelogs/40105000.txt b/fastlane/metadata/android/fa/changelogs/40105000.txt
new file mode 100644
index 0000000000..605efd76f1
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+تغییرات عمده در این نگارش: پیام‌های مستقیم تعویقی به کار افتاده به صورت پیش‌گزیده!
+گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/fa/changelogs/40105020.txt b/fastlane/metadata/android/fa/changelogs/40105020.txt
new file mode 100644
index 0000000000..f6e193ede5
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+تغییرات عمده در این نگارش: چینش کارهٔ جدید به کار افتاده به صورت پیش‌گزیده!
+گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases

From 7fdfc6680433f2ae6c3705e0b7fb903732ae0c14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com>
Date: Fri, 7 Oct 2022 05:32:10 +0000
Subject: [PATCH 052/400] Translated using Weblate (Estonian)

Currently translated at 99.6% (2436 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/et/
---
 .../src/main/res/values-et/strings.xml        | 45 ++++++++++++++++---
 1 file changed, 39 insertions(+), 6 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-et/strings.xml b/library/ui-strings/src/main/res/values-et/strings.xml
index dbdbbdbb00..10453a4fc9 100644
--- a/library/ui-strings/src/main/res/values-et/strings.xml
+++ b/library/ui-strings/src/main/res/values-et/strings.xml
@@ -1197,8 +1197,8 @@
     <string name="settings_discovery_disconnect_identity_server_info">Isikutuvastusserveri kasutamise lõpetamine tähendab, et sa ei ole leitav teiste kasutajate poolt ega sulle ei saa telefoninumbri või e-posti aadressi alusel kutset saata. Küll aga saab kutset saata Matrix\'i kasutajatunnuse alusel.</string>
     <string name="settings_discovery_msisdn_title">Leitavad telefoninumbrid</string>
     <string name="labs_show_unread_notifications_as_tab">Lisa avaekraanile eraldi sakk lugemata teavituste jaoks.</string>
-    <string name="settings_discovery_confirm_mail">Saatsime kinnituskirja e-posti aadressile %s. Palun vaata oma kirju ja klõpsi selles kirjas leiduvat linki</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Saatsime kinnituskirja e-posti aadressile %s. Esmalt palun vaata oma kirju ja klõpsi selles kirjas leiduvat linki</string>
+    <string name="settings_discovery_confirm_mail">Saatsime kirja e-posti aadressile %s. Palun vaata oma kirju ja klõpsi selles kirjas leiduvat linki</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Saatsime kirja e-posti aadressile %s. Esmalt palun vaata oma kirju ja klõpsi selles kirjas leiduvat linki</string>
     <string name="settings_agree_to_terms">Selleks, et sind võiks leida e-posti aadressi või telefoninumbri alusel, nõustu isikutuvastusserveri (%s) kasutustingimustega.</string>
     <string name="labs_allow_extended_logging">Lülita sisse detailsete logide salvestamine.</string>
     <string name="no_ignored_users">Sa parasjagu ei eira ühtegi kasutajat</string>
@@ -1336,7 +1336,7 @@
     <string name="disconnect_identity_server_dialog_content">Kas katkestame ühenduse isikutuvastusserveriga %s\?</string>
     <string name="identity_server_error_no_identity_server_configured">Palun esmalt seadista isikutuvastusserver.</string>
     <string name="identity_server_error_terms_not_signed">Palun esmalt nõustu seadistustes isikutuvastusserveri kasutustingimustega.</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Sinu privaatsuse nimel saadab ${app_name} e-posti aadressid ja telefoninumbrid serverisse vaid räsituna.</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Sinu privaatsuse nimel saadab ${app_name} e-posti aadressid ja telefoninumbrid serverisse vaid räsidena.</string>
     <string name="identity_server_error_binding_error">Seoste loomine ei õnnestunud.</string>
     <string name="identity_server_error_no_current_binding_error">Selle tunnusega ei ole hetkel ühtegi seost.</string>
     <string name="identity_server_set_default_notice">Sinu koduserver (%1$s) soovitab kasutada sinu isikutuvastusserverina %2$s teenust</string>
@@ -1778,7 +1778,7 @@
     <string name="not_a_valid_qr_code">See ei ole korralik Matrix\'i QR-kood</string>
     <string name="settings_discovery_consent_action_give_consent">Nõustu</string>
     <string name="settings_discovery_consent_action_revoke">Tühista minu nõusolek</string>
-    <string name="settings_discovery_consent_notice_on">Selleks, et leida Matrixikasutajaid oma kontaktide hulgast, oled sa andnud nõusoleku saata e-posti aadresse ja telefoninumbreid sellele isikutuvastusserverile.</string>
+    <string name="settings_discovery_consent_notice_on">Selleks, et leida Matrixikasutajaid oma kontaktide hulgast, oled sa andnud nõusoleku e-posti aadresside ja telefoninumbrite saatmiseks sellele isikutuvastusserverile.</string>
     <string name="settings_discovery_consent_title">Saada e-posti aadresse ja telefoninumbreid</string>
     <string name="direct_room_user_list_suggestions_title">Soovitused</string>
     <string name="direct_room_user_list_known_title">Tuttavad kasutajad</string>
@@ -2211,7 +2211,7 @@
     <string name="room_settings_space_access_title">Ligipääs siia kogukonda</string>
     <string name="room_settings_access_rules_pref_dialog_title">Kes pääsevad ligi siia jututuppa\?</string>
     <string name="settings_notification_emails_enable_for_email">Saada teavitusi %s e-posti aadressile</string>
-    <string name="settings_notification_emails_no_emails">Kui soovi e-posti teel saada teavitusi, siis palun seosta oma e-posti aadress oma Matrix\'i kasutajakontoga</string>
+    <string name="settings_notification_emails_no_emails">Kui soovid e-posti teel saada teavitusi, siis palun seosta oma e-posti aadress oma Matrix\'i kasutajakontoga</string>
     <string name="settings_notification_emails_category">E-posti teel saadetavad teavitused</string>
     <string name="room_permissions_upgrade_the_space">Uuenda kogukonda</string>
     <string name="room_permissions_change_space_name">Muuda kogukonna nime</string>
@@ -2691,7 +2691,7 @@
     <string name="device_manager_filter_bottom_sheet_title">Filtreeri</string>
     <string name="device_manager_session_last_activity">Viimati kasutusel %1$s</string>
     <string name="device_manager_device_title">Seade</string>
-    <string name="device_manager_session_title">Sessioonid</string>
+    <string name="device_manager_session_title">Sessioon</string>
     <string name="device_manager_current_session_title">Praegune sessioon</string>
     <string name="device_manager_verification_status_detail_other_session_unverified">Parima turvalisuse ja töökindluse nimel verifitseeri see sessioon või logi ta võrgust välja.</string>
     <string name="device_manager_verification_status_detail_current_session_unverified">Turvalise sõnumivahetuse nimel palun verifitseeri oma praegune sessioon.</string>
@@ -2701,4 +2701,37 @@
     <string name="labs_enable_deferred_dm_title">Võta kasutusele viivitusega otsevestlused</string>
     <string name="labs_enable_new_app_layout_summary">Lihtsustatud Element valikuliste kaartidega</string>
     <string name="labs_enable_new_app_layout_title">Võta kasutusele rakenduse uus välimus</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Sa oled rakenduse turvaseadistustes määranud, et krüptimine kehtib vaid verifitseeritud sessioonidele.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Selles jututoas on verifitseerimata seadmeid, mis ei saa sinu saadetud sõnumeid dekrüptida.</string>
+    <string name="device_manager_learn_more_session_rename">Teised osapooled nii otsesõnumites kui jututubades näevad sinu kõikide sessioonide loendit.
+\n
+\nSee tähendab, et nad võivad uskuda, et tegemist on tõesti sinuga. Samal ajal näevad ka siin sisestatud sessiooninime.</string>
+    <string name="device_manager_learn_more_session_rename_title">Sessioonide nimede muutmine</string>
+    <string name="device_manager_learn_more_sessions_verified">Verifitseeritud sessioonid on sellised, kuhu sa oled oma kasutajanime ja salasõnaga sisse loginud ning mille puhul oled risttunnustamise läbi teinud või paroolifraasi abil ta turvaliseks märkinud.
+\n
+\nSee tähendab, et nendes sessioonides on olemas sinu varasemate sõnumite krüptovõtmed ja teistele osapooltele on nad tuvastatavad nii, et tegemist on tõesti sinuga.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Verifitseeritud sessioonid</string>
+    <string name="device_manager_learn_more_sessions_unverified">Verifitseerimata sessioonid on sellised, kuhu sa oled oma kasutajanime ja salasõnaga sisse loginud, kuid mille puhul on risttunnustamine tegemata.
+\n
+\nPalun kontrolli, et need on sulle teadaolevad sessioonid. Vastasel korral võib olla tegemist sinu kasutajakonto lubamatu kasutamisega.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Verifitseerimata sessioonid</string>
+    <string name="device_manager_learn_more_sessions_inactive">Sessioonid, mida sa pole mõnda aega kasutanud, saavad jätkuvalt pärida sinu krüptovõtmeid.
+\n
+\nSelliste sessioonide eemaldamine parandab jõudlust ja turvalisust ning sul on lihtsam märgata, kui loendisse tekib midagi kahtlast.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Mitteaktiivsed sessioonid</string>
+    <string name="device_manager_session_rename_warning">Palun arvesta, et sessioonide nimed on näha ka kõikidele osapooltele, kellega sa suhtled.</string>
+    <string name="device_manager_session_rename_description">Sinu enda kirjutatud sessiooninimede alusel on sul oma seadmeid lihtsam ära tunda.</string>
+    <string name="device_manager_session_rename_edit_hint">Sessiooni nimi</string>
+    <string name="device_manager_session_rename">Muuda sessiooni nime</string>
+    <string name="device_manager_session_overview_signout">Logi sellest sessioonist välja</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Verifitseerimata · Sinu praegune sessioon</string>
+    <string name="tooltip_attachment_voice_broadcast">Alusta ringhäälingukõnet</string>
+    <string name="key_authenticity_not_guaranteed">Selle krüptitud sõnumi autentsus pole selles seadmes tagatud.</string>
+    <string name="settings_security_incognito_keyboard_summary">Väldi sisetuste ja muude isikustatud andmete edastamist väljaspoole seda rakendust. Palun arvesta, et kõik klaviatuurirakendused ei pruugi seda seadistust tunnistada.</string>
+    <string name="settings_security_incognito_keyboard_title">Privaatne klahvistik</string>
+    <string name="command_description_table_flip">Lisab vormindamata sõnumi ette (╯°□°)╯︵ ┻━┻</string>
+    <string name="attachment_type_voice_broadcast">Ringhäälingukõne</string>
+    <string name="command_description_devtools">Ava arendaja töövahendite vaade</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Ära iialgi saada selles jututoas krüptitud sõnumeid verifitseerimata sessioonidesse.</string>
+    <string name="action_got_it">Selge lugu</string>
 </resources>
\ No newline at end of file

From d903c612235cde336e744cfbcfa4b1e43e235f41 Mon Sep 17 00:00:00 2001
From: Jozef Gaal <preklady@mayday.sk>
Date: Thu, 6 Oct 2022 21:36:59 +0000
Subject: [PATCH 053/400] Translated using Weblate (Slovak)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/
---
 .../src/main/res/values-sk/strings.xml        | 43 ++++++++++++++++---
 1 file changed, 38 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-sk/strings.xml b/library/ui-strings/src/main/res/values-sk/strings.xml
index f37af1a654..e479e932d4 100644
--- a/library/ui-strings/src/main/res/values-sk/strings.xml
+++ b/library/ui-strings/src/main/res/values-sk/strings.xml
@@ -1040,8 +1040,8 @@
     <string name="settings_discovery_no_msisdn">Možnosti objavovania sa zobrazia, až keď pridáte telefónne číslo.</string>
     <string name="settings_discovery_disconnect_identity_server_info">Odpojením sa od servera totožností znemožníte ostatným, aby vás našli a tiež nebudete môcť kontakty pozývať zadaním emailovej adresy alebo telefónneho čísla.</string>
     <string name="settings_discovery_msisdn_title">Telefónne čísla, podľa ktorých je vás možné nájsť</string>
-    <string name="settings_discovery_confirm_mail">Odoslali sme vám potvrdzujúci e-mail na adresu %s, skontrolujte svoj e-mail a kliknite na potvrdzujúci odkaz</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Odoslali sme vám potvrdzujúci e-mail na adresu %s, najskôr si prosím skontrolujte svoj e-mail a kliknite na potvrdzujúci odkaz</string>
+    <string name="settings_discovery_confirm_mail">Odoslali sme vám e-mail na adresu %s, skontrolujte svoj e-mail a kliknite na potvrdzujúci odkaz</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Odoslali sme vám e-mail na adresu %s, najskôr si prosím skontrolujte svoj e-mail a kliknite na potvrdzujúci odkaz</string>
     <string name="settings_discovery_enter_identity_server">Zadajte URL adresu servera totožností</string>
     <string name="settings_discovery_bad_identity_server">Nie je možné sa pripojiť k serveru totožností</string>
     <string name="settings_discovery_please_enter_server">Prosím, zadajte URL adresu servera totožností</string>
@@ -1846,7 +1846,7 @@
     <string name="settings_notification_keyword_contains_dot">Kľúčové slová nemôžu začínať na \".\"</string>
     <string name="settings_notification_new_keyword">Pridať nové kľúčové slovo</string>
     <string name="settings_notification_emails_enable_for_email">Povoliť e-mailové oznámenia pre %s</string>
-    <string name="settings_notification_emails_no_emails">Ak chcete dostávať e-mail s upozornením, priraďte e-mail k svojmu kontu Matrix</string>
+    <string name="settings_notification_emails_no_emails">Ak chcete dostávať e-mail s upozornením, priraďte e-mail k svojmu Matrix účtu</string>
     <string name="settings_notification_emails_category">E-mailové oznámenie</string>
     <string name="room_settings_room_notifications_notify_me">Upozorniť ma na</string>
     <string name="settings_notification_notify_me_for">Upozorniť ma na</string>
@@ -1856,10 +1856,10 @@
     <string name="room_settings_mention_and_keyword_only">Iba zmienky a kľúčové slová</string>
     <string name="login_msisdn_error_not_international">Medzinárodné telefónne čísla musia začínať znakom \"+\"</string>
     <string name="identity_server_consent_dialog_content_3">Ak chcete zistiť existujúce kontakty, potrebujete odoslať kontaktné informácie (e-maily a telefónne čísla) na server totožností. Pred odoslaním vaše údaje zahašujeme kvôli ochrane osobných údajov.</string>
-    <string name="identity_server_consent_dialog_title_2">Odoslať e-maily a telefónne čísla na %s</string>
+    <string name="identity_server_consent_dialog_title_2">Odoslať emailové adresy a telefónne čísla na %s</string>
     <string name="settings_discovery_consent_notice_on">Dali ste súhlas na odosielanie e-mailov a telefónnych čísel na tento server totožností na objavenie ďalších používateľov z vašich kontaktov.</string>
     <string name="settings_discovery_consent_title">Odoslať e-maily a telefónne čísla</string>
-    <string name="settings_emails_and_phone_numbers_summary">Spravovať e-maily a telefónne čísla prepojené s vaším účtom Matrix</string>
+    <string name="settings_emails_and_phone_numbers_summary">Spravujte e-maily a telefónne čísla prepojené s vaším účtom Matrix</string>
     <string name="settings_emails_and_phone_numbers_title">Emaily a telefónne čísla</string>
     <string name="change_password_summary">Nastaviť nové heslo k účtu…</string>
     <string name="bootstrap_dont_reuse_pwd">Nepoužívajte heslo k svojmu účtu.</string>
@@ -2764,4 +2764,37 @@
     <string name="labs_enable_deferred_dm_title">Povoliť odložené priame správy</string>
     <string name="labs_enable_new_app_layout_summary">Zjednodušený Element s voliteľnými kartami</string>
     <string name="labs_enable_new_app_layout_title">Zapnúť nové usporiadanie</string>
+    <string name="device_manager_learn_more_session_rename">Ostatní používatelia v priamych správach a miestnostiach, do ktorých sa pripojíte, si môžu pozrieť úplný zoznam vašich relácií.
+\n
+\nTo im poskytuje istotu, že sa s vami naozaj rozprávajú, ale zároveň to znamená, že vidia názov relácie, ktorý sem zadáte.</string>
+    <string name="device_manager_learn_more_session_rename_title">Premenovanie relácií</string>
+    <string name="device_manager_learn_more_sessions_verified">Overené relácie, do ktorých ste sa prihlásili pomocou svojich prihlasovacích údajov a ktoré boli následne overené buď pomocou vašej bezpečnostnej prístupovej frázy, alebo krížovým overením.
+\n
+\nTo znamená, že majú šifrovacie kľúče pre vaše predchádzajúce správy a potvrdzujú ostatným používateľom, s ktorými komunikujete, že tieto relácie ste skutočne vy.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Overené relácie</string>
+    <string name="device_manager_learn_more_sessions_unverified">Neoverené relácie sú relácie, do ktorých ste sa prihlásili pomocou svojich prístupových údajov, ale ktoré neboli krížovo overené.
+\n
+\nMali by ste si byť obzvlášť istí, že tieto relácie poznáte, pretože by mohli predstavovať neoprávnené použitie vášho konta.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Neoverené relácie</string>
+    <string name="device_manager_learn_more_sessions_inactive">Neaktívne relácie sú relácie, ktoré ste určitý čas nepoužívali, ale naďalej dostávajú šifrovacie kľúče.
+\n
+\nOdstránenie neaktívnych relácií zvyšuje bezpečnosť a výkon a uľahčuje identifikáciu podozrivých nových relácií.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Neaktívne relácie</string>
+    <string name="device_manager_session_rename_warning">Uvedomte si, že názvy relácií sú viditeľné aj pre ľudí, s ktorými komunikujete.</string>
+    <string name="device_manager_session_rename_description">Vlastné názvy relácií vám pomôžu ľahšie rozpoznať vaše zariadenia.</string>
+    <string name="device_manager_session_rename_edit_hint">Názov relácie</string>
+    <string name="device_manager_session_rename">Premenovať reláciu</string>
+    <string name="device_manager_session_overview_signout">Odhlásiť sa z tejto relácie</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Neoverená - Vaša aktuálna relácia</string>
+    <string name="tooltip_attachment_voice_broadcast">Spustiť hlasové vysielanie</string>
+    <string name="key_authenticity_not_guaranteed">Vierohodnosť tejto zašifrovanej správy nie je možné zaručiť na tomto zariadení.</string>
+    <string name="settings_security_incognito_keyboard_summary">Požiadajte, aby klávesnica neaktualizovala žiadne personalizované údaje, napríklad históriu písania a slovník, na základe toho, čo ste napísali v konverzáciách. Upozorňujeme, že niektoré klávesnice nemusia toto nastavenie rešpektovať.</string>
+    <string name="settings_security_incognito_keyboard_title">Inkognito klávesnica</string>
+    <string name="command_description_table_flip">Pridá znaky (╯°□°)╯︵ ┻━┻ pred správy vo formáte obyčajného textu</string>
+    <string name="attachment_type_voice_broadcast">Hlasové vysielanie</string>
+    <string name="command_description_devtools">Otvoriť obrazovku vývojárskych nástrojov</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 V Nastaveniach zabezpečenia ste povolili šifrovanie len pre overené relácie pre všetky miestnosti.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ V tejto miestnosti sa nachádzajú neoverené zariadenia, ktoré nebudú schopné dešifrovať odoslané správy.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Nikdy neposielať šifrované správy do neoverených relácií v tejto miestnosti.</string>
+    <string name="action_got_it">Rozumiem</string>
 </resources>
\ No newline at end of file

From 5284312282d07622f7f23af280c9e6c584694bb6 Mon Sep 17 00:00:00 2001
From: LinAGKar <linus.kardell@gmail.com>
Date: Fri, 7 Oct 2022 19:38:16 +0000
Subject: [PATCH 054/400] Translated using Weblate (Swedish)

Currently translated at 95.0% (2322 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/sv/
---
 .../src/main/res/values-sv/strings.xml        | 37 ++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values-sv/strings.xml b/library/ui-strings/src/main/res/values-sv/strings.xml
index 30b63c213c..c34f302a4f 100644
--- a/library/ui-strings/src/main/res/values-sv/strings.xml
+++ b/library/ui-strings/src/main/res/values-sv/strings.xml
@@ -2581,4 +2581,39 @@
     <string name="font_size_section_manually">Välj manuellt</string>
     <string name="font_size_section_auto">Ställ in automatiskt</string>
     <string name="font_size_title">Välj teckenstorlek</string>
-</resources>
+    <string name="command_description_devtools">Öppna utvecklingsverktygsskärmen</string>
+    <string name="timeline_error_room_not_found">Tyvärr hittades inte det här rummet.
+\nVänligen pröva igen senare.%s</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Du har aktiverat kryptering endast till verifierade sessioner för alla rum i säkerhetsinställningarna.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Det finns overifierade enheter i det här rummet, de kommer inte kunna avkryptera meddelanden du skickar.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Skicka aldrig krypterade meddelanden till overifierade sessioner i det här rummet.</string>
+    <plurals name="search_space_multiple_parents">
+        <item quantity="one">%1$s och %2$d till</item>
+        <item quantity="other">%1$s och %2$d till</item>
+    </plurals>
+    <string name="search_space_two_parents">%1$s och %2$s</string>
+    <string name="auth_reset_password_error_unverified">E-post inte verifierad, kolla din inkorg</string>
+    <string name="invites_empty_message">Det här är var dina nya förfrågningar och inbjudningar kommer att vara.</string>
+    <string name="invites_empty_title">Inget nytt.</string>
+    <string name="invites_title">Inbjudningar</string>
+    <string name="space_list_empty_message">Utrymmen är ett nytt sätt att gruppera rum och personer. Skapa ett utrymme för att komma igång.</string>
+    <string name="space_list_empty_title">Inga utrymmen än.</string>
+    <string name="labs_enable_deferred_dm_summary">Skapa DM först när du skickar första meddelandet</string>
+    <string name="labs_enable_deferred_dm_title">Aktivera avvaktade DM:er</string>
+    <string name="labs_enable_new_app_layout_summary">En förenklad Element med valfria flikar</string>
+    <string name="labs_enable_new_app_layout_title">Aktivera ny layout</string>
+    <string name="home_layout_preferences_sort_name">A - Ö</string>
+    <string name="home_layout_preferences_sort_activity">Aktivitet</string>
+    <string name="home_layout_preferences_sort_by">Sortera efter</string>
+    <string name="home_layout_preferences_recents">Visa nyliga</string>
+    <string name="home_layout_preferences_filters">Visa filter</string>
+    <string name="home_layout_preferences">Gränssnittsinställningar</string>
+    <string name="action_got_it">Förstått</string>
+    <string name="a11y_collapse_space_children">Kollapsa underutrymmen för %s</string>
+    <string name="a11y_expand_space_children">Expandera underutrymmen för %s</string>
+    <string name="explore_rooms">Utforska rum</string>
+    <string name="change_space">Byt utrymme</string>
+    <string name="create_room">Skapa rum</string>
+    <string name="start_chat">Starta chatt</string>
+    <string name="all_chats">Alla chattar</string>
+</resources>
\ No newline at end of file

From f9ce912c0ca173b89c4910c007068dfb20eda58c Mon Sep 17 00:00:00 2001
From: Jeff Huang <s8321414@gmail.com>
Date: Thu, 6 Oct 2022 02:06:02 +0000
Subject: [PATCH 055/400] Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/
---
 .../src/main/res/values-zh-rTW/strings.xml    | 61 ++++++++++++++-----
 1 file changed, 47 insertions(+), 14 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
index 876084d566..096bf9e36f 100644
--- a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
@@ -938,11 +938,11 @@
     <string name="settings_discovery_identity_server_info">您正在使用 %1$s 來探索與被您認識的既有聯絡人探索。</string>
     <string name="settings_discovery_identity_server_info_none">您目前並未使用身份識別伺服器。要探索與被您認識的聯絡人探索,請在下方設定一個。</string>
     <string name="settings_discovery_emails_title">可探索的電子郵件地址</string>
-    <string name="settings_discovery_no_mails">在您新增電子郵件後,探索選項將會出現。</string>
+    <string name="settings_discovery_no_mails">在您新增電子郵件地址後,探索選項將會出現。</string>
     <string name="settings_discovery_no_msisdn">在您新增電話號碼後,探索選項將會出現。</string>
     <string name="settings_discovery_disconnect_identity_server_info">與您的身份識別伺服器斷線代表您無法被其他使用者探索,且您將無法透過電子郵件或電話邀請其他人。</string>
     <string name="settings_discovery_msisdn_title">可探索的電話號碼</string>
-    <string name="settings_discovery_confirm_mail">我們將會傳送確認電子郵件到 %s 給您,請檢查您的電子郵件並在確認連結上點選</string>
+    <string name="settings_discovery_confirm_mail">我們將會傳送電子郵件到 %s,請檢查您的電子郵件並在確認連結上點選</string>
     <string name="settings_discovery_enter_identity_server">輸入身份識別伺服器 URL</string>
     <string name="settings_discovery_bad_identity_server">無法連線到身份識別伺服器</string>
     <string name="settings_discovery_please_enter_server">請輸入身份識別伺服器 URL</string>
@@ -1069,7 +1069,7 @@
     <string name="login_registration_not_supported">應用程式無法在此家伺服器上建立帳號。
 \n
 \n您想要使用網路客戶端註冊嗎?</string>
-    <string name="login_login_with_email_error">此電子郵件未關聯到任何帳號。</string>
+    <string name="login_login_with_email_error">此電子郵件地址未關聯到任何帳號。</string>
     <string name="login_reset_password_on">在 %1$s 上重設密碼</string>
     <string name="login_reset_password_notice">驗證郵件已傳送到您的收件匣以確認您要設定新密碼。</string>
     <string name="login_reset_password_submit">下一步</string>
@@ -1078,7 +1078,7 @@
     <string name="login_reset_password_warning_title">警告!</string>
     <string name="login_reset_password_warning_content">變更您的密碼將會重設在您所有工作階段中任何的端到端加密金鑰,讓已加密的聊天歷史無法讀取。請在重設您的密碼前從其他工作階段設定金鑰備份或匯出您的聊天室金鑰。</string>
     <string name="login_reset_password_warning_submit">繼續</string>
-    <string name="login_reset_password_error_not_found">此電子郵件未被連結到任何帳號</string>
+    <string name="login_reset_password_error_not_found">此電子郵件地址未被連結到任何帳號</string>
     <string name="login_reset_password_mail_confirmation_title">檢查您的收件匣</string>
     <string name="login_reset_password_mail_confirmation_notice">驗證電子郵件已傳送至 %1$s。</string>
     <string name="login_reset_password_mail_confirmation_notice_2">輕點連結以確認您的新密碼。在您使用了其中包含的連結後,請點擊下方。</string>
@@ -1092,7 +1092,7 @@
 \n
 \n停止密碼變更流程?</string>
     <string name="login_set_email_title">設定電子郵件地址</string>
-    <string name="login_set_email_notice">設定電子郵件以復原您的帳號。之後您也可以選擇性地讓您認識的人透過您的電子郵件找到您。</string>
+    <string name="login_set_email_notice">設定電子郵件地址以復原您的帳號。之後您也可以選擇性地讓您認識的人透過您的這個地址找到您。</string>
     <string name="login_set_email_mandatory_hint">電子郵件</string>
     <string name="login_set_email_optional_hint">電子郵件(選擇性)</string>
     <string name="login_set_email_submit">下一個</string>
@@ -1430,7 +1430,7 @@
     <string name="event_redacted">訊息已移除</string>
     <string name="settings_show_redacted">顯示已移除的訊息</string>
     <string name="settings_show_redacted_summary">為已移除的訊息顯示佔位符</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">我們已傳送確認電子郵件給 %s,請先檢查您的電子郵件並點擊確認連結</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">我們已傳送電子郵件到 %s,請先檢查您的電子郵件並點擊確認連結</string>
     <string name="settings_text_message_sent_wrong_code">驗證代碼不正確。</string>
     <string name="uploads_media_title">媒體</string>
     <string name="uploads_media_no_result">此聊天室中沒有媒體</string>
@@ -1453,7 +1453,7 @@
     <string name="identity_server_error_outdated_home_server">此動作是不可能的。家伺服器太舊了。</string>
     <string name="identity_server_error_no_identity_server_configured">請先設定身份識別伺服器。</string>
     <string name="identity_server_error_terms_not_signed">請先在設定中同意身份識別伺服器的條款。</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">為了保護您的隱私,${app_name} 僅支援傳送雜湊過的使用者電子郵件與電話號碼。</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">為了保護您的隱私,${app_name} 僅支援傳送雜湊過的使用者電子郵件地址與電話號碼。</string>
     <string name="identity_server_error_binding_error">關聯失敗。</string>
     <string name="identity_server_error_no_current_binding_error">目前沒有此識別符的關聯。</string>
     <string name="identity_server_set_default_notice">您的家伺服器 (%1$s) 建議將 %2$s 用於您的身份識別伺服器</string>
@@ -1623,12 +1623,12 @@
     <string name="auth_msisdn_already_defined">此電話號碼已被定義。</string>
     <string name="settings_phone_number_empty">未新增電話號碼到您的帳號</string>
     <string name="settings_emails">電子郵件地址</string>
-    <string name="settings_emails_empty">未傳送電子郵件到您的帳號</string>
+    <string name="settings_emails_empty">未新增電子郵件地址至您的帳號</string>
     <string name="settings_phone_numbers">電話號碼</string>
     <string name="settings_remove_three_pid_confirmation_content">移除 %s?</string>
     <string name="error_threepid_auth_failed">確保您已經點擊我們傳送給您的電子郵件中的連結。</string>
     <string name="settings_emails_and_phone_numbers_title">電子郵件與電話號碼</string>
-    <string name="settings_emails_and_phone_numbers_summary">管理連結到您 Matrix 帳號的電子郵件與電話號碼</string>
+    <string name="settings_emails_and_phone_numbers_summary">管理連結到您 Matrix 帳號的電子郵件地址與電話號碼</string>
     <string name="settings_text_message_sent_hint">代碼</string>
     <string name="login_msisdn_notice">請使用國際格式(電話號碼必須以 \'+\' 開頭)</string>
     <string name="confirm_your_identity_quad_s">透過確認此登入來驗證您的身份,以及授予存取加密訊息的權限。</string>
@@ -1744,7 +1744,7 @@
     <string name="attachment_viewer_item_x_of_y">%2$d 中的 %1$d</string>
     <string name="settings_discovery_consent_action_give_consent">給予同意</string>
     <string name="settings_discovery_consent_action_revoke">撤銷我的同意</string>
-    <string name="settings_discovery_consent_notice_on">您已同意傳送電子郵件與電話號碼到此身份提供者以從您的聯絡人中探索其他使用者。</string>
+    <string name="settings_discovery_consent_notice_on">您已同意傳送電子郵件地址與電話號碼到此身份提供者以從您的聯絡人中探索其他使用者。</string>
     <string name="settings_discovery_consent_title">傳送電子郵件或電話號碼</string>
     <string name="direct_room_user_list_suggestions_title">建議</string>
     <string name="direct_room_user_list_known_title">已知的使用者</string>
@@ -2103,7 +2103,7 @@
     <string name="settings_notification_mentions_and_keywords">提及與關鍵字</string>
     <string name="settings_notification_default">預設通知</string>
     <string name="link_this_email_with_your_account">設定中的 %s 可直接在 ${app_name} 中接收邀請。</string>
-    <string name="link_this_email_settings_link">將此電子郵件與您的帳號連結</string>
+    <string name="link_this_email_settings_link">將此電子郵件地址與您的帳號連結</string>
     <string name="this_invite_to_this_space_was_sent">此空間的邀請已傳送給與您的帳號無關的 %s</string>
     <string name="this_invite_to_this_room_was_sent">此聊天室的邀請已傳送給與您的帳號無關的 %s</string>
     <string name="all_rooms_youre_in_will_be_shown_in_home">您所在的所有聊天室都會顯示在 Home 中。</string>
@@ -2171,7 +2171,7 @@
     <string name="room_settings_space_access_title">空間存取</string>
     <string name="room_settings_access_rules_pref_dialog_title">誰可以存取?</string>
     <string name="settings_notification_emails_enable_for_email">啟用 %s 的電子郵件通知</string>
-    <string name="settings_notification_emails_no_emails">要收到通知用的電子郵件,請將電子郵件關聯至您的 Matrix 帳號</string>
+    <string name="settings_notification_emails_no_emails">要收到通知用的電子郵件,請將電子郵件地址關聯至您的 Matrix 帳號</string>
     <string name="settings_notification_emails_category">電子郵件通知</string>
     <string name="room_permissions_upgrade_the_space">升級空間</string>
     <string name="room_permissions_change_space_name">變更空間名稱</string>
@@ -2217,12 +2217,12 @@
     <string name="create_poll_question_title">投票問題或主題</string>
     <string name="create_poll_title">建立投票</string>
     <string name="attachment_type_poll">投票</string>
-    <string name="identity_server_consent_dialog_title_2">向 %s 傳送電子郵件與電話號碼</string>
+    <string name="identity_server_consent_dialog_title_2">向 %s 傳送電子郵件地址與電話號碼</string>
     <string name="settings_discovery_consent_notice_off_2">您的通訊錄是私人的。要從您的通訊錄中探索使用者,我們需要您的權限來傳送聯絡人資訊到您的身份識別伺服器。</string>
     <string name="shortcut_disabled_reason_sign_out">已登出工作階段!</string>
     <string name="shortcut_disabled_reason_room_left">已離開聊天室!</string>
     <string name="identity_server_consent_dialog_content_question">您同意傳送此資訊嗎?</string>
-    <string name="identity_server_consent_dialog_content_3">要探索現有聯絡人,您必須傳送聯絡人資訊(電子郵件與電話號碼)到您的身份識別伺服器。我們會在傳送前對您的資料進行雜湊處理以保護隱私。</string>
+    <string name="identity_server_consent_dialog_content_3">要探索現有聯絡人,您必須傳送聯絡人資訊(電子郵件地址與電話號碼)到您的身份識別伺服器。我們會在傳送前對您的資料進行雜湊處理以保護隱私。</string>
     <string name="action_not_now">現在不要</string>
     <string name="delete_poll_dialog_content">您確定要移除此投票?移除後將無法復原。</string>
     <string name="delete_poll_dialog_title">移除投票</string>
@@ -2656,4 +2656,37 @@
     <string name="labs_enable_deferred_dm_title">啟用延期直接訊息</string>
     <string name="labs_enable_new_app_layout_summary">包含選擇性分頁的簡潔 Element</string>
     <string name="labs_enable_new_app_layout_title">啟用新佈局</string>
+    <string name="device_manager_learn_more_session_rename">您加入的直接消息與聊天室中的其他使用者可以檢視您的工作階段的完整清單。
+\n
+\n這讓他們確信他們真的在與您交談,但這也意味著他們可以看到您在此處輸入的工作階段名稱。</string>
+    <string name="device_manager_learn_more_session_rename_title">正在重新命名工作階段</string>
+    <string name="device_manager_learn_more_sessions_verified">已驗證的工作階段代表使用您的憑證登入,然後使用您的安全通關密語或透過交叉驗證進行驗證。
+\n
+\n這代表了它們持有您先前訊息的加密金鑰,並向您正在與之通訊的其他使用者確認這些工作階段確實是您。</string>
+    <string name="device_manager_learn_more_sessions_verified_title">已驗證的工作階段</string>
+    <string name="device_manager_learn_more_sessions_unverified">未驗證的工作階段是使用您的憑證登入但未交叉驗證的工作階段。
+\n
+\n您應特別確定您可以識別這些工作階段,因為它們可能代表未經授權使用您的帳號。</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">未驗證的工作階段</string>
+    <string name="device_manager_learn_more_sessions_inactive">不活躍的工作階段是您有一段時間未使用的工作階段,但它們會繼續接收加密金鑰。
+\n
+\n移除不活躍的工作階段可以改善安全性與效能,並讓您可以更容易地識別新的工作階段是否可疑。</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">不活躍的工作階段</string>
+    <string name="device_manager_session_rename_warning">請注意,與您交流的人也可以看到工作階段名稱。</string>
+    <string name="device_manager_session_rename_description">自訂工作階段名稱可以協助您更輕鬆地識別您的裝置。</string>
+    <string name="device_manager_session_rename_edit_hint">工作階段名稱</string>
+    <string name="device_manager_session_rename">重新命名工作階段</string>
+    <string name="device_manager_session_overview_signout">登出此工作階段</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">未驗證 · 您目前的工作階段</string>
+    <string name="tooltip_attachment_voice_broadcast">開始語音廣播</string>
+    <string name="key_authenticity_not_guaranteed">此裝置無法保證此加密訊息的真實性。</string>
+    <string name="settings_security_incognito_keyboard_summary">要求鍵盤不要根據您在對話中輸入的內容更新任何個人化資料(如輸入歷史紀錄與字典等)。請注意,某些鍵盤可能不會遵守此設定。</string>
+    <string name="settings_security_incognito_keyboard_title">無痕式鍵盤</string>
+    <string name="command_description_table_flip">將 (╯°□°)╯︵ ┻━┻ 放到純文字訊息之前</string>
+    <string name="attachment_type_voice_broadcast">語音廣播</string>
+    <string name="command_description_devtools">開啟開發者工具畫面</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 您已在「安全」設定中為所有聊天室啟用加密驗證工作階段。</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ 此聊天室中有未驗證的裝置,它們將無法解密您傳送的訊息。</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">切莫向此聊天室中未經驗證的工作階段傳送加密訊息。</string>
+    <string name="action_got_it">知道了</string>
 </resources>
\ No newline at end of file

From 9fe12cfdffc8b7c3fa4c1918c2a86ab53d01610d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sveinn=20=C3=AD=20Felli?= <sv1@fellsnet.is>
Date: Thu, 6 Oct 2022 16:34:42 +0000
Subject: [PATCH 056/400] Translated using Weblate (Icelandic)

Currently translated at 85.3% (2085 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/is/
---
 .../src/main/res/values-is/strings.xml        | 55 ++++++++++++++++++-
 1 file changed, 52 insertions(+), 3 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-is/strings.xml b/library/ui-strings/src/main/res/values-is/strings.xml
index 69191e1741..ceb4d614de 100644
--- a/library/ui-strings/src/main/res/values-is/strings.xml
+++ b/library/ui-strings/src/main/res/values-is/strings.xml
@@ -152,7 +152,7 @@
     <string name="settings_version">Útgáfa</string>
     <string name="settings_olm_version">Útgáfa olm</string>
     <string name="settings_app_term_conditions">Skilmálar og kvaðir</string>
-    <string name="settings_third_party_notices">Athugasemdir frá þriðja aðila</string>
+    <string name="settings_third_party_notices">Tilkynningar frá utanaðkomandi aðilum</string>
     <string name="settings_copyright">Höfundarréttur</string>
     <string name="settings_privacy_policy">Meðferð persónuupplýsinga</string>
     <string name="settings_clear_cache">Hreinsa skyndiminni</string>
@@ -1218,7 +1218,7 @@
     <string name="room_list_quick_actions_notifications_all_noisy">Öll skilaboð (hávært)</string>
     <string name="content_reported_as_inappropriate_title">Tilkynnt sem óviðeigandi</string>
     <string name="content_reported_as_spam_title">Tilkynnt sem ruslpóstur</string>
-    <string name="content_reported_title">Efni tilkynnt</string>
+    <string name="content_reported_title">Efni kært</string>
     <string name="report_content_custom_submit">KÆRA</string>
     <string name="report_content_custom_hint">Ástæður fyrir kæru á þessu efni</string>
     <string name="report_content_custom_title">Kæra þetta efni</string>
@@ -1990,7 +1990,7 @@
     <string name="seen_by">Séð af</string>
     <string name="ftue_personalize_skip_this_step">Sleppa þessu skrefi</string>
     <string name="ftue_personalize_submit">Vista og halda áfram</string>
-    <string name="ftue_personalize_complete_subtitle">Farðu hvenær sem er í stillingarnar til að breyta notandasniðinu þínu.</string>
+    <string name="ftue_personalize_complete_subtitle">Farðu hvenær sem er í stillingarnar til að breyta notandasniðinu þínu</string>
     <string name="ftue_personalize_complete_title">Nú ertu tilbúin(n)!</string>
     <string name="ftue_personalize_lets_go">Hefjumst handa</string>
     <string name="ftue_profile_picture_subtitle">Þú getur breytt þessu hvenær sem er</string>
@@ -2214,4 +2214,53 @@
     <string name="explore_rooms">Kanna spjallrásir</string>
     <string name="create_room">Búa til spjallrás</string>
     <string name="start_chat">Hefja spjall</string>
+    <string name="settings_discovery_no_terms">Auðkennisþjónninn sem þú valdir er ekki með neina þjónustuskilmála. Ekki halda áfram nema þú treystir eiganda netþjónsins</string>
+    <string name="settings_troubleshoot_test_token_registration_failed">Mistókst að skrá FCM-teikn á heimaþjóninn:
+\n%1$s</string>
+    <string name="settings_troubleshoot_test_token_registration_success">Það tókst að skrá FCM-teikn á heimaþjóninn.</string>
+    <string name="settings_troubleshoot_diagnostic_failure_status_with_quickfix">Ein eða fleiri prófanir mistókust, prófaðu tillögur að lagfæringum.</string>
+    <string name="error_threepid_auth_failed">Gakktu úr skugga um að þú hafir smellt á tengilinn í tölvupóstinum sem við sendum þér.</string>
+    <string name="space_permissions_notice_read_only">Þú hefur ekki heimild til að uppfæra þau hlutverk sem krafist er til að breyta ýmsum þáttum svæðisins</string>
+    <string name="room_permissions_notice_read_only">Þú hefur ekki heimild til að uppfæra þau hlutverk sem krafist er til að breyta ýmsum þáttum spjallrásarinnar</string>
+    <string name="space_permissions_notice">Veldu þau hlutverk sem krafist er til að breyta ýmsum þáttum svæðisins</string>
+    <string name="room_permissions_notice">Veldu þau hlutverk sem krafist er til að breyta ýmsum þáttum spjallrásarinnar</string>
+    <string name="room_unsupported_e2e_algorithm_as_admin">Dulritun er rangt stillt þannig að þú getur ekki sent skilaboð. Smelltu til að opna stillingar.</string>
+    <string name="room_unsupported_e2e_algorithm">Dulritun er rangt stillt þannig að þú getur ekki sent skilaboð. Hafðu samband við einhvern stjórnanda til að koma dulritun í lag.</string>
+    <string name="space_participants_unban_prompt_msg">Afbönnun á þessum notanda mun gera viðkomandi kleift að taka þátt aftur í svæðinu.</string>
+    <string name="room_participants_unban_prompt_msg">Afbönnun á þessum notanda mun gera viðkomandi kleift að taka þátt aftur í spjallrásinni.</string>
+    <string name="space_participants_ban_prompt_msg">Bann á notanda mun henda honum út af þessu svæði og koma í veg fyrir að viðkomandi komi aftur.</string>
+    <string name="space_participants_remove_prompt_msg">Notandinn verður fjarlægður af þessu svæði.
+\n
+\nTil koma í veg fyrir að viðkomandi komi aftur, ætti frekar að banna hann.</string>
+    <string name="room_participants_remove_prompt_msg">Notandinn verður fjarlægður af þessari spjallrás.
+\n
+\nTil koma í veg fyrir að viðkomandi komi aftur, ætti frekar að banna hann.</string>
+    <string name="room_participants_action_cancel_invite_prompt_msg">Ertu viss um að þú viljir hætta við boðið til þessa notanda\?</string>
+    <string name="room_participants_action_unignore_prompt_msg">Afhunsun á þessum notanda mun sýna öll skilaboð frá viðkomandi aftur.</string>
+    <string name="room_participants_action_ignore_prompt_msg">Að hunsa þennan notanda mun fjarlægja skilaboð frá viðkomandi í þeim spjallrásum sem þið eigið sameiginlegar.
+\n
+\nÞú getur afturkallað þessa aðgerð hvenær sem er í almennu stillingunum.</string>
+    <string name="room_participants_power_level_demote_warning_prompt">Þú getur ekki afturkallað þessa aðgerð, þar sem þú ert að lækka sjálfa/n þig í tign, og ef þú ert síðasti notandinn með nógu mikil völd á þessari spjallrás, verður ómögulegt að ná aftur stjórn á henni.</string>
+    <string name="e2e_re_request_encryption_key_dialog_content">Ræstu ${app_name} á öðru tæki sem getur afkóðað skilaboðin og síðan sent dulritunarlyklana yfir í þessa setu.</string>
+    <string name="e2e_re_request_encryption_key">Biðja aftur um dulritunarlykla frá hinum setunum þínum.</string>
+    <string name="invites_empty_message">Þetta er þar sem nýjar beiðnir og boðsgestir birtast.</string>
+    <string name="space_list_empty_message">Svæði eru ný leið til að hópa fólk og spjallrásir. Útbúðu svæði til að komast í gang.</string>
+    <string name="sign_out_bottom_sheet_warning_backup_not_active">Öryggisafritun dulritunarlykla ætti að vera virk í öllum setunum þínum til að koma í veg fyrir að þú getir tapað aðgangi að dulrituðu skilaboðunum þínum.</string>
+    <string name="initial_sync_request_reason_unignored_users">- Sumir tengiliðir hafa verið afhunsaðir</string>
+    <string name="initial_sync_request_content">${app_name} þarf að hreinsa skyndiminnið til að haldast uppfært, af eftirfarandi ástæðu:
+\n%s\?
+\n
+\nAthugaðu að þessi aðgerð mun endurræsa forritið og það getur tekið nokkurn tíma.</string>
+    <string name="a11y_collapse_space_children">Fella saman undirsvæði %s</string>
+    <string name="a11y_expand_space_children">Fella út undirsvæði %s</string>
+    <string name="ftue_auth_password_reset_email_confirmation_subtitle">Farðu eftir leiðbeiningunum sem sendar voru á %s</string>
+    <string name="ftue_auth_email_verification_footer">Fékkstu ekki tölvupóst\?</string>
+    <string name="ftue_auth_email_verification_subtitle">Farðu eftir leiðbeiningunum sem sendar voru á %s</string>
+    <string name="ftue_auth_new_password_subtitle">Hafðu það að minnsta kosti 8 stafa langt.</string>
+    <string name="ftue_auth_reset_password_email_subtitle">%s mun senda þér staðfestingartengil</string>
+    <string name="ftue_auth_phone_subtitle">%s þarf að sannreyna notandaaðganginn þinn</string>
+    <string name="ftue_auth_email_subtitle">%s þarf að sannreyna notandaaðganginn þinn</string>
+    <string name="ftue_auth_terms_subtitle">Endilega lestu í gegnum stefnur og skilmála fyrir %s</string>
+    <string name="ftue_auth_terms_title">Stefnur netþjónsins</string>
+    <string name="ftue_auth_choose_server_ems_subtitle">Element Matrix Services (EMS) er afkastamikil og áreiðanleg hýsingarþjónusta fyrir hraðvirk og örugg samskipti í rauntíma. Skoðaðu hvernig við förum að því á <a href="${ftue_ems_url}">element.io/ems</a></string>
 </resources>
\ No newline at end of file

From ec72300f4928255ae69aa418f553872d57f20a38 Mon Sep 17 00:00:00 2001
From: Danial Behzadi <dani.behzi@ubuntu.com>
Date: Fri, 7 Oct 2022 17:31:40 +0000
Subject: [PATCH 057/400] Translated using Weblate (Persian)

Currently translated at 99.5% (2433 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/fa/
---
 .../src/main/res/values-fa/strings.xml        | 48 ++++++++++++-------
 1 file changed, 32 insertions(+), 16 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-fa/strings.xml b/library/ui-strings/src/main/res/values-fa/strings.xml
index 9012bc2ebe..1191e98df9 100644
--- a/library/ui-strings/src/main/res/values-fa/strings.xml
+++ b/library/ui-strings/src/main/res/values-fa/strings.xml
@@ -155,7 +155,7 @@
     <string name="notice_room_guest_access_can_join_by_you">به میهمانان اجازهٔ پیوستن به گروه دادید.</string>
     <string name="notice_room_guest_access_forbidden_by_you">میمهانان را از پیوستن به گروه بازداشتید.</string>
     <string name="notice_end_to_end_ok_by_you">رمزنگاری سرتاسری را روشن کردید.</string>
-    <string name="notice_end_to_end_unknown_algorithm_by_you">رمزنگاری سرتاسری را روشن کردید (الگوریتم ناشناخته %1$s).</string>
+    <string name="notice_end_to_end_unknown_algorithm_by_you">رمزنگاری سرتاسری را روشن کردید (الگوریتم تشخیص‌داده‌نشده %1$s).</string>
     <string name="notice_direct_room_guest_access_forbidden_by_you">مهمان‌ها را از پیوستن به اتاق بازداشتید.</string>
     <string name="notice_direct_room_guest_access_forbidden">%1$s مهمان‌ها را از پیوستن به اتاق بازداشت.</string>
     <string name="notice_direct_room_guest_access_can_join_by_you">به مهمان‌ها اجازه دادید به این‌جا بپیوندند.</string>
@@ -659,7 +659,7 @@
     <string name="settings_discovery_manage">مدیریت تنظیمات کشفتان.</string>
     <string name="settings_unignore_user">نمایش همهٔ پیام‌ها از %s؟</string>
     <string name="settings_emails_and_phone_numbers_title">رایانامه‌ها و شماره تلفن‌ها</string>
-    <string name="settings_emails_and_phone_numbers_summary">مدیریت رایانامه‌ها و شماره تلفن‌های پیوسته به حساب ماتریکستان</string>
+    <string name="settings_emails_and_phone_numbers_summary">مدیریت نشانی‌های رایانامه و شماره تلفن‌های پیوسته به حساب ماتریکستان</string>
     <string name="media_saving_period_3_days">۳ روز</string>
     <string name="media_saving_period_1_week">۱ هفته</string>
     <string name="media_saving_period_1_month">۱ ماه</string>
@@ -822,7 +822,7 @@
     <string name="add_identity_server">پیکربندی کارساز هویت</string>
     <string name="change_identity_server">تغییر کارساز هویت</string>
     <string name="settings_discovery_emails_title">نشانی‌های رایانامهٔ قابل‌کشف</string>
-    <string name="settings_discovery_no_mails">گزینه‌های کشف به محض افزودن یک رایانامه، ظاهر خواهند شد.</string>
+    <string name="settings_discovery_no_mails">گزینه‌های کشف به محض افزودن یک نشانی رایانامه، ظاهر خواهند شد.</string>
     <string name="settings_discovery_no_msisdn">گزینه‌های کشف به محض افزودن یک شماره تلفن، ظاهر خواهند شد.</string>
     <string name="settings_discovery_msisdn_title">شماره تلفن‌های قابل‌کشف</string>
     <string name="labs_allow_extended_logging">به کار انداختن گزارش‌های پرگو.</string>
@@ -1234,7 +1234,7 @@
     <string name="error_threepid_auth_failed">مطمئن شوید لینک فعال‌سازی‌ای را که به ایمیل شما ارسال شده، باز کرده‌اید.</string>
     <string name="settings_remove_three_pid_confirmation_content">برداشتن %s؟</string>
     <string name="settings_phone_numbers">شماره تلفن‌ها</string>
-    <string name="settings_emails_empty">هیچ رایانامه‌ای به حسابتان افزوده نشده</string>
+    <string name="settings_emails_empty">هیچ نشانی رایانامه‌ای به حسابتان افزوده نشده</string>
     <string name="settings_emails">نشانی‌های رایانامه</string>
     <string name="settings_phone_number_empty">هیچ شماره تلفنی به حسابتان افزوده نشده</string>
     <string name="search_no_results">نتیجه‌ای در پی نداشت</string>
@@ -1397,7 +1397,7 @@
     <string name="login_signup_to">ثبت نام در %1$s</string>
     <string name="login_msisdn_error_other">شماره تلفن نامعتبر است. لطفا آن را بررسی کنید</string>
     <string name="login_msisdn_confirm_notice">ما یک کد فعال‌سازی به %1$s ارسال کردیم. لطفا آن را وارد کنید.</string>
-    <string name="login_set_email_notice">برای بازیابی حساب خود یک ایمیل تنظیم کنید. بعداً، در صورتی که تمایل داشتید می‌توانید به افراد اجازه دهید با ایمیل، شما را پیدا کنند.</string>
+    <string name="login_set_email_notice">برای بازیابی حسابتان، نشانی رایانامه‌ای تنظیم کنید. بعداً می‌توانید بگذارید افرادی که می‌شناسید، با این نشانی بیابندتان.</string>
     <string name="login_set_msisdn_notice">یک شماره تلفن تنظیم کنید تا در صورتی که تمایل دارید، به افراد اجازه پیدا کردن شما را با استفاده از آن بدهد.</string>
     <string name="login_reset_password_cancel_confirmation_content">گذرواژه شما هنوز تغییر نکرده‌است.
 \n
@@ -1407,12 +1407,12 @@
     <string name="login_reset_password_mail_confirmation_notice_2">برای تایید گذرواژه جدید لینک ارسالی را باز کرده، سپس روی متن زیر کلیک کنید.</string>
     <string name="login_reset_password_mail_confirmation_notice">یک ایمیل تائید به %1$s ارسال شد.</string>
     <string name="login_reset_password_mail_confirmation_title">صندوق ایمیل خود را بررسی کنید</string>
-    <string name="login_reset_password_error_not_found">این ایمیل به هیچ حسابی متصل نشده‌است</string>
+    <string name="login_reset_password_error_not_found">این نشانی رایانامه به هیچ حسابی پیوند داده نشده</string>
     <string name="login_reset_password_warning_content">با تغییر گذرواژه ، کلیدهای رمزگذاری سرتاسر در تمام نشست‌های شما تغییر کرده و تاریخچه گفتگوی رمزشده‌ی شما غیرقابل خواندن می‌شود. قبل از تغییر گذرواژه، کلید امنیتی یا کلید پشتیبان را وارد کرده و یا کلیدهای اتاق خود را از نشست دیگری استخراج کنید.</string>
     <string name="login_reset_password_submit">بعدی</string>
     <string name="login_reset_password_notice">برای تأیید تنظیم گذرواژه‌ی جدید ، یک ایمیل تأیید به آدرس ایمیل شما ارسال خواهد شد.</string>
     <string name="login_reset_password_on">بازنشانی گذرواژه در %1$s</string>
-    <string name="login_login_with_email_error">این ایمیل به هیچ حسابی مرتبط نیست.</string>
+    <string name="login_login_with_email_error">این نشانی رایانامه به هیچ حسابی مرتبط نیست.</string>
     <string name="login_registration_not_supported">برنامه قادر به ایجاد حساب در این سرور نیست.
 \n
 \nآیا می خواهید با استفاده از مرورگر حساب کاربری بسازید؟</string>
@@ -1427,7 +1427,7 @@
     <string name="login_server_text">درست مانند ایمیل، حساب‌های کاربری یک خانه دارند؛ اگرچه می توانید با هر کسی که دوست دارید، صحبت کنید</string>
     <string name="login_server_title">کارسازی برگزینید</string>
     <string name="login_splash_submit">شروع کنید</string>
-    <string name="login_splash_text3">تجارب خود را گسترش داده و شخصی‌سازی کنید</string>
+    <string name="login_splash_text3">تجربه‌هایتان را گسترش داده و شخصی‌سازی کنید</string>
     <string name="login_splash_text2">گفتگوهای خصوصی خود را با رمزگذاری محافظت کنید</string>
     <string name="login_splash_text1">با افراد به صورت مستقیم و یا در قالب گروه گپ بزنید</string>
     <string name="login_splash_title">صاحب گفتگوهای خود باشید.</string>
@@ -1484,8 +1484,8 @@
     <string name="settings_discovery_please_enter_server">لطفاً نشانی کارساز هویت را وارد کنید</string>
     <string name="settings_discovery_bad_identity_server">نتوانست به کارساز هویت وصل شود</string>
     <string name="settings_discovery_enter_identity_server">یک نشانی کارساز هویت وارد کنید</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">ما یک ایمیل تأیید به %s ارسال کردیم، ابتدا ایمیل خود را بررسی کرده و روی لینک تأیید کلیک کنید</string>
-    <string name="settings_discovery_confirm_mail">ما یک ایمیل تأیید به %s ارسال کردیم، ایمیل خود را بررسی کرده و روی لینک تأیید کلیک کنید</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">رایانامه‌ای به %s فرستادیم. لطفاً نخست رایانامه‌تان را بررسی کرده و روی پیوند تأیید کلیک کنید</string>
+    <string name="settings_discovery_confirm_mail">رایانامه‌ای به %s فرستادیم. رایانامه‌تان را بررسی کرده و روی پیوند تأیید کلیک کنید</string>
     <string name="settings_discovery_disconnect_identity_server_info">قطع ارتباط با سرور هویت‌سنجی به این معنی است که توسط کاربران دیگر قابل شناسایی نخواهید بود و نمی توانید دیگران را از طریق ایمیل یا تلفن دعوت کنید.</string>
     <string name="settings_discovery_identity_server_info_none">در حال استفاده از کارساز هویتی نیستید. برای کشق و قابل کشف بودن به دست آشنایان موجودی که می‌شناسید، یک کارساز هویت در زیر پیکربندی کنید.</string>
     <string name="settings_discovery_identity_server_info">دارید برای کشف و قابل کشف بودن به دست آشنایان موجودی که می‌شناسید از %1$s استفاده می‌کنید.</string>
@@ -1680,7 +1680,7 @@
     <string name="bottom_sheet_setup_secure_backup_security_phrase_subtitle">عبارت رمزی که فقط خودتان می‌دانید را وارد کرده و کلیدی برای پشتیبان تولید کنید.</string>
     <string name="identity_server_set_alternative_notice">به هنوتم جایگزین، می‌توانید نشامی هر کارساز هویت دیگری را وارد کنید</string>
     <string name="identity_server_set_default_notice">کارساز خانگیتان (%1$s) پیشنهاد استفاده از %2$s برای کارساز هویتتان را می‌دهد</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">برای محرمانگیتان، المنت تنها از فرستادن شماره تلفن و رایانامه‌های کاربری در هم ریخته پشتیبانی می‌کند.</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">برای محرمانگیتان، ${app_name} تنها از فرستادن شماره‌های تلفن و نشانی‌های رایانامهٔ کاربری در هم ریخته پشتیبانی می‌کند.</string>
     <string name="identity_server_error_outdated_home_server">این عملیات ممکن نیست. کارساز خانگی منقضی شده است.</string>
     <string name="invite_users_to_room_failure">نتوانستیم کاربران را دعوت کنیم. لطفاً کاربرانی که می‌خواهید دعوت کنید را بررسی کرده و دوباره تلاش کنید.</string>
     <string name="create_room_dm_failure">نتوانستیم پیامتان را ایجاد کنیم. لطفاً کاربرانی که می‌خواهید دعوت کنید را بررسی کرده و دوباره تلاش کنید.</string>
@@ -1747,7 +1747,7 @@
         <item quantity="one">دستگاهی را که می‌توانید با استفاده از آن‌ها خود را تایید کنید نشان بده</item>
         <item quantity="other">%d دستگاهی را که می‌توانید با استفاده از آن‌ها خود را تایید کنید نشان بده</item>
     </plurals>
-    <string name="settings_discovery_consent_notice_on">رضاییتان را برای فرستادن رایانامه‌ها و شماره‌تلفن‌ها به این کارساز هویت به منظور کشف دیگر کاربران از آشنایانتان، داده‌اید.</string>
+    <string name="settings_discovery_consent_notice_on">رضاییتان را برای فرستادن نشانی‌های رایانامه و شماره‌های تلفن به این کارساز هویت به منظور کشف دیگر کاربران از آشنایانتان، داده‌اید.</string>
     <string name="room_preview_not_found">در حال حاضر این اتاق قابل دسترسی نیست.
 \nبعدا دوباره تلاش کنید، یا از مدیر اتاق بخواهید بررسی کند که آیا دسترسی دارید.</string>
     <string name="room_alias_action_publish">انتشار این نشانی</string>
@@ -2141,7 +2141,7 @@
     <string name="settings_notification_mentions_and_keywords">اشاره‌ها و کلیدواژگان</string>
     <string name="settings_notification_default">آگاهی‌های پیش‌گزیده</string>
     <string name="link_this_email_with_your_account">%s در تنظیمات برای دریافت مستقیم دعوت‌ها در المنت.</string>
-    <string name="link_this_email_settings_link">این رایانامه را به حسابتان پیوند دهید</string>
+    <string name="link_this_email_settings_link">پیوند این نشانی رایانامه به حسابتان</string>
     <string name="this_invite_to_this_space_was_sent">این دعوت به این فضا به %s فرستاده شده که با حسابتان در ارتباط نیست</string>
     <string name="this_invite_to_this_room_was_sent">این دعوت به این اتاق به %s فرستاده شده که با حسابتان در ارتباط نیست</string>
     <string name="upgrade_public_room_from_to">این اتاق را از %1$s به %2$s ارتقا خواهید داد.</string>
@@ -2211,7 +2211,7 @@
     <string name="room_settings_space_access_title">دسترسی فضا</string>
     <string name="room_settings_access_rules_pref_dialog_title">چه کسی می‌تواند دسترسی داشته باشد؟</string>
     <string name="settings_notification_emails_enable_for_email">به کار انداختن آگاهی‌های رایانامه‌ای برای %s</string>
-    <string name="settings_notification_emails_no_emails">برای دریافت رایانامه با آگاهی، لطفاً رایانامه‌ای را به حساب ماتریکستان وصل کنید</string>
+    <string name="settings_notification_emails_no_emails">برای دریافت رایانامه با آگاهی، لطفاً نشانی رایانامه‌ای را به حساب ماتریکستان وصل کنید</string>
     <string name="settings_notification_emails_category">آگاهی رایانامه‌ای</string>
     <string name="room_permissions_upgrade_the_space">ارتقای فضا</string>
     <string name="room_permissions_change_space_name">تغییر نام فضا</string>
@@ -2258,13 +2258,13 @@
     <string name="create_poll_question_title">پرسش یا موضوع نظرسنجی</string>
     <string name="create_poll_title">ایجاد نظرسنجی</string>
     <string name="attachment_type_poll">نظرسنجی</string>
-    <string name="identity_server_consent_dialog_title_2">فرستادن رایانامه‌ّا و شماره‌های تلفن به %s</string>
+    <string name="identity_server_consent_dialog_title_2">فرستادن نشانی‌های رایانامه و شماره‌های تلفن به %s</string>
     <string name="settings_discovery_consent_notice_off_2">آشنایانتان خصوصی هستند. برای کشف کاربران از آشنایانتان، نیاز به اجازه‌تان برای فرستادن اطّلاعات آشنا به کارساز هویتتان داریم.</string>
     <string name="shortcut_disabled_reason_sign_out">نشست خارج شده است!</string>
     <string name="shortcut_disabled_reason_room_left">اتاق ترک شده است!</string>
     <string name="identity_server_consent_dialog_content_question">با فرستادن این اطّلاعات موافقید؟</string>
     <string name="action_not_now">اکنون نه</string>
-    <string name="identity_server_consent_dialog_content_3">برای کشف آشنایان موجود، لازم است اطلاعات آشنایان (رایانامه‌ها و شماره تلفن‌ها) را به کارساز هویتتان بفرستید. برای محرمانگیتان، داده‌هایتان را پیش از فرستادن، در هم می‌ریزیم.</string>
+    <string name="identity_server_consent_dialog_content_3">برای کشف آشنایان موجود، لازم است اطلاعات آشنایان (نشانی‌های رایانامه و شماره‌های تلفن) را به کارساز هویتتان بفرستید. برای محرمانگیتان، داده‌هایتان را پیش از فرستادن، در هم می‌ریزیم.</string>
     <string name="analytics_opt_in_content">با هم‌رسانی داده‌ّای استفادهٔ ناشناس، در تشخیص مشکل‌ها و بهبود المنت یاریمان کنید. برای درک چگونگی استفادهٔ مردم از چندین افزاره، شناسه‌ای کاتوره‌ای بین افزاره‌هایتان هم‌رسانی خواهیم کرد.
 \n
 \nمی‌توانید از %s قوانینمان را بخوانید.</string>
@@ -2705,4 +2705,20 @@
         <item quantity="one">غیرفعّال برای ۱ روز یا بیش‌تر</item>
         <item quantity="other">غیرفعّال برای %1$d روز یا بیش‌تر</item>
     </plurals>
+    <string name="device_manager_other_sessions_description_unverified_current_session">تأیید نشده · نشست کنونیتان</string>
+    <string name="key_authenticity_not_guaranteed">اعتبار این پیام رمز شده نمی‌تواند روی این افزاره تأیید شود.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">در این اتاق، هرگز پیام‌های رمز شده به نشست‌های تأیید نشده فرستاده نشود.</string>
+    <string name="device_manager_learn_more_session_rename_title">تغییر نام نشست‌ها</string>
+    <string name="device_manager_learn_more_sessions_verified_title">نشست‌های تأییدشده</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">نشست‌های تأییدنشده</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">نشست‌های غیرفعّال</string>
+    <string name="device_manager_session_rename_warning">لطفاً آگاه باشید که نام‌های نشست برای افرادی که با آن‌ها در تماسید نیز نشان داده می‌شود.</string>
+    <string name="device_manager_session_rename_description">نام‌های نشست شخصی به نظم دادن آسان‌تر افزاره‌هایتان کمک می‌کند.</string>
+    <string name="device_manager_session_rename_edit_hint">نام نشست</string>
+    <string name="device_manager_session_rename">تغییر نام نشست</string>
+    <string name="device_manager_session_overview_signout">خروج از این نشست</string>
+    <string name="settings_security_incognito_keyboard_title">صفحه‌کلید ناشناس</string>
+    <string name="command_description_devtools">گشودن صفحهٔ ابزارهای توسعه‌دهنده</string>
+    <string name="labs_enable_deferred_dm_title">به کار انداختن پیام‌های مستقیم تعویقی</string>
+    <string name="action_got_it">گرفتم</string>
 </resources>
\ No newline at end of file

From 9f8f14ce3050a5a179fb7f97b57bc0bc502659ef Mon Sep 17 00:00:00 2001
From: waclaw66 <waclaw66@seznam.cz>
Date: Fri, 7 Oct 2022 05:51:41 +0000
Subject: [PATCH 058/400] Translated using Weblate (Czech)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/
---
 .../src/main/res/values-cs/strings.xml        | 43 ++++++++++++++++---
 1 file changed, 38 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-cs/strings.xml b/library/ui-strings/src/main/res/values-cs/strings.xml
index 1983036271..b23f5f102a 100644
--- a/library/ui-strings/src/main/res/values-cs/strings.xml
+++ b/library/ui-strings/src/main/res/values-cs/strings.xml
@@ -1011,7 +1011,7 @@
     <string name="settings_discovery_no_msisdn">Volby pro nalezení se ukážou, jakmile doplníte telefonní číslo.</string>
     <string name="settings_discovery_disconnect_identity_server_info">Odpojení od serveru identit bude znamenat, že Vás jiní uživatelé nebudou moci najít a Vy nebudete moci pozvat druhé pomocí emailu nebo telefonního čísla.</string>
     <string name="settings_discovery_msisdn_title">Telefonní čísla pro nalezení</string>
-    <string name="settings_discovery_confirm_mail">Poslali jsme Vám potvrzovací email na %s, podívejte se do emailu a klikněte na protvrzovací odkaz</string>
+    <string name="settings_discovery_confirm_mail">Poslali jsme vám email na %s, podívejte se do něj a klikněte na protvrzovací odkaz</string>
     <string name="settings_discovery_enter_identity_server">Zadejte URL serveru pro identity</string>
     <string name="settings_discovery_bad_identity_server">Nemohl jsem se spojit se serverem pro identity</string>
     <string name="settings_discovery_please_enter_server">Prosím, zadejte url serveru pro identity</string>
@@ -1084,7 +1084,7 @@
     <string name="login_splash_title">Je to Vaše konverzace. Vlastněte ji.</string>
     <string name="login_splash_text1">Chatujte s lidmi přímo nebo ve skupinách</string>
     <string name="login_splash_text2">Udržujte konverzace soukromé pomocí šifrování</string>
-    <string name="login_splash_text3">Rozšiřte a upravte si svůj zážitek</string>
+    <string name="login_splash_text3">Rozšiřte a upravte si možnosti</string>
     <string name="login_splash_submit">Můžeme začít</string>
     <string name="login_server_title">Vybrat server</string>
     <string name="login_server_text">Jako email, účty mají jeden domov, ačkoli můžete mluvit s kýmkoli</string>
@@ -1136,7 +1136,7 @@
 \n
 \nZastavit proces změny hesla\?</string>
     <string name="login_set_email_title">Nastavit emailovou adresu</string>
-    <string name="login_set_email_notice">Nastavte emailovou adresu pro obnově svého účtu. Později můžete volitelně dovolit lidem, které znáte, aby Vás podle emailu nalezli.</string>
+    <string name="login_set_email_notice">Nastavte emailovou adresu pro obnovu svého účtu. Později můžete volitelně dovolit lidem, které znáte, aby Vás podle emailu nalezli.</string>
     <string name="login_set_email_mandatory_hint">Email</string>
     <string name="login_set_email_optional_hint">Email (volitelné)</string>
     <string name="login_set_email_submit">Dále</string>
@@ -1440,7 +1440,7 @@
     <string name="event_redacted">Zpráva smazána</string>
     <string name="settings_show_redacted">Ukázat odstraněné zprávy</string>
     <string name="settings_show_redacted_summary">Zobrazit zástupný symbol za odstraněné zprávy</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Poslali jsme Vám potvrzovací email na %s, podívejte se nejdříve do emailu a klikněte na protvrzovací odkaz</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Poslali jsme vám email na %s, podívejte se nejdříve do emailu a klikněte na protvrzovací odkaz</string>
     <string name="settings_text_message_sent_wrong_code">Ověřovací kód není správný.</string>
     <string name="uploads_media_title">MÉDIA</string>
     <string name="uploads_media_no_result">V této místnosti nejsou žádná média</string>
@@ -2025,7 +2025,7 @@
     <string name="space_type_public">Veřejný</string>
     <string name="create_spaces_private_teammates">Privátní prostor pro Vás a Vaše kolegy</string>
     <string name="create_spaces_me_and_teammates">Já a kolegové</string>
-    <string name="create_spaces_organise_rooms">Privátní prostor k organizaci Vašich místností</string>
+    <string name="create_spaces_organise_rooms">Soukromý prostor pro uspořádání vašich místností</string>
     <string name="create_spaces_just_me">Jen já</string>
     <string name="create_spaces_make_sure_access">Ujistěte se, že ti správní lidé mají přístup do %s.</string>
     <string name="create_spaces_who_are_you_working_with">S kým pracujete\?</string>
@@ -2764,4 +2764,37 @@
     <string name="labs_enable_deferred_dm_title">Povolit odložené přímé zprávy</string>
     <string name="labs_enable_new_app_layout_summary">Zjednodušený Element s volitelnými kartami</string>
     <string name="labs_enable_new_app_layout_title">Povolit nový vzhled</string>
+    <string name="device_manager_learn_more_session_rename">Ostatní uživatelé v přímých zprávách a místnostech, ke kterým se připojíte, si mohou prohlédnout úplný seznam vašich relací.
+\n
+\nTo jim poskytuje jistotu, že s vámi skutečně mluví, ale také to znamená, že mohou vidět název relace, který zde zadáte.</string>
+    <string name="device_manager_learn_more_session_rename_title">Přejmenování relací</string>
+    <string name="device_manager_learn_more_sessions_verified">Ověřené relace se přihlásily pomocí vašich přihlašovacích údajů a poté byly ověřeny buď pomocí vaší zabezpečené přístupové fráze, nebo křížovým ověřením.
+\n
+\nTo znamená, že uchovávají šifrovací klíče pro vaše předchozí zprávy a potvrzují ostatním uživatelům, se kterými komunikujete, že tyto relace jste skutečně vy.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Ověřené relace</string>
+    <string name="device_manager_learn_more_sessions_unverified">Neověřené relace jsou relace, které se přihlásily pomocí vašich přihlašovacích údajů, ale nebyly křížově ověřeny.
+\n
+\nMěli byste si být obzvláště jisti, že tyto relace rozpoznáte, protože by mohly představovat neoprávněné použití vašeho účtu.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Neověřené relace</string>
+    <string name="device_manager_learn_more_sessions_inactive">Neaktivní relace jsou relace, které jste po určitou dobu nepoužili, ale nadále dostávají šifrovací klíče.
+\n
+\nOdstranění neaktivních relací zvyšuje zabezpečení a výkon a usnadňuje identifikaci podezřelé nové relace.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Neaktivní relace</string>
+    <string name="device_manager_session_rename_warning">Uvědomte si, že jména relací jsou viditelná i pro osoby, se kterými komunikujete.</string>
+    <string name="device_manager_session_rename_description">Vlastní názvy relací vám pomohou snadněji rozpoznat vaše zařízení.</string>
+    <string name="device_manager_session_rename_edit_hint">Název relace</string>
+    <string name="device_manager_session_rename">Přejmenovat relaci</string>
+    <string name="device_manager_session_overview_signout">Odhlásit se z této relace</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Neověřeno · Vaše aktuální relace</string>
+    <string name="tooltip_attachment_voice_broadcast">Zahájit hlasové vysílání</string>
+    <string name="key_authenticity_not_guaranteed">Pravost této šifrované zprávy nelze v tomto zařízení zaručit.</string>
+    <string name="settings_security_incognito_keyboard_summary">Požadujte, aby klávesnice neaktualizovala žádné personalizované údaje, jako je historie psaní a slovník, na základě toho, co jste napsali v konverzacích. Upozorňujeme, že některé klávesnice nemusí toto nastavení respektovat.</string>
+    <string name="settings_security_incognito_keyboard_title">Inkognito klávesnice</string>
+    <string name="command_description_table_flip">Přidá znaky (╯°□°)╯︵ ┻━┻ před zprávy ve formátu obyčejného textu</string>
+    <string name="attachment_type_voice_broadcast">Hlasové vysílání</string>
+    <string name="command_description_devtools">Otevřít nástroje pro vývojáře</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 V nastavení zabezpečení jste povolili šifrování pouze do ověřených relací pro všechny místnosti.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ V této místnosti jsou neověřená zařízení, která nebudou schopna dešifrovat odeslané zprávy.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Nikdy neodesílat šifrované zprávy do neověřených relací v této místnosti.</string>
+    <string name="action_got_it">Rozumím</string>
 </resources>
\ No newline at end of file

From c54c3b1a7c914147d07a4c8bc5a6b4a7b80389bf Mon Sep 17 00:00:00 2001
From: LinAGKar <linus.kardell@gmail.com>
Date: Fri, 7 Oct 2022 19:13:04 +0000
Subject: [PATCH 059/400] Translated using Weblate (Swedish)

Currently translated at 92.3% (72 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/sv/
---
 fastlane/metadata/android/sv-SE/changelogs/40105020.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/sv-SE/changelogs/40105020.txt b/fastlane/metadata/android/sv-SE/changelogs/40105020.txt
new file mode 100644
index 0000000000..66af38ec60
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Ny applayout aktiv som förval.
+Full ändringslogg: https://github.com/vector-im/element-android/releases

From b30c91a4d7298987635515b3cebced073bdc3ac9 Mon Sep 17 00:00:00 2001
From: random <dictionary@tutamail.com>
Date: Thu, 6 Oct 2022 12:36:17 +0000
Subject: [PATCH 060/400] Translated using Weblate (Italian)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/it/
---
 .../src/main/res/values-it/strings.xml        | 63 ++++++++++++++-----
 1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-it/strings.xml b/library/ui-strings/src/main/res/values-it/strings.xml
index b2f9fa9238..2b78078ff4 100644
--- a/library/ui-strings/src/main/res/values-it/strings.xml
+++ b/library/ui-strings/src/main/res/values-it/strings.xml
@@ -155,7 +155,7 @@
     <string name="notice_room_guest_access_can_join_by_you">Hai permesso l\'accesso alla stanza per gli ospiti.</string>
     <string name="notice_room_guest_access_forbidden_by_you">Hai impedito l\'accesso alla stanza per gli ospiti.</string>
     <string name="notice_end_to_end_ok_by_you">Hai attivato la crittografia end-to-end.</string>
-    <string name="notice_end_to_end_unknown_algorithm_by_you">Hai attivato la crittografia E2E (algoritmo %1$s sconosciuto).</string>
+    <string name="notice_end_to_end_unknown_algorithm_by_you">Hai attivato la crittografia E2E (algoritmo %1$s non riconosciuto).</string>
     <string name="notice_direct_room_guest_access_forbidden_by_you">Hai impedito l\'accesso alla stanza agli ospiti.</string>
     <string name="notice_direct_room_guest_access_forbidden">%1$s ha impedito l\'accesso alla stanza agli ospiti.</string>
     <string name="notice_direct_room_guest_access_can_join_by_you">Hai permesso l\'accesso agli ospiti.</string>
@@ -955,11 +955,11 @@
     <string name="settings_discovery_identity_server_info">Attualmente stai usando %1$s per trovare altri utenti ed essere a tua volta rintracciabile da loro.</string>
     <string name="settings_discovery_identity_server_info_none">Attualmente non stai usando alcun server d\'identità. Per trovare e farti rintracciare dagli altri utenti, configurane uno qua sotto.</string>
     <string name="settings_discovery_emails_title">Indirizzi email visibili pubblicamente</string>
-    <string name="settings_discovery_no_mails">Le opzioni su come farsi trovare appariranno dopo che avrai aggiunto un\'email.</string>
+    <string name="settings_discovery_no_mails">Le opzioni su come farsi trovare appariranno dopo che avrai aggiunto un indirizzo email.</string>
     <string name="settings_discovery_no_msisdn">Le opzioni su come farsi trovare appariranno dopo che avrai aggiunto un numero di telefono.</string>
     <string name="settings_discovery_disconnect_identity_server_info">Se ti disconnetti dal server d\'identità gli altri utenti non potranno trovarti e tu non potrai invitarli tramite le loro email o numeri di telefono.</string>
     <string name="settings_discovery_msisdn_title">Numeri di telefono visibili pubblicamente</string>
-    <string name="settings_discovery_confirm_mail">Abbiamo inviato un\'email di conferma a %s, controlla la tua posta e clicca sul link di conferma</string>
+    <string name="settings_discovery_confirm_mail">Abbiamo inviato un\'email a %s, controlla la tua posta e clicca sul link di conferma</string>
     <string name="settings_discovery_enter_identity_server">Inserisci un URL di un server d\'identità</string>
     <string name="settings_discovery_bad_identity_server">Impossibile connettersi al server d\'identità</string>
     <string name="settings_discovery_please_enter_server">Inserisci l\'URL del server d\'identità</string>
@@ -1087,7 +1087,7 @@
     <string name="login_registration_not_supported">L\'applicazione non riesce a creare un account su questo Home Server.
 \n
 \nVuoi registrarti usando un client web\?</string>
-    <string name="login_login_with_email_error">Questa email non è associata ad alcun account.</string>
+    <string name="login_login_with_email_error">Questo indirizzo email non è associato ad alcun account.</string>
     <string name="login_reset_password_on">Reimposta la password su %1$s</string>
     <string name="login_reset_password_notice">Per confermare la nuova password ti verrà inviata un\'email di verifica.</string>
     <string name="login_reset_password_submit">Avanti</string>
@@ -1096,7 +1096,7 @@
     <string name="login_reset_password_warning_title">Attenzione!</string>
     <string name="login_reset_password_warning_content">Cambiando la password verranno reimpostate le chiavi crittografiche E2E di tutte le tue sessioni rendendo illeggibile la cronologia delle chat criptate. Prima di reimpostare la password imposta il Backup delle Chiavi o esporta le chiavi della tua stanza da un\'altra sessione.</string>
     <string name="login_reset_password_warning_submit">Continua</string>
-    <string name="login_reset_password_error_not_found">Questa email non è collegata ad alcun account</string>
+    <string name="login_reset_password_error_not_found">Questo indirizzo email non è collegato ad alcun account</string>
     <string name="login_reset_password_mail_confirmation_title">Controlla la tua posta</string>
     <string name="login_reset_password_mail_confirmation_notice">Un\'email di verifica è stata inviata a %1$s.</string>
     <string name="login_reset_password_mail_confirmation_notice_2">Clicca sul link per confermare la tua nuova password. Una volta fatto, clicca sotto.</string>
@@ -1110,7 +1110,7 @@
 \n
 \nFermare il processo di cambio password\?</string>
     <string name="login_set_email_title">Imposta indirizzo email</string>
-    <string name="login_set_email_notice">Imposta un\'email per recuperare il tuo account. Più tardi potrai decidere se permettere alle persone che conosci di trovarti tramite questa email.</string>
+    <string name="login_set_email_notice">Imposta un indirizzo email per recuperare il tuo account. Più tardi potrai decidere se permettere alle persone che conosci di trovarti tramite questo indirizzo.</string>
     <string name="login_set_email_mandatory_hint">Email</string>
     <string name="login_set_email_optional_hint">Email (facoltativa)</string>
     <string name="login_set_email_submit">Avanti</string>
@@ -1448,7 +1448,7 @@
     <string name="event_redacted">Messaggio rimosso</string>
     <string name="settings_show_redacted">Mostra messaggi rimossi</string>
     <string name="settings_show_redacted_summary">Mostra un segnaposto per i messaggi rimossi</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Ti abbiamo inviato un\'email di conferma a %s, controlla la tua posta e clicca il link di conferma</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Abbiamo inviato un\'email a %s, controlla la tua posta e clicca il link di conferma</string>
     <string name="settings_text_message_sent_wrong_code">Il codice di verifica non è corretto.</string>
     <string name="uploads_media_title">MEDIA</string>
     <string name="uploads_media_no_result">In questa stanza non ci sono file multimediali</string>
@@ -1471,7 +1471,7 @@
     <string name="identity_server_error_outdated_home_server">Questa operazione non è possibile. L\'Home Server è obsoleto.</string>
     <string name="identity_server_error_no_identity_server_configured">Prima configura un server d\'identità.</string>
     <string name="identity_server_error_terms_not_signed">Prima accetta le condizioni del server d\'identità nelle impostazioni.</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Per la tua privacy, ${app_name} supporta solo l\'invio di email e numeri di telefono degli utenti in modalità oscurata (hash).</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Per la tua privacy, ${app_name} supporta solo l\'invio di hash degli indirizzi email e dei numeri di telefono degli utenti.</string>
     <string name="identity_server_error_binding_error">L\'associazione è fallita.</string>
     <string name="identity_server_error_no_current_binding_error">Non c\'è alcuna associazione con questo identificativo.</string>
     <string name="identity_server_set_default_notice">Il tuo home server (%1$s) propone di usare %2$s come tuo server d\'identità</string>
@@ -1645,12 +1645,12 @@
     <string name="auth_msisdn_already_defined">Questo numero di telefono è già definito.</string>
     <string name="settings_phone_number_empty">Nessun numero di telefono aggiunto al tuo account</string>
     <string name="settings_emails">Indirizzi email</string>
-    <string name="settings_emails_empty">Nessuna email aggiunta al tuo account</string>
+    <string name="settings_emails_empty">Nessun indirizzo email aggiunto al tuo account</string>
     <string name="settings_phone_numbers">Numeri di telefono</string>
     <string name="settings_remove_three_pid_confirmation_content">Rimuovere %s\?</string>
     <string name="error_threepid_auth_failed">Assicurati di avere cliccato il link nell\'email che ti abbiamo inviato.</string>
     <string name="settings_emails_and_phone_numbers_title">Email e numeri di telefono</string>
-    <string name="settings_emails_and_phone_numbers_summary">Gestisci le email e i numeri di telefono collegati al tuo account Matrix</string>
+    <string name="settings_emails_and_phone_numbers_summary">Gestisci gli indirizzi email e i numeri di telefono collegati al tuo account Matrix</string>
     <string name="settings_text_message_sent_hint">Codice</string>
     <string name="login_msisdn_notice">Si prega di usare il formato internazionale (il numero deve iniziare con \'+\')</string>
     <string name="confirm_your_identity_quad_s">Conferma la tua identità verificando questo accesso, dandogli l\'accesso ai messaggi cifrati.</string>
@@ -1769,7 +1769,7 @@
     <string name="attachment_viewer_item_x_of_y">%1$d di %2$d</string>
     <string name="settings_discovery_consent_action_give_consent">Dai il consenso</string>
     <string name="settings_discovery_consent_action_revoke">Revoca il mio consenso</string>
-    <string name="settings_discovery_consent_notice_on">Hai acconsentito ad inviare email e numeri di telefono a questo server d\'identità per poter rintracciare altri utenti tra i tuoi contatti.</string>
+    <string name="settings_discovery_consent_notice_on">Hai acconsentito ad inviare indirizzi email e numeri di telefono a questo server d\'identità per poter rintracciare altri utenti tra i tuoi contatti.</string>
     <string name="settings_discovery_consent_title">Invia email e numeri di telefono</string>
     <string name="direct_room_user_list_suggestions_title">Suggerimenti</string>
     <string name="direct_room_user_list_known_title">Utenti conosciuti</string>
@@ -1978,7 +1978,7 @@
     <string name="space_type_public">Pubblico</string>
     <string name="create_spaces_private_teammates">Uno Spazio privato per te e i tuoi compagni</string>
     <string name="create_spaces_me_and_teammates">Io e i miei compagni</string>
-    <string name="create_spaces_organise_rooms">Uno Spazio privato per organizzare le tue stanze</string>
+    <string name="create_spaces_organise_rooms">Uno spazio privato per organizzare le tue stanze</string>
     <string name="create_spaces_just_me">Solo io</string>
     <string name="create_spaces_make_sure_access">Assicurati che le persone giuste abbiano accesso a %s.</string>
     <string name="create_spaces_who_are_you_working_with">Con chi stai lavorando\?</string>
@@ -2133,7 +2133,7 @@
     <string name="settings_notification_mentions_and_keywords">Menzioni e parole chiave</string>
     <string name="settings_notification_default">Notifiche predefinite</string>
     <string name="link_this_email_with_your_account">%s nella impostazioni per ricevere inviti direttamente in ${app_name}.</string>
-    <string name="link_this_email_settings_link">Collega questa email con il tuo account</string>
+    <string name="link_this_email_settings_link">Collega questo indirizzo email con il tuo account</string>
     <string name="this_invite_to_this_space_was_sent">Questo invito per questo spazio è stato inviato a %s, la quale non è associata al tuo account</string>
     <string name="this_invite_to_this_room_was_sent">Questo invito per questa stanza è stato inviato a %s, la quale non è associata al tuo account</string>
     <string name="all_rooms_youre_in_will_be_shown_in_home">Tutte le stanze in cui sei appariranno nella pagina principale.</string>
@@ -2249,12 +2249,12 @@
     <string name="create_poll_question_title">Domanda o argomento del sondaggio</string>
     <string name="create_poll_title">Crea sondaggio</string>
     <string name="attachment_type_poll">Sondaggio</string>
-    <string name="identity_server_consent_dialog_title_2">Invia email e numeri di telefono a %s</string>
+    <string name="identity_server_consent_dialog_title_2">Invia indirizzi email e numeri di telefono a %s</string>
     <string name="settings_discovery_consent_notice_off_2">I tuoi contatti sono privati. Per trovare utenti dai tuoi contatti, ci serve l\'autorizzazione per inviare le informazioni dei contatti al tuo server d\'identità.</string>
     <string name="shortcut_disabled_reason_sign_out">La sessione è stata disconnessa!</string>
     <string name="shortcut_disabled_reason_room_left">La stanza è stata lasciata!</string>
     <string name="identity_server_consent_dialog_content_question">Sei d\'accordo con l\'invio di queste informazioni\?</string>
-    <string name="identity_server_consent_dialog_content_3">Per trovare i contatti esistenti, devi inviare le informazioni dei contatti (email e numeri di telefono) al tuo server d\'identità. Facciamo un hash dei dati prima di inviarli per privacy.</string>
+    <string name="identity_server_consent_dialog_content_3">Per trovare i contatti esistenti, devi inviare le informazioni dei contatti (indirizzi email e numeri di telefono) al tuo server d\'identità. Facciamo un hash dei dati prima di inviarli per privacy.</string>
     <string name="action_not_now">Non ora</string>
     <string name="delete_poll_dialog_content">Vuoi davvero rimuovere questo sondaggio\? Non potrai recuperarlo una volta rimosso.</string>
     <string name="delete_poll_dialog_title">Rimuovi sondaggio</string>
@@ -2701,4 +2701,37 @@
     <string name="labs_enable_deferred_dm_summary">Crea messaggio diretto solo al primo messaggio</string>
     <string name="labs_enable_new_app_layout_summary">Un Element semplificato con schede opzionali</string>
     <string name="labs_enable_new_app_layout_title">Attiva nuova disposizione</string>
+    <string name="device_manager_learn_more_session_rename">Gli altri utenti nei messaggi diretti e nelle stanze in cui entri, possono vedere una lista completa delle tue sessioni.
+\n
+\nIn questo modo hanno la certezza che stanno parlando davvero con te, ma significa anche che possono vedere il nome della sessione che inserisci qui.</string>
+    <string name="device_manager_learn_more_session_rename_title">Rinominare le sessioni</string>
+    <string name="device_manager_learn_more_sessions_verified">Le sessioni verificate hanno effettuato l\'accesso con le tue credenziali e sono state verificate, usando la frase di sicurezza o la verifica incrociata.
+\n
+\nCiò significa che hanno le tue chiavi di crittografia per i messaggi passati, e confermano agli altri utenti con cui comunichi che queste sessioni sono usate da te.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Sessioni verificate</string>
+    <string name="device_manager_learn_more_sessions_unverified">Le sessioni non verificate sono quelle in cui è stato fatto l\'accesso con le tue credenziali, ma che non sono state verificate.
+\n
+\nDovresti essere particolarmente sicuro di riconoscere queste sessioni dato che potrebbero rappresentare un uso non autorizzato del tuo account.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Sessioni non verificate</string>
+    <string name="device_manager_learn_more_sessions_inactive">Le sessioni inattive sono quelle che non usi da un po\' di tempo, ma che continuano a ricevere chiavi di crittografia.
+\n
+\nLa rimozione di sessioni inattive migliora la sicurezza e le prestazioni, e ti rende più facile capire se una sessione nuova è sospetta.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Sessioni inattive</string>
+    <string name="device_manager_session_rename_warning">Ricorda che i nomi di sessione sono anche visibili alle persone con cui comunichi.</string>
+    <string name="device_manager_session_rename_description">I nomi di sessione personalizzati possono aiutarti a riconoscere i tuoi dispositivi più facilmente.</string>
+    <string name="device_manager_session_rename_edit_hint">Nome sessione</string>
+    <string name="device_manager_session_rename">Rinomina sessione</string>
+    <string name="device_manager_session_overview_signout">Disconnetti questa sessione</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Non verificata · La sessione attuale</string>
+    <string name="tooltip_attachment_voice_broadcast">Inizia un broadcast vocale</string>
+    <string name="key_authenticity_not_guaranteed">L\'autenticità di questo messaggio cifrato non può essere garantita su questo dispositivo.</string>
+    <string name="settings_security_incognito_keyboard_summary">Richiedi che la tastiera non debba aggiornare dati personalizzati come la cronologia di digitazione e il dizionario in base a cosa digiti nelle conversazioni. Nota che alcune tastiere potrebbero non rispettare questa impostazione.</string>
+    <string name="settings_security_incognito_keyboard_title">Tastiera incognito</string>
+    <string name="command_description_table_flip">Antepone (╯°□°)╯︵ ┻━┻ ad un messaggio di testo</string>
+    <string name="attachment_type_voice_broadcast">Broadcast voce</string>
+    <string name="command_description_devtools">Apri la schermata degli strumenti per sviluppatori</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Hai attivato la crittografia solo per sessioni verificate in tutte le stanze nelle impostazioni di sicurezza.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Ci sono dispositivi non verificati in questa stanza, non potranno decifrare i messaggi che invii.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Non inviare mai messaggi cifrati a sessioni non verificate in questa stanza.</string>
+    <string name="action_got_it">Capito</string>
 </resources>
\ No newline at end of file

From e9bde89fec2d5ff5b41280e81666dac8d6ae13d2 Mon Sep 17 00:00:00 2001
From: ByeongsuPark <byeongsu@soongsil.ac.kr>
Date: Mon, 10 Oct 2022 16:44:01 +0900
Subject: [PATCH 061/400] Add changelog.d

Signed-off-by: byeongsu@soongsil.ac.kr <byeongsu@soongsil.ac.kr>
---
 changelog.d/5968.bugfix | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/5968.bugfix

diff --git a/changelog.d/5968.bugfix b/changelog.d/5968.bugfix
new file mode 100644
index 0000000000..05cf5cea60
--- /dev/null
+++ b/changelog.d/5968.bugfix
@@ -0,0 +1 @@
+Fix wrong mic button direction to cancel on RTL languages

From ad208a08199817715c4372a416b6f4a1b0b10767 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 10 Oct 2022 13:48:25 +0300
Subject: [PATCH 062/400] Refactor layout.

---
 .../src/main/res/values/strings.xml           |  8 ++---
 .../features/login/qr/QrCodeLoginActivity.kt  |  3 +-
 .../qr/QrCodeLoginInstructionsFragment.kt     | 14 +++++++++
 vector/src/main/res/drawable/ic_qr_code.xml   |  9 ++++++
 .../layout/fragment_ftue_combined_login.xml   | 29 ++++++++++---------
 5 files changed, 44 insertions(+), 19 deletions(-)
 create mode 100644 vector/src/main/res/drawable/ic_qr_code.xml

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 86e1afa471..a37e15f91d 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -2168,7 +2168,7 @@
     <string name="login_signin_matrix_id_password_notice">If you don’t know your password, go back to reset it.</string>
     <string name="login_signin_matrix_id_error_invalid_matrix_id">This is not a valid user identifier. Expected format: \'@user:homeserver.org\'</string>
     <string name="autodiscover_well_known_error">Unable to find a valid homeserver. Please check your identifier</string>
-    <string name="login_signin_with_qr_code">Sign in with QR code</string>
+    <string name="login_scan_qr_code">Scan QR code</string>
 
     <string name="seen_by">Seen by</string>
 
@@ -3341,10 +3341,10 @@
     <string name="qr_code_login_header_failed_device_is_not_supported_description">Linking with this device is not supported.</string>
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
-    <string name="qr_code_login_new_device_instruction_1">Open Element on your other device</string>
+    <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
-    <string name="qr_code_login_new_device_instruction_3">Select \'Link a device\'</string>
-    <string name="qr_code_login_new_device_instruction_4">Select \'Show QR code on this device\'</string>
+    <string name="qr_code_login_new_device_instruction_3">Are you on web? -> Select \'Show QR code\'</string>
+    <string name="qr_code_login_new_device_instruction_4">Are you on a mobile device? -> Select \'Scan QR code\' and then \'Show QR code\'</string>
     <string name="qr_code_login_show_qr_code_button">Show QR code in this device</string>
     <string name="qr_code_login_need_an_alternative_method">Need an alternative method?</string>
     <string name="qr_code_login_scan_qr_code_button">Scan QR code</string>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 18efdca4ef..7ca58088f8 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -23,6 +23,7 @@ import android.view.View
 import com.airbnb.mvrx.Mavericks
 import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
 
@@ -38,7 +39,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
             if (qrCodeLoginArgs?.loginType == QrCodeLoginType.LOGIN) {
-                addFragmentToBackstack(
+                addFragment(
                         views.container,
                         QrCodeLoginInstructionsFragment::class.java,
                         qrCodeLoginArgs,
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index c9cf59a7d5..6b48975e15 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -21,6 +21,7 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.core.view.isVisible
 import com.airbnb.mvrx.activityViewModel
 import com.airbnb.mvrx.fragmentViewModel
 import dagger.hilt.android.AndroidEntryPoint
@@ -41,10 +42,23 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
+        observeViewState()
         initScanQrCodeButton()
         initShowQrCodeButton()
     }
 
+    private fun observeViewState() {
+        viewModel.onEach {
+            if (it.loginType == QrCodeLoginType.LOGIN) {
+                views.qrCodeLoginInstructionsShowQrCodeButton.isVisible = false
+                views.qrCodeLoginInstructionsAlternativeLayout.isVisible = false
+            } else {
+                views.qrCodeLoginInstructionsShowQrCodeButton.isVisible = true
+                views.qrCodeLoginInstructionsAlternativeLayout.isVisible = true
+            }
+        }
+    }
+
     private fun initShowQrCodeButton() {
         views.qrCodeLoginInstructionsShowQrCodeButton.debouncedClicks {
             viewModel.handle(QrCodeLoginAction.ShowQrCode)
diff --git a/vector/src/main/res/drawable/ic_qr_code.xml b/vector/src/main/res/drawable/ic_qr_code.xml
new file mode 100644
index 0000000000..1ebdc169c9
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_qr_code.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="13dp"
+    android:height="12dp"
+    android:viewportWidth="13"
+    android:viewportHeight="12">
+  <path
+      android:pathData="M7.167,12V10.667H8.5V12H7.167ZM5.833,10.667V7.333H7.167V10.667H5.833ZM11.167,8.667V6H12.5V8.667H11.167ZM9.833,6V4.667H11.167V6H9.833ZM1.833,7.333V6H3.167V7.333H1.833ZM0.5,6V4.667H1.833V6H0.5ZM6.5,1.333V0H7.833V1.333H6.5ZM1.333,3.167H3.667V0.833H1.333V3.167ZM1,4C0.856,4 0.736,3.953 0.642,3.858C0.547,3.764 0.5,3.644 0.5,3.5V0.5C0.5,0.356 0.547,0.236 0.642,0.142C0.736,0.047 0.856,0 1,0H4C4.144,0 4.264,0.047 4.358,0.142C4.453,0.236 4.5,0.356 4.5,0.5V3.5C4.5,3.644 4.453,3.764 4.358,3.858C4.264,3.953 4.144,4 4,4H1ZM1.333,11.167H3.667V8.833H1.333V11.167ZM1,12C0.856,12 0.736,11.953 0.642,11.858C0.547,11.764 0.5,11.644 0.5,11.5V8.5C0.5,8.356 0.547,8.236 0.642,8.142C0.736,8.047 0.856,8 1,8H4C4.144,8 4.264,8.047 4.358,8.142C4.453,8.236 4.5,8.356 4.5,8.5V11.5C4.5,11.644 4.453,11.764 4.358,11.858C4.264,11.953 4.144,12 4,12H1ZM9.333,3.167H11.667V0.833H9.333V3.167ZM9,4C8.856,4 8.736,3.953 8.642,3.858C8.547,3.764 8.5,3.644 8.5,3.5V0.5C8.5,0.356 8.547,0.236 8.642,0.142C8.736,0.047 8.856,0 9,0H12C12.144,0 12.264,0.047 12.358,0.142C12.453,0.236 12.5,0.356 12.5,0.5V3.5C12.5,3.644 12.453,3.764 12.358,3.858C12.264,3.953 12.144,4 12,4H9ZM9.833,12V10H8.5V8.667H11.167V10.667H12.5V12H9.833ZM7.167,7.333V6H9.833V7.333H7.167ZM4.5,7.333V6H3.167V4.667H7.167V6H5.833V7.333H4.5ZM5.167,4V1.333H6.5V2.667H7.833V4H5.167ZM2,2.5V1.5H3V2.5H2ZM2,10.5V9.5H3V10.5H2ZM10,2.5V1.5H11V2.5H10Z"
+      android:fillColor="#0DBD8B"/>
+</vector>
diff --git a/vector/src/main/res/layout/fragment_ftue_combined_login.xml b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
index 5440fcba15..17fe895cf2 100644
--- a/vector/src/main/res/layout/fragment_ftue_combined_login.xml
+++ b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
@@ -220,17 +220,6 @@
             app:layout_constraintStart_toStartOf="@id/loginGutterStart"
             app:layout_constraintTop_toBottomOf="@id/actionSpacing" />
 
-        <Button
-            android:id="@+id/loginWithQrCode"
-            style="@style/Widget.Vector.Button.Outlined.Login"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="12dp"
-            android:text="@string/login_signin_with_qr_code"
-            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
-            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
-            app:layout_constraintTop_toBottomOf="@id/loginSubmit" />
-
         <androidx.constraintlayout.widget.Group
             android:id="@+id/ssoGroup"
             android:layout_width="wrap_content"
@@ -238,7 +227,7 @@
             android:visibility="gone"
             app:constraint_referenced_ids="ssoButtonsHeader,ssoButtons"
             app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
-            app:layout_constraintTop_toBottomOf="@id/loginWithQrCode"
+            app:layout_constraintTop_toBottomOf="@id/loginSubmit"
             tools:visibility="visible" />
 
         <TextView
@@ -253,7 +242,19 @@
             app:layout_constraintBottom_toTopOf="@id/ssoButtons"
             app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
             app:layout_constraintStart_toStartOf="@id/loginGutterStart"
-            app:layout_constraintTop_toBottomOf="@id/loginWithQrCode" />
+            app:layout_constraintTop_toBottomOf="@id/loginSubmit" />
+
+        <Button
+            android:id="@+id/loginWithQrCode"
+            style="@style/Widget.Vector.Button.Outlined.Login"
+            android:layout_width="0dp"
+            android:layout_height="60dp"
+            android:layout_marginTop="12dp"
+            android:text="@string/login_scan_qr_code"
+            app:drawableLeftCompat="@drawable/ic_qr_code"
+            app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
+            app:layout_constraintStart_toStartOf="@id/loginGutterStart"
+            app:layout_constraintTop_toBottomOf="@id/ssoButtonsHeader" />
 
         <im.vector.app.features.login.SocialLoginButtonsView
             android:id="@+id/ssoButtons"
@@ -263,7 +264,7 @@
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
             app:layout_constraintStart_toStartOf="@id/loginGutterStart"
-            app:layout_constraintTop_toBottomOf="@id/ssoButtonsHeader"
+            app:layout_constraintTop_toBottomOf="@id/loginWithQrCode"
             tools:signMode="signup" />
 
     </androidx.constraintlayout.widget.ConstraintLayout>

From 1db6b7be1ffab1f1fd8491de102dfd8ba533dc46 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 10 Oct 2022 23:05:08 +0000
Subject: [PATCH 063/400] Bump danger/danger-js from 11.1.3 to 11.1.4

Bumps [danger/danger-js](https://github.com/danger/danger-js) from 11.1.3 to 11.1.4.
- [Release notes](https://github.com/danger/danger-js/releases)
- [Changelog](https://github.com/danger/danger-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/danger/danger-js/compare/11.1.3...11.1.4)

---
updated-dependencies:
- dependency-name: danger/danger-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/danger.yml  | 2 +-
 .github/workflows/quality.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml
index 5698a696b6..30b6600c94 100644
--- a/.github/workflows/danger.yml
+++ b/.github/workflows/danger.yml
@@ -11,7 +11,7 @@ jobs:
       - run: |
           npm install --save-dev @babel/plugin-transform-flow-strip-types
       - name: Danger
-        uses: danger/danger-js@11.1.3
+        uses: danger/danger-js@11.1.4
         with:
           args: "--dangerfile tools/danger/dangerfile.js"
         env:
diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
index 1692e2e281..9d9e8e76e8 100644
--- a/.github/workflows/quality.yml
+++ b/.github/workflows/quality.yml
@@ -66,7 +66,7 @@ jobs:
           yarn add danger-plugin-lint-report --dev
       - name: Danger lint
         if: always()
-        uses: danger/danger-js@11.1.3
+        uses: danger/danger-js@11.1.4
         with:
           args: "--dangerfile tools/danger/dangerfile-lint.js"
         env:

From aacf2ba8a660688c61db2eebe48f13c3bd319e32 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 11 Oct 2022 13:08:43 +0300
Subject: [PATCH 064/400] Refactor layout.

---
 .../ui-strings/src/main/res/values/strings.xml |  4 +++-
 .../login/qr/QrCodeLoginInstructionsView.kt    | 18 ++++++++++++++++++
 .../fragment_qr_code_login_instructions.xml    |  2 +-
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index a37e15f91d..d6a3cc8dbf 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3345,8 +3345,10 @@
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
     <string name="qr_code_login_new_device_instruction_3">Are you on web? -> Select \'Show QR code\'</string>
     <string name="qr_code_login_new_device_instruction_4">Are you on a mobile device? -> Select \'Scan QR code\' and then \'Show QR code\'</string>
+    <string name="qr_code_login_link_a_device_instruction_1">Open ${app_name} on your other device</string>
+    <string name="qr_code_login_link_a_device_instruction_2">Select \'Sign in with QR code\'</string>
     <string name="qr_code_login_show_qr_code_button">Show QR code in this device</string>
-    <string name="qr_code_login_need_an_alternative_method">Need an alternative method?</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Signing in a mobile device?</string>
     <string name="qr_code_login_scan_qr_code_button">Scan QR code</string>
     <string name="qr_code_login_connecting_to_device">Connecting to device</string>
     <string name="qr_code_login_signing_in">Signing you in</string>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
index e3ddf608af..30a24090c6 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
@@ -20,6 +20,8 @@ import android.content.Context
 import android.content.res.TypedArray
 import android.util.AttributeSet
 import android.view.LayoutInflater
+import android.widget.LinearLayout
+import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.content.res.use
 import androidx.core.view.isVisible
@@ -63,4 +65,20 @@ class QrCodeLoginInstructionsView @JvmOverloads constructor(
         binding.instruction3TextView.text = instruction3
         binding.instruction4TextView.text = instruction4
     }
+
+    fun setInstructions(instructions: List<String>) {
+        setInstruction(binding.instructions1Layout, binding.instruction1TextView, instructions.getOrNull(0))
+        setInstruction(binding.instructions2Layout, binding.instruction2TextView, instructions.getOrNull(1))
+        setInstruction(binding.instructions3Layout, binding.instruction3TextView, instructions.getOrNull(2))
+        setInstruction(binding.instructions4Layout, binding.instruction4TextView, instructions.getOrNull(3))
+    }
+
+    private fun setInstruction(instructionLayout: LinearLayout, instructionTextView: TextView, instruction: String?) {
+        instruction?.let {
+            instructionLayout.isVisible = true
+            instructionTextView.text = instruction
+        } ?: run {
+            instructionLayout.isVisible = false
+        }
+    }
 }
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
index df043e8238..7983d784af 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
@@ -63,7 +63,7 @@
             android:layout_gravity="center"
             android:background="?android:colorBackground"
             android:paddingHorizontal="12dp"
-            android:text="@string/qr_code_login_need_an_alternative_method"
+            android:text="@string/qr_code_login_signing_in_a_mobile_device"
             app:drawableLeftCompat="@drawable/divider_horizontal"
             app:drawableTint="@color/alert_default_error_background" />
     </FrameLayout>

From 55663006655da93891cc85f0f292703ef181b725 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 11 Oct 2022 14:15:44 +0300
Subject: [PATCH 065/400] Add qr code options to layout.

---
 .../src/main/res/values/strings.xml           |  2 +
 .../stylable_sessions_list_header_view.xml    |  1 +
 .../devices/v2/list/SessionsListHeaderView.kt | 29 ++++---
 .../res/layout/fragment_other_sessions.xml    |  7 +-
 .../res/layout/fragment_settings_devices.xml  | 76 ++++++++++++++-----
 5 files changed, 83 insertions(+), 32 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index d6a3cc8dbf..bf0bce8a8c 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3303,6 +3303,8 @@
     <string name="device_manager_session_rename_edit_hint">Session name</string>
     <string name="device_manager_session_rename_description">Custom session names can help you recognize your devices more easily.</string>
     <string name="device_manager_session_rename_warning">Please be aware that session names are also visible to people you communicate with.</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">SIGN IN WITH QR CODE</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">You can use this device to sign in a mobile or web device with a QR code. There are two ways to do this:</string>
 
     <!-- Note to translators: %s will be replaces with selected space name -->
     <string name="home_empty_space_no_rooms_title">%s\nis looking a little empty.</string>
diff --git a/library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml b/library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
index d3b931e44a..f0907c8087 100644
--- a/library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
+++ b/library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
@@ -4,6 +4,7 @@
     <declare-styleable name="SessionsListHeaderView">
         <attr name="sessionsListHeaderTitle" format="string" />
         <attr name="sessionsListHeaderDescription" format="string" />
+        <attr name="sessionListHeaderShowLearnMore" format="boolean" />
     </declare-styleable>
 
 </resources>
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
index ef8682df01..c94dd469da 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
@@ -65,20 +65,29 @@ class SessionsListHeaderView @JvmOverloads constructor(
             return
         }
 
+        val showLearnMore = typedArray.getBoolean(R.styleable.SessionsListHeaderView_sessionListHeaderShowLearnMore, true)
         val learnMore = context.getString(R.string.action_learn_more)
-        val fullDescription = buildString {
-            append(description)
-            append(" ")
-            append(learnMore)
+        val fullDescription = if (showLearnMore) {
+            buildString {
+                append(description)
+                append(" ")
+                append(learnMore)
+            }
+        } else {
+            description
         }
 
         binding.sessionsListHeaderDescription.isVisible = true
-        binding.sessionsListHeaderDescription.setTextWithColoredPart(
-                fullText = fullDescription,
-                coloredPart = learnMore,
-                underline = false
-        ) {
-            onLearnMoreClickListener?.invoke()
+        if (showLearnMore) {
+            binding.sessionsListHeaderDescription.setTextWithColoredPart(
+                    fullText = fullDescription,
+                    coloredPart = learnMore,
+                    underline = false
+            ) {
+                onLearnMoreClickListener?.invoke()
+            }
+        } else {
+            binding.sessionsListHeaderDescription.text = fullDescription
         }
     }
 }
diff --git a/vector/src/main/res/layout/fragment_other_sessions.xml b/vector/src/main/res/layout/fragment_other_sessions.xml
index 037f85ad28..d78db073c4 100644
--- a/vector/src/main/res/layout/fragment_other_sessions.xml
+++ b/vector/src/main/res/layout/fragment_other_sessions.xml
@@ -53,11 +53,12 @@
         android:id="@+id/deviceListHeaderOtherSessions"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        app:sessionsListHeaderDescription="@string/device_manager_sessions_other_description"
-        app:sessionsListHeaderTitle=""
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/appBarLayout" />
+        app:layout_constraintTop_toBottomOf="@id/appBarLayout"
+        app:sessionListHeaderShowLearnMore="true"
+        app:sessionsListHeaderDescription="@string/device_manager_sessions_other_description"
+        app:sessionsListHeaderTitle="" />
 
     <im.vector.app.features.settings.devices.v2.othersessions.OtherSessionsSecurityRecommendationView
         android:id="@+id/otherSessionsSecurityRecommendationView"
diff --git a/vector/src/main/res/layout/fragment_settings_devices.xml b/vector/src/main/res/layout/fragment_settings_devices.xml
index 8e2daa2239..7167eebb8e 100644
--- a/vector/src/main/res/layout/fragment_settings_devices.xml
+++ b/vector/src/main/res/layout/fragment_settings_devices.xml
@@ -12,11 +12,12 @@
             android:id="@+id/deviceListHeaderSectionSecurityRecommendations"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
-            app:sessionsListHeaderDescription="@string/device_manager_header_section_security_recommendations_description"
-            app:sessionsListHeaderTitle="@string/device_manager_header_section_security_recommendations_title"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
+            app:layout_constraintTop_toTopOf="parent"
+            app:sessionListHeaderShowLearnMore="true"
+            app:sessionsListHeaderDescription="@string/device_manager_header_section_security_recommendations_description"
+            app:sessionsListHeaderTitle="@string/device_manager_header_section_security_recommendations_title" />
 
         <im.vector.app.features.settings.devices.v2.list.SecurityRecommendationView
             android:id="@+id/deviceListUnverifiedSessionsRecommendation"
@@ -24,13 +25,13 @@
             android:layout_height="wrap_content"
             android:layout_marginHorizontal="16dp"
             android:layout_marginVertical="16dp"
-            app:recommendationTitle="@string/device_manager_unverified_sessions_title"
-            app:recommendationDescription="@string/device_manager_unverified_sessions_description"
-            app:recommendationImageResource="@drawable/ic_shield_warning_no_border"
-            app:recommendationImageBackgroundTint="@color/shield_color_warning_background"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSectionSecurityRecommendations"/>
+            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSectionSecurityRecommendations"
+            app:recommendationDescription="@string/device_manager_unverified_sessions_description"
+            app:recommendationImageBackgroundTint="@color/shield_color_warning_background"
+            app:recommendationImageResource="@drawable/ic_shield_warning_no_border"
+            app:recommendationTitle="@string/device_manager_unverified_sessions_title" />
 
         <im.vector.app.features.settings.devices.v2.list.SecurityRecommendationView
             android:id="@+id/deviceListInactiveSessionsRecommendation"
@@ -38,13 +39,13 @@
             android:layout_height="wrap_content"
             android:layout_marginHorizontal="16dp"
             android:layout_marginVertical="16dp"
-            app:recommendationTitle="@string/device_manager_inactive_sessions_title"
-            app:recommendationDescription="@plurals/device_manager_inactive_sessions_description"
-            app:recommendationImageResource="@drawable/ic_inactive_sessions"
-            app:recommendationImageBackgroundTint="?vctr_system"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/deviceListUnverifiedSessionsRecommendation"/>
+            app:layout_constraintTop_toBottomOf="@id/deviceListUnverifiedSessionsRecommendation"
+            app:recommendationDescription="@plurals/device_manager_inactive_sessions_description"
+            app:recommendationImageBackgroundTint="?vctr_system"
+            app:recommendationImageResource="@drawable/ic_inactive_sessions"
+            app:recommendationTitle="@string/device_manager_inactive_sessions_title" />
 
         <View
             android:id="@+id/deviceListSecurityRecommendationsDivider"
@@ -60,11 +61,12 @@
             android:id="@+id/deviceListHeaderCurrentSession"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
-            app:sessionsListHeaderDescription=""
-            app:sessionsListHeaderTitle="@string/device_manager_current_session_title"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/deviceListSecurityRecommendationsDivider" />
+            app:layout_constraintTop_toBottomOf="@id/deviceListSecurityRecommendationsDivider"
+            app:sessionListHeaderShowLearnMore="false"
+            app:sessionsListHeaderDescription=""
+            app:sessionsListHeaderTitle="@string/device_manager_current_session_title" />
 
         <im.vector.app.features.settings.devices.v2.list.SessionInfoView
             android:id="@+id/deviceListCurrentSession"
@@ -90,11 +92,12 @@
             android:id="@+id/deviceListHeaderOtherSessions"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
-            app:sessionsListHeaderDescription="@string/device_manager_sessions_other_description"
-            app:sessionsListHeaderTitle="@string/device_manager_sessions_other_title"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/deviceListDividerCurrentSession" />
+            app:layout_constraintTop_toBottomOf="@id/deviceListDividerCurrentSession"
+            app:sessionListHeaderShowLearnMore="true"
+            app:sessionsListHeaderDescription="@string/device_manager_sessions_other_description"
+            app:sessionsListHeaderTitle="@string/device_manager_sessions_other_title" />
 
         <im.vector.app.features.settings.devices.v2.list.OtherSessionsView
             android:id="@+id/deviceListOtherSessions"
@@ -105,6 +108,41 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/deviceListHeaderOtherSessions" />
 
+        <im.vector.app.features.settings.devices.v2.list.SessionsListHeaderView
+            android:id="@+id/deviceListHeaderSignInWithQrCode"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/deviceListOtherSessions"
+            app:sessionListHeaderShowLearnMore="false"
+            app:sessionsListHeaderDescription="@string/device_manager_sessions_sign_in_with_qr_code_description"
+            app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title" />
+
+        <Button
+            android:id="@+id/deviceListHeaderScanQrCodeButton"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="16dp"
+            android:layout_marginTop="12dp"
+            android:text="@string/qr_code_login_scan_qr_code_button"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode" />
+
+        <Button
+            android:id="@+id/deviceListHeaderShowQrCodeButton"
+            style="@style/Widget.Vector.Button.Outlined"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="16dp"
+            android:layout_marginTop="4dp"
+            android:layout_marginBottom="12dp"
+            android:text="@string/qr_code_login_show_qr_code_button"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton" />
+
         <include
             android:id="@+id/waiting_view"
             layout="@layout/merge_overlay_waiting_view"

From 0ac7f43438f2126c2f400ff92d1a9051cd263e91 Mon Sep 17 00:00:00 2001
From: "Auri B. P" <auri97@gmail.com>
Date: Mon, 10 Oct 2022 17:34:35 +0000
Subject: [PATCH 066/400] Translated using Weblate (Catalan)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ca/
---
 library/ui-strings/src/main/res/values-ca/strings.xml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-ca/strings.xml b/library/ui-strings/src/main/res/values-ca/strings.xml
index a6a5ec0b8d..05795417dd 100644
--- a/library/ui-strings/src/main/res/values-ca/strings.xml
+++ b/library/ui-strings/src/main/res/values-ca/strings.xml
@@ -2738,4 +2738,9 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Hi ha dispositius no verificats en aquesta sala, no podran desxifrat els missatges que enviïs.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">No enviïs mai missatges xifrats a sessions no verificades d\'aquesta sala.</string>
     <string name="action_got_it">D\'acord</string>
+    <string name="attachment_type_voice_broadcast">Transmissió de veu</string>
+    <string name="tooltip_attachment_voice_broadcast">Inicia una transmissió de veu</string>
+    <string name="settings_security_incognito_keyboard_summary">Sol·licita que no es desi cap dada personalitzada del teclat en funció del que escrius a les converses (per exemple l\'historial d\'escriptura o el diccionari). Tingues en compte que alguns teclats poden no respectar aquesta configuració.</string>
+    <string name="settings_security_incognito_keyboard_title">Teclat incògnit</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Has activat el xifrat a només en sessions verificades a totes les sales, a Configuració &gt; Seguretat.</string>
 </resources>
\ No newline at end of file

From 5bea54518f7be46e2ffe9d519cada82dca573df5 Mon Sep 17 00:00:00 2001
From: Vri <element@vrifox.cc>
Date: Mon, 10 Oct 2022 17:05:55 +0000
Subject: [PATCH 067/400] Translated using Weblate (German)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 library/ui-strings/src/main/res/values-de/strings.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index e34effc082..a749d6a2e1 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -766,7 +766,7 @@
     <string name="are_you_sure">Sicher\?</string>
     <string name="backup">Sicherung</string>
     <string name="sign_out_bottom_sheet_will_lose_secure_messages">Alle verschlüsselten Nachrichten gehen verloren, wenn Du dich abmeldest ohne die Schlüssel gesichert zu haben.</string>
-    <string name="action_sign_out_confirmation_simple">Wirklich abmelden\?</string>
+    <string name="action_sign_out_confirmation_simple">Bist du sicher, dass du dich abmelden möchtest\?</string>
     <string name="encryption_message_recovery">Wiederherstellung verschlüsselter Nachrichten</string>
     <string name="error_empty_field_enter_user_name">Bitte gib einen Benutzernamen ein.</string>
     <string name="keys_backup_setup">Richte Schlüsselsicherung ein</string>
@@ -1777,7 +1777,7 @@
     <string name="user_code_info_text">Teile diesen Code, damit andere ihn einlesen und mit dir schreiben können.</string>
     <string name="user_code_share">Meinen Code teilen</string>
     <string name="user_code_my_code">Mein Code</string>
-    <string name="user_code_scan">Scanne einen QR-Code</string>
+    <string name="user_code_scan">QR-Code einlesen</string>
     <string name="not_a_valid_qr_code">Das ist kein korrekter QR-Code von Matrix</string>
     <string name="invite_friends_rich_title">🔐️ Komm mit zu ${app_name}</string>
     <string name="invite_friends_text">Hey, schreibe mit mir auf ${app_name}: %s</string>

From 62522ae9e18486646fe22afcc7781ed99c01fd4d Mon Sep 17 00:00:00 2001
From: Linerly <linerly@protonmail.com>
Date: Mon, 10 Oct 2022 11:10:38 +0000
Subject: [PATCH 068/400] Translated using Weblate (Indonesian)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/id/
---
 .../src/main/res/values-in/strings.xml        | 147 +++++++++++-------
 1 file changed, 90 insertions(+), 57 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-in/strings.xml b/library/ui-strings/src/main/res/values-in/strings.xml
index 7b103a9131..51753995bf 100644
--- a/library/ui-strings/src/main/res/values-in/strings.xml
+++ b/library/ui-strings/src/main/res/values-in/strings.xml
@@ -95,7 +95,7 @@
     <string name="auth_invalid_login_param">Nama pengguna dan/atau kata sandi salah</string>
     <string name="auth_reset_password_error_unauthorized">Verifikasi alamat email gagal: pastikan tautan yang termuat di email telah diklik</string>
     <string name="login_error_bad_json">JSON amburadul</string>
-    <string name="login_error_not_json">Tidak berisi JSON yang sah</string>
+    <string name="login_error_not_json">Tidak berisi JSON yang absah</string>
     <string name="login_error_limit_exceeded">Pengajuan yang dikirimkan terlalu banyak</string>
     <string name="incoming_video_call">Panggilan Video Masuk</string>
     <string name="incoming_voice_call">Panggilan Suara Masuk</string>
@@ -216,7 +216,7 @@
     <string name="widget_integration_missing_user_id">Tidak ada user_id dalam permohonan.</string>
     <string name="widget_integration_room_not_visible">Ruang %s tidak terlihat.</string>
     <string name="widget_integration_missing_parameter">Ada parameter penting yang hilang.</string>
-    <string name="room_add_matrix_apps">Tambahkan apps Matrix</string>
+    <string name="room_add_matrix_apps">Tambahkan aplikasi Matrix</string>
     <string name="settings_labs_native_camera">Gunakan kamera bawaan</string>
     <string name="you_added_a_new_device">Anda menambahkan perangkat baru \'%s\', yang sedang meminta kunci enkripsi.</string>
     <string name="your_unverified_device_requesting">Perangkat Anda yang belum terverifikasi \'%s\' sedang meminta kunci enkripsi.</string>
@@ -234,7 +234,7 @@
     <string name="command_description_remove_user">Keluarkan pengguna dengan id berikut</string>
     <string name="command_description_nick">Ubah nama panggilan layar Anda</string>
     <string name="command_description_markdown">Mati/Nyalakan markdown</string>
-    <string name="command_description_clear_scalar_token">Untuk memperbaiki kepengurusan Apps Matrix</string>
+    <string name="command_description_clear_scalar_token">Untuk memperbaiki kepengurusan Aplikasi Matrix</string>
     <string name="notification_off">Mati</string>
     <string name="notification_noisy">Berisik</string>
     <string name="encrypted_message">Pesan terenkripsi</string>
@@ -450,7 +450,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="settings_troubleshoot_test_fcm_failed_account_missing">[%1$s]
 \nError ini di luar kendali ${app_name}. Tidak terdapat akun Google pada perangkat. Mohon buka pengelola akun dan tambahkan akun Google.</string>
     <string name="settings_troubleshoot_test_fcm_failed_account_missing_quick_fix">Tambah Akun</string>
-    <string name="settings_troubleshoot_test_battery_failed">Apabila perangkat tidak sedang diisi atau dipergunakan dengan layar dimatikan, perangkat masuk mode Doze. Ini akan menghalangi aplikasi mengakses jaringan dan menunda tugas, sinkronisasi, dan alarm standar.</string>
+    <string name="settings_troubleshoot_test_battery_failed">Apabila perangkat tidak sedang diisi atau dipergunakan dengan layar dimatikan, perangkat masuk mode tidur. Ini akan menghalangi aplikasi mengakses jaringan dan menunda tugas, sinkronisasi, dan alarm standar.</string>
     <string name="settings_troubleshoot_test_battery_quickfix">Abaikan Optimisasi</string>
     <string name="settings_noisy_notifications_preferences">Kelola Pemberitahuan Berisik</string>
     <string name="settings_call_notifications_preferences">Kelola Pemberitahuan Panggilan</string>
@@ -610,11 +610,11 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     </plurals>
     <string name="settings_call_show_confirmation_dialog_summary">Meminta untuk konfirmasi sebelum memulai panggilan</string>
     <string name="settings_call_show_confirmation_dialog_title">Cegah panggilan tidak disengaja</string>
-    <string name="error_unauthorized">Tidak sah, tidak ada kredensial otentikasi yang valid</string>
+    <string name="error_unauthorized">Tidak sah, tidak ada kredensial otentikasi yang absah</string>
     <string name="login_error_ssl_other">Kesalahan SSL.</string>
     <string name="login_error_ssl_peer_unverified">Kesalahan SSL: identitas peer belum diverifikasi.</string>
     <string name="login_error_homeserver_not_found">Tidak dapat mencapai homeserver pada URL ini, silakan periksa</string>
-    <string name="login_error_no_homeserver_found">Ini bukan alamat server Matrix yang valid</string>
+    <string name="login_error_no_homeserver_found">Ini bukan alamat server Matrix yang absah</string>
     <string name="auth_msisdn_already_defined">Nomor telepon ini sudah ditentukan.</string>
     <string name="auth_login_sso">Masuk dengan single sign-on</string>
     <string name="use_as_default_and_do_not_ask_again">Gunakan sebagai bawaan dan jangan tanya lagi</string>
@@ -676,10 +676,10 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="action_revoke">Batalkan</string>
     <string name="none">Tidak Ada</string>
     <string name="system_theme">Bawaan Sistem</string>
-    <string name="notice_end_to_end_unknown_algorithm_by_you">Anda mengaktifkan enkripsi ujung-ke-ujung. (algoritma tidak dikenali %1$s).</string>
-    <string name="notice_end_to_end_unknown_algorithm">%1$s mengaktifkan enkripsi ujung-ke-ujung. (algoritma tidak dikenali %2$s).</string>
-    <string name="notice_end_to_end_ok_by_you">Anda mengaktifkan enkripsi ujung-ke-ujung.</string>
-    <string name="notice_end_to_end_ok">%1$s mengaktifkan enkripsi ujung-ke-ujung.</string>
+    <string name="notice_end_to_end_unknown_algorithm_by_you">Anda mengaktifkan enkripsi ujung ke ujung. (algoritma tidak dikenali %1$s).</string>
+    <string name="notice_end_to_end_unknown_algorithm">%1$s mengaktifkan enkripsi ujung ke ujung. (algoritma tidak dikenali %2$s).</string>
+    <string name="notice_end_to_end_ok_by_you">Anda mengaktifkan enkripsi ujung ke ujung.</string>
+    <string name="notice_end_to_end_ok">%1$s mengaktifkan enkripsi ujung ke ujung.</string>
     <string name="notice_direct_room_guest_access_forbidden_by_you">Anda telah mencegah para tamu untuk bergabung ke ruangan.</string>
     <string name="notice_direct_room_guest_access_forbidden">%1$s telah mencegah para tamu untuk bergabung ke ruangan.</string>
     <string name="notice_room_guest_access_forbidden_by_you">Anda telah mencegah para tamu untuk bergabung ke ruangan.</string>
@@ -816,7 +816,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="notice_room_server_acl_allow_is_empty">🎉 Semua server dilarang untuk berpartisipasi! Ruangan ini tidak lagi dapat digunakan.</string>
     <string name="notice_room_server_acl_updated_no_change">Tidak ada berubahan.</string>
     <string name="settings_phone_numbers">Nomor telepon</string>
-    <string name="settings_emails_empty">Tidak ada email yang ditambahkan ke akun Anda</string>
+    <string name="settings_emails_empty">Tidak ada alamat email yang ditambahkan ke akun Anda</string>
     <string name="settings_emails">Surel</string>
     <string name="notice_room_server_acl_updated_ip_literals_not_allowed">• Server yang cocok dengan literal IP sekarang dilarang.</string>
     <string name="notice_room_server_acl_updated_banned">• Server yang cocok dengan %s sekarang dilarang.</string>
@@ -969,9 +969,9 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="compression_opt_list_choose">Pilih</string>
     <string name="settings_default_compression">Kompresi bawaan</string>
     <string name="settings_media">Media</string>
-    <string name="settings_emails_and_phone_numbers_summary">Kelola email dan nomor telepon yang ditautkan ke akun Matrix Anda</string>
+    <string name="settings_emails_and_phone_numbers_summary">Kelola alamat email dan nomor telepon yang ditautkan ke akun Matrix Anda</string>
     <string name="settings_emails_and_phone_numbers_title">Email dan nomor telepon</string>
-    <string name="settings_fail_to_update_password_invalid_current_password">Sandi tidak valid</string>
+    <string name="settings_fail_to_update_password_invalid_current_password">Kata sandi tidak absah</string>
     <string name="settings_password">Sandi</string>
     <string name="disabled_integration_dialog_content">Aktifkan \'Izinkan integrasi\' di Pengaturan untuk melakukan ini.</string>
     <string name="disabled_integration_dialog_title">Integrasi dinonaktifkan</string>
@@ -1035,16 +1035,16 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="video_call_with_participant">Panggilan video dengan %s</string>
     <string name="call_ringing">Panggilan berdering…</string>
     <string name="spaces">Space</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Kami mengirimi Anda email konfirmasi ke %s, mohon periksa email Anda dan klik tautan konfirmasi</string>
-    <string name="settings_discovery_no_mails">Opsi penemuan akan muncul setelah Anda menambahkan email.</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Kami mengirim Anda email konfirmasi ke %s, mohon periksa email Anda dan klik tautan konfirmasi</string>
+    <string name="settings_discovery_no_mails">Opsi penemuan akan muncul setelah Anda menambahkan sebuah alamat email.</string>
     <string name="settings_discovery_disconnect_identity_server_info">Memutuskan sambungan dari server identitas Anda akan membuat Anda tidak dapat ditemukan oleh pengguna lain dan Anda tidak akan dapat mengundang orang lain melalui email atau nomor telepon.</string>
     <string name="settings_discovery_consent_title">Kirim email dan nomor telepon</string>
-    <string name="settings_discovery_consent_notice_on">Anda telah memberikan persetujuan untuk mengirim email dan nomor telepon ke server identitas ini untuk menemukan pengguna lain dari kontak Anda.</string>
+    <string name="settings_discovery_consent_notice_on">Anda telah memberikan persetujuan untuk mengirim alamat email dan nomor telepon ke server identitas ini untuk menemukan pengguna lain dari kontak Anda.</string>
     <string name="settings_discovery_disconnect_with_bound_pid">Anda sedang berbagi email atau nomor telepon di server identitas %1$s. Anda harus menyambungkan kembali ke %2$s untuk berhenti membagikannya.</string>
     <string name="settings_agree_to_terms">Setujui Persyaratan Layanan server identitas (%s) agar Anda dapat ditemukan melalui email atau nomor telepon.</string>
-    <string name="settings_discovery_confirm_mail">Kami mengirimi Anda email konfirmasi ke %s, periksa email Anda dan klik tautan konfirmasi</string>
+    <string name="settings_discovery_confirm_mail">Kami mengirim Anda sebuah email ke %s, periksa email Anda dan klik tautan konfirmasi</string>
     <string name="login_reset_password_on">Setel ulang sandi di %1$s</string>
-    <string name="login_login_with_email_error">Email ini tidak terkait dengan akun apa pun.</string>
+    <string name="login_login_with_email_error">Alamat email ini tidak terkait dengan akun apa pun.</string>
     <string name="login_registration_not_supported">Aplikasi tidak dapat membuat akun di homeserver ini.
 \n
 \nApakah Anda ingin mendaftar menggunakan klien web\?</string>
@@ -1080,7 +1080,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="login_server_text">Sama seperti email, akun memiliki satu tempat, tetapi Anda dapat berkomunikasi dengan siapa saja</string>
     <string name="login_server_title">Pilih server</string>
     <string name="login_splash_submit">Mulai</string>
-    <string name="login_splash_text3">Luaskan &amp; sesuaikan pengalaman Anda</string>
+    <string name="login_splash_text3">Tingkatkan &amp; sesuaikan pengalaman Anda</string>
     <string name="login_splash_title">Ini adalah percakapan Anda. Miliki percakapan Anda.</string>
     <string name="login_splash_text2">Jaga percakapan tetap pribadi dengan enkripsi</string>
     <string name="login_splash_text1">Chat dengan orang-orang secara langsung atau dalam grup</string>
@@ -1148,9 +1148,9 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="notification_ticker_text_group">%1$s: %2$s %3$s</string>
     <string name="labs_show_unread_notifications_as_tab">Tambahkan tab terdedikasi untuk notifikasi yang belum dibaca di layar utama.</string>
     <string name="error_file_too_big_simple">File ini terlalu besar untuk diupload.</string>
-    <string name="keys_backup_settings_invalid_signature_from_unverified_device">Cadangan mempunyai tanda tangan yang tidak valid dari sesi %s yang belum diverifikasi</string>
-    <string name="keys_backup_settings_invalid_signature_from_verified_device">Cadangan mempunyai tanda tangan yang tidak valid dari sesi %s yang terverifikasi</string>
-    <string name="keys_backup_settings_valid_signature_from_unverified_device">Cadangan mempunyai tanda tangan yang valid dari sesi %s yang belum diverifikasi</string>
+    <string name="keys_backup_settings_invalid_signature_from_unverified_device">Cadangan mempunyai tanda tangan yang tidak absah dari sesi %s yang belum diverifikasi</string>
+    <string name="keys_backup_settings_invalid_signature_from_verified_device">Cadangan mempunyai tanda tangan yang tidak absah dari sesi %s yang terverifikasi</string>
+    <string name="keys_backup_settings_valid_signature_from_unverified_device">Cadangan mempunyai tanda tangan yang absah dari sesi %s yang belum diverifikasi</string>
     <string name="send_file_step_sending_thumbnail">Mengirim gambar mini (%1$s / %2$s)</string>
     <plurals name="fallback_users_read">
         <item quantity="other">%d pengguna telah membaca</item>
@@ -1278,8 +1278,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="keys_backup_settings_delete_confirm_title">Hapus Cadangan</string>
     <string name="keys_backup_settings_checking_backup_state">Memeriksa status cadangan</string>
     <string name="keys_backup_settings_deleting_backup">Menghapus cadangan…</string>
-    <string name="keys_backup_settings_valid_signature_from_verified_device">Cadangan mempunyai tanda tangan yang valid dari sesi %s yang terverifikasi.</string>
-    <string name="keys_backup_settings_valid_signature_from_this_device">Cadangan mempunyai tanda tangan yang valid dari sesi ini.</string>
+    <string name="keys_backup_settings_valid_signature_from_verified_device">Cadangan mempunyai tanda tangan yang absah dari sesi %s yang terverifikasi.</string>
+    <string name="keys_backup_settings_valid_signature_from_this_device">Cadangan mempunyai tanda tangan yang absah dari sesi ini.</string>
     <string name="keys_backup_settings_signature_from_unknown_device">Cadangan mempunyai tanda tangan dari sesi tidak dikenal dengan ID %s.</string>
     <string name="keys_backup_settings_status_not_setup">Kunci Anda tidak dicadangkan dari sesi ini.</string>
     <string name="keys_backup_settings_status_ko">Cadangan Kunci belum aktif di sesi ini.</string>
@@ -1342,7 +1342,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="keys_backup_setup_step1_manual_export">Ekspor kunci secara manual</string>
     <string name="keys_backup_setup_step1_advanced">(Lanjutan)</string>
     <string name="keys_backup_setup">Mulai menggunakan Cadangan Kunci</string>
-    <string name="keys_backup_setup_step1_description">Pesan di ruangan terenkripsi diamankan dengan enkripsi ujung-ke-ujung. Hanya Anda dan penerima memiliki kunci untuk membaca pesan-pesan ini.
+    <string name="keys_backup_setup_step1_description">Pesan di ruangan terenkripsi diamankan dengan enkripsi ujung ke ujung. Hanya Anda dan penerima memiliki kunci untuk membaca pesan ini.
 \n
 \nCadangkan kunci Anda dengan aman untuk menghindari kehilangan kunci Anda.</string>
     <string name="keys_backup_setup_step1_title">Jangan pernah kehilangan pesan terenkripsi</string>
@@ -1351,7 +1351,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="passphrase_empty_error_message">Mohon masukkan frasa sandi</string>
     <string name="passphrase_passphrase_does_not_match">Frasa sandi tidak cocok</string>
     <string name="passphrase_create_passphrase">Buat frasa sandi</string>
-    <string name="no_valid_google_play_services_apk">Tidak menemukan APK Layanan Google Play yang valid. Notifikasi mungkin tidak berkerja dengan seharusnya.</string>
+    <string name="no_valid_google_play_services_apk">Tidak menemukan APK Layanan Google Play yang absah. Notifikasi mungkin tidak bekerja dengan seharusnya.</string>
     <string name="settings_security_and_privacy">Keamanan &amp; Privasi</string>
     <string name="settings_preferences">Preferensi</string>
     <string name="settings_general_title">Umum</string>
@@ -1435,7 +1435,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="settings_active_sessions_manage">Kelola Sesi</string>
     <string name="settings_active_sessions_show_all">Tampilkan Semua Sesi</string>
     <string name="settings_active_sessions_list">Sesi Aktif</string>
-    <string name="settings_hs_admin_e2e_disabled">Admin server Anda telah menonaktifkan enkripsi ujung-ke-ujung secara bawaan di ruangan &amp; Pesan Langsung privat.</string>
+    <string name="settings_hs_admin_e2e_disabled">Admin server Anda telah menonaktifkan enkripsi ujung ke ujung secara bawaan di ruangan &amp; Pesan Langsung privat.</string>
     <string name="encryption_information_dg_xsigning_disabled">Tanda Tangan Silang dinonaktifkan</string>
     <string name="encryption_information_dg_xsigning_trusted">Tanda Tangan Silang diaktifkan.
 \nKunci dipercaya.
@@ -1446,7 +1446,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
 \nKunci Privat di perangkat.</string>
     <string name="encryption_information_cross_signing_state">Tanda Tangan Silang</string>
     <string name="verification_conclusion_ok_self_notice">Sesi baru Anda telah diverifikasi. Ini memiliki akses ke pesan terenkripsi Anda, dan pengguna lain akan melihatnya sebagai tepercaya.</string>
-    <string name="verification_conclusion_ok_notice">Pesan dengan pengguna ini dienkripsi ujung-ke-ujung dan tidak dapat dibaca oleh pihak ketiga.</string>
+    <string name="verification_conclusion_ok_notice">Pesan dengan pengguna ini dienkripsi ujung ke ujung dan tidak dapat dibaca oleh pihak ketiga.</string>
     <string name="verification_code_notice">Bandingkan kode dengan yang ditampilkan di layar pengguna lain.</string>
     <string name="verification_emoji_notice">Bandingkan emoji yang unik, dan pastikan mereka muncul di urutan yang sama.</string>
     <string name="verification_request_start_notice">Supaya aman, lakukan secara langsung atau gunakan cara lain untuk berkomunikasi.</string>
@@ -1454,8 +1454,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="room_settings_enable_encryption_dialog_submit">Aktifkan enkripsi</string>
     <string name="room_settings_enable_encryption_dialog_content">Ketika diaktifkan, enkripsi tidak dapat dinonaktifkan. Pesan yang dikirim di ruangan terenkripsi tidak dapat dilihat oleh servernya, hanya anggota ruangan. Mengaktifkan enkripsi mungkin mencegah banyaknya bot dan jembatan bekerja dengan seharusnya.</string>
     <string name="room_settings_enable_encryption_dialog_title">Aktifkan enkripsi\?</string>
-    <string name="room_settings_enable_encryption_no_permission">Anda tidak memiliki izin untuk mengaktifkan enkripsi ujung-ke-ujung di ruangan ini.</string>
-    <string name="room_settings_enable_encryption">Aktifkan enkripsi ujung-ke-ujung…</string>
+    <string name="room_settings_enable_encryption_no_permission">Anda tidak memiliki izin untuk mengaktifkan enkripsi ujung ke ujung di ruangan ini.</string>
+    <string name="room_settings_enable_encryption">Aktifkan enkripsi ujung ke ujung…</string>
     <string name="settings_category_composer">Editor pesan</string>
     <string name="settings_category_timeline">Linimasa</string>
     <string name="command_description_rainbow_emote">Mengirim emote yang dicantum berwarna pelangi</string>
@@ -1490,14 +1490,14 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="room_profile_section_more">Lebih banyak</string>
     <string name="room_profile_section_security_learn_more">Pelajari lebih lanjut</string>
     <string name="room_profile_section_security">Keamanan</string>
-    <string name="direct_room_profile_encrypted_subtitle">Pesan di ruangan ini dienkripsi ujung-ke-ujung.
+    <string name="direct_room_profile_encrypted_subtitle">Pesan di ruangan ini dienkripsi secara ujung ke ujung.
 \n
 \nPesan Anda diamankan dengan kunci dan hanya Anda dan penerima memiliki kunci unik untuk mengakses mereka.</string>
-    <string name="room_profile_encrypted_subtitle">Pesan di ruangan ini dienkripsi ujung-ke-ujung.
+    <string name="room_profile_encrypted_subtitle">Pesan di ruangan ini dienkripsi secara ujung ke ujung.
 \n
 \nPesan Anda diamankan dengan kunci dan hanya Anda dan penerima memiliki kunci unik untuk mengakses mereka.</string>
-    <string name="direct_room_profile_not_encrypted_subtitle">Pesan ini tidak terenkripsi secara ujung-ke-ujung.</string>
-    <string name="room_profile_not_encrypted_subtitle">Pesan di ruangan ini tidak terenkripsi secara ujung-ke-ujung.</string>
+    <string name="direct_room_profile_not_encrypted_subtitle">Pesan ini tidak terenkripsi secara ujung ke ujung.</string>
+    <string name="room_profile_not_encrypted_subtitle">Pesan di ruangan ini tidak terenkripsi secara ujung ke ujung.</string>
     <string name="verification_request_waiting_for">Menunggu untuk %s…</string>
     <string name="verification_verified_user">Diverifikasi %s</string>
     <string name="verification_verify_user">Verifikasi %s</string>
@@ -1587,8 +1587,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
 \n• Administrator server Anda telah menghilangkan akses Anda untuk keamanan.</string>
     <string name="signed_out_title">Anda telah keluar</string>
     <string name="seen_by">Dilihat oleh</string>
-    <string name="autodiscover_well_known_error">Tidak dapat menemukan homeserver yang valid. Mohon cek pengenal Anda</string>
-    <string name="login_signin_matrix_id_error_invalid_matrix_id">Ini bukan pengenal pengguna yang valid. Format yang diharapkan: \'@pengguna:homeserver.org\'</string>
+    <string name="autodiscover_well_known_error">Tidak dapat menemukan homeserver yang absah. Mohon cek pengenal Anda</string>
+    <string name="login_signin_matrix_id_error_invalid_matrix_id">Ini bukan pengenal pengguna yang absah. Format yang diharapkan: \'@pengguna:homeserver.org\'</string>
     <string name="login_signin_matrix_id_password_notice">Jika Anda tidak tahu kata sandi Anda, kembali untuk mengatur ulang.</string>
     <string name="login_signin_matrix_id_hint">ID Matrix</string>
     <string name="login_signin_matrix_id_notice">Jika Anda membuat akun di sebuah homeserver, gunakan ID Matrix Anda (mis. @pengguna:domain.com) dan kata sandi dibawah.</string>
@@ -1616,7 +1616,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="login_signup_password_hint">Kata sandi</string>
     <string name="login_signup_username_hint">Nama pengguna</string>
     <string name="login_signin_username_hint">Nama pengguna atau email</string>
-    <string name="login_msisdn_error_other">Nomor telepon kelihatannya tidak valid. Mohon dicek lagi</string>
+    <string name="login_msisdn_error_other">Nomor telepon kelihatannya tidak absah. Mohon dicek lagi</string>
     <string name="login_msisdn_error_not_international">Nomor telepon internasional harus mulai dengan \'+\'</string>
     <string name="login_msisdn_notice">Mohon menggunakan format internasional (nomor telepon harus mulai dengan \'+\')</string>
     <string name="login_msisdn_confirm_submit">Lanjut</string>
@@ -1632,7 +1632,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="login_set_email_submit">Lanjut</string>
     <string name="login_set_email_optional_hint">Email (opsional)</string>
     <string name="login_set_email_mandatory_hint">Email</string>
-    <string name="login_set_email_notice">Atur sebuah email untuk memulihkan akun Anda. Nantinya, Anda dapat mengizinkan orang yang Anda tahu untuk menemukan Anda dari email secara opsional.</string>
+    <string name="login_set_email_notice">Atur sebuah alamat email untuk memulihkan akun Anda. Nantinya, Anda dapat mengizinkan orang yang Anda tahu untuk menemukan Anda dari email secara opsional.</string>
     <string name="login_set_email_title">Atur alamat email</string>
     <string name="login_reset_password_cancel_confirmation_content">Kata sandi Anda belum diubah.
 \n
@@ -1646,9 +1646,9 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="login_reset_password_mail_confirmation_notice_2">Ketuk tautan untuk mengkonfirmasi kata sandi baru Anda. Setelah Anda mengikuti petunjuk yang ada di tautan, klik bawahnya.</string>
     <string name="login_reset_password_mail_confirmation_notice">Email verifikasi terkirim ke %1$s.</string>
     <string name="login_reset_password_mail_confirmation_title">Cek kotak masuk Anda</string>
-    <string name="login_reset_password_error_not_found">Email ini tidak tertaut dengan akun apa pun</string>
+    <string name="login_reset_password_error_not_found">Alamat email ini tidak tertaut dengan akun apa pun</string>
     <string name="login_reset_password_warning_submit">Lanjut</string>
-    <string name="login_reset_password_warning_content">Mengubah kata sandi Anda akan mengatur ulang kunci enkripsi ujung-ke-ujung pada semua sesi Anda, yang akan membuat riwayat obrolan terenkripsi tidak dapat dibaca. Atur Cadangan Kunci atau ekspor kunci ruangan Anda dari sesi lain sebelum mengatur ulang kata sandi Anda.</string>
+    <string name="login_reset_password_warning_content">Mengubah kata sandi Anda akan mengatur ulang kunci enkripsi ujung ke ujung pada semua sesi Anda, yang akan membuat riwayat obrolan terenkripsi tidak dapat dibaca. Atur Cadangan Kunci atau ekspor kunci ruangan Anda dari sesi lain sebelum mengatur ulang kata sandi Anda.</string>
     <string name="login_reset_password_warning_title">Peringatan!</string>
     <string name="login_reset_password_password_hint">Kata sandi baru</string>
     <string name="login_reset_password_email_hint">Email</string>
@@ -1760,7 +1760,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="space_type_public">Publik</string>
     <string name="create_spaces_private_teammates">Space privat untuk Anda &amp; tim Anda</string>
     <string name="create_spaces_me_and_teammates">Saya dan tim saya</string>
-    <string name="create_spaces_organise_rooms">Space yang privat untuk mengorganisir ruangan Anda</string>
+    <string name="create_spaces_organise_rooms">Space yang privat untuk mengelola ruangan Anda</string>
     <string name="create_spaces_just_me">Saya saja</string>
     <string name="create_spaces_make_sure_access">Pastikan orang yang tepat memiliki akses ke %s.</string>
     <string name="create_spaces_who_are_you_working_with">Dengan siapa Anda bekerja\?</string>
@@ -1814,7 +1814,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="warning_room_not_created_yet">Ruangannya belum dibuat. Batalkan pembuatan ruangan\?</string>
     <string name="universal_link_malformed">Tautannya cacat</string>
     <string name="qr_code_not_scanned">Kode QR tidak dipindai!</string>
-    <string name="invalid_qr_code_uri">Kode QR tidak valid (URI tidak valid)!</string>
+    <string name="invalid_qr_code_uri">Kode QR tidak absah (URI tidak absah)!</string>
     <string name="cannot_dm_self">Tidak dapat membuat pesan langsung dengan Anda sendiri!</string>
     <string name="share_by_text">Bagikan melalui teks</string>
     <string name="room_error_not_found">Tidak dapat mencari ruangan ini. Pastikan ruangannya sudah ada.</string>
@@ -1875,7 +1875,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="crypto_error_withheld_generic">Anda tidak dapat mengakses pesan ini karena pengirim telah sengaja tidak mengirim kuncinya</string>
     <string name="crypto_error_withheld_unverified">Anda tidak dapat mengakses pesan ini karena sesi Anda tidak dipercayai oleh pengirim</string>
     <string name="crypto_error_withheld_blacklisted">Anda tidak dapat mengakses pesan ini karena Anda telah diblokir oleh pengirim</string>
-    <string name="notice_crypto_unable_to_decrypt_friendly_desc">Karena enkripsi ujung-ke-ujung, Anda mungkin harus menunggu untuk pesan dari seseorang untuk datang karena kunci enkripsinya tidak dikirim secara benar ke Anda.</string>
+    <string name="notice_crypto_unable_to_decrypt_friendly_desc">Karena adanya enkripsi ujung ke ujung, Anda mungkin harus menunggu untuk pesan dari seseorang untuk datang karena kunci enkripsinya tidak dikirim secara benar ke Anda.</string>
     <string name="notice_crypto_unable_to_decrypt_friendly">Menunggu untuk pesan ini, mungkin membutuhkan beberapa waktu</string>
     <string name="notice_crypto_unable_to_decrypt_final">Anda tidak dapat mengakses pesan ini</string>
     <string name="room_settings_set_avatar">Atur avatar</string>
@@ -1910,7 +1910,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="identity_server_user_consent_not_provided">Izin pengguna belum diberikan.</string>
     <string name="identity_server_error_no_current_binding_error">Tidak ada asosiasi saat ini dengan pengenal ini.</string>
     <string name="identity_server_error_binding_error">Asosiasi telah gagal.</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Untuk pricvasi Anda, ${app_name} hanya mendukung pengiriman email pengguna yang telah di-hash dan nomor telepon.</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Demi privasi Anda, ${app_name} hanya mendukung pengiriman email pengguna dan nomor telepon yang telah di-hash.</string>
     <string name="identity_server_error_terms_not_signed">Mohon terima ketentuan server identitas ini di pengaturan.</string>
     <string name="identity_server_error_no_identity_server_configured">Mohon konfigurasi server identitas.</string>
     <string name="identity_server_error_outdated_identity_server">Server identitas ini telah usang. ${app_name} hanya mendukung API V2.</string>
@@ -1946,7 +1946,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="create_space_error_empty_field_space_name">Beri nama untuk melanjutkan.</string>
     <string name="create_pin_confirm_failure">Gagal untuk memvalidasi PIN, mohon ketuk yang baru.</string>
     <string name="link_this_email_with_your_account">%s di Pengaturan untuk menerima undangan secara langsung di ${app_name}.</string>
-    <string name="link_this_email_settings_link">Tautkan email ini ke akun Anda</string>
+    <string name="link_this_email_settings_link">Tautkan alamat email ini ke akun Anda</string>
     <string name="this_invite_to_this_space_was_sent">Undangan space ini telah dikirim ke %s yang tidak diasosiasikan dengan akun Anda</string>
     <string name="this_invite_to_this_room_was_sent">Undangan ruangan ini telah dikirim ke %s yang tidak diasosiasikan dengan akun Anda</string>
     <string name="external_link_confirmation_title">Periksa ulang tautan ini</string>
@@ -2001,7 +2001,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="bootstrap_progress_checking_backup_with_info">Memeriksa kunci cadangan (%s)</string>
     <string name="bootstrap_progress_checking_backup">Memeriksa kunci cadangan</string>
     <string name="recovery_key_empty_error_message">Mohon masukkan sebuah kunci pemulihan</string>
-    <string name="bootstrap_invalid_recovery_key">Bukan kunci pemulihan yang valid</string>
+    <string name="bootstrap_invalid_recovery_key">Bukan kunci pemulihan yang absah</string>
     <string name="use_file">Gunakan File</string>
     <string name="bootstrap_enter_recovery">Masukkan %s Anda untuk melanjutkan</string>
     <string name="security_prompt_text">Verifikasi diri Anda dan lainnya untuk tetap membuat pesan Anda aman</string>
@@ -2027,8 +2027,8 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="room_created_summary_item_by_you">Anda membuat dan mengatur ruangan ini.</string>
     <string name="room_created_summary_item">%s membuat dan mengatur ruangan ini.</string>
     <string name="encryption_not_enabled">Enkripsi tidak diaktifkan</string>
-    <string name="direct_room_encryption_enabled_tile_description">Pesan di obrolan ini dienkripsi secara ujung-ke-ujung.</string>
-    <string name="encryption_enabled_tile_description">Pesan di ruangan ini dienkripsi secara ujung-ke-ujung. Pelajari lebih lanjut &amp; verifikasi pengguna di profil mereka.</string>
+    <string name="direct_room_encryption_enabled_tile_description">Pesan di obrolan ini dienkripsi secara ujung ke ujung.</string>
+    <string name="encryption_enabled_tile_description">Pesan di ruangan ini dienkripsi secara ujung ke ujung. Pelajari lebih lanjut &amp; verifikasi pengguna di profil mereka.</string>
     <string name="encryption_enabled">Enkripsi diaktifkan</string>
     <string name="bootstrap_cancel_text">Jika Anda batalkan, Anda mungkin kehilangan pesan terenkripsi dan data Anda jika Anda kehilangan akses ke login Anda.
 \n
@@ -2165,7 +2165,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="create_space_in_progress">Membuat space…</string>
     <string name="settings_developer_mode_show_info_on_screen_summary">Tampilkan info yang berguna untuk membantu debugging aplikasi</string>
     <string name="settings_developer_mode_show_info_on_screen_title">Tampilkan info debug di layar</string>
-    <string name="does_not_look_like_valid_email">Tidak terlihat sebagai alamat email yang valid</string>
+    <string name="does_not_look_like_valid_email">Tidak terlihat sebagai alamat email yang absah</string>
     <string name="open_discovery_settings">Buka Pengaturan Penemuan</string>
     <string name="user_directory_search_hint_2">Cari dengan nama, ID atau email</string>
     <string name="create_new_space">Buat Space Baru</string>
@@ -2173,7 +2173,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="room_settings_space_access_title">Akses space</string>
     <string name="room_settings_access_rules_pref_dialog_title">Siapa yang dapat akses\?</string>
     <string name="settings_notification_emails_enable_for_email">Aktifkan notifikasi email untuk %s</string>
-    <string name="settings_notification_emails_no_emails">Untuk menerima email dengan notifikasi, mohon tautkan sebuah email ke akun Matrix Anda</string>
+    <string name="settings_notification_emails_no_emails">Untuk menerima email dengan notifikasi, mohon tautkan sebuah alamat email ke akun Matrix Anda</string>
     <string name="settings_notification_emails_category">Notifikasi email</string>
     <string name="room_permissions_upgrade_the_space">Tingkatkan space ini</string>
     <string name="room_permissions_change_space_name">Ubah nama space</string>
@@ -2219,12 +2219,12 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="create_poll_question_title">Pertanyaan atau topik poll</string>
     <string name="create_poll_title">Buat Poll</string>
     <string name="attachment_type_poll">Poll</string>
-    <string name="identity_server_consent_dialog_title_2">Kirim email dan nomor telepon ke %s</string>
+    <string name="identity_server_consent_dialog_title_2">Kirim alamat email dan nomor telepon ke %s</string>
     <string name="settings_discovery_consent_notice_off_2">Kontak Anda privat. Untuk menemukan pengguna dari kontak Anda, kami membutuhkan izin untuk mengirim info kontak ke server identitas Anda.</string>
     <string name="shortcut_disabled_reason_sign_out">Sesinya telah dikeluarkan!</string>
     <string name="shortcut_disabled_reason_room_left">Ruangannya telah ditinggalkan!</string>
     <string name="identity_server_consent_dialog_content_question">Apakah Anda setuju untuk mengirimkan info ini\?</string>
-    <string name="identity_server_consent_dialog_content_3">Untuk menemukan kontak yang sudah ada, Anda harus mengirim info kontak (email dan nomor telepon) ke server identitas Anda. Kami meng-hash data Anda sebelum mengirim untuk privasi.</string>
+    <string name="identity_server_consent_dialog_content_3">Untuk menemukan kontak yang sudah ada, Anda harus mengirim info kontak (email dan nomor telepon) ke server identitas Anda. Kami hash data Anda sebelum mengirim demi privasi.</string>
     <string name="action_not_now">Nanti</string>
     <string name="delete_poll_dialog_content">Apakah Anda yakin untuk menghapus poll ini\? Anda tidak akan dapat memulihkannya setelah dihapus.</string>
     <string name="delete_poll_dialog_title">Hapus poll</string>
@@ -2298,17 +2298,17 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="poll_no_votes_cast">Tidak ada suara</string>
     <string name="encryption_misconfigured">Enkripsi dikonfigurasi dengan salah</string>
     <string name="room_profile_section_restore_security">Pulihkan Enkripsi</string>
-    <string name="contact_admin_to_restore_encryption">Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid.</string>
+    <string name="contact_admin_to_restore_encryption">Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang absah.</string>
     <string name="encryption_has_been_misconfigured">Enkripsi telah dikonfigurasi dengan salah.</string>
     <string name="sent_location">Membagikan lokasinya</string>
     <string name="login_splash_create_account">Buat akun</string>
     <string name="ftue_auth_carousel_workplace_title">Perpesanan untuk tim Anda.</string>
-    <string name="ftue_auth_carousel_encrypted_body">Terenkripsi secara ujung-ke-ujung dan tidak memerlukan nomor telepon. Tidak ada iklan atau penambangan data.</string>
+    <string name="ftue_auth_carousel_encrypted_body">Terenkripsi secara ujung ke ujung dan tidak memerlukan nomor telepon. Tanpa iklan atau penambangan data.</string>
     <string name="ftue_auth_carousel_control_body">Anda pilih di mana percakapan Anda disimpan, memberikan Anda kendali dan kebebasan. Terhubung via Matrix.</string>
     <string name="ftue_auth_carousel_secure_body">Komunikasi aman dan independen yang memberikan tingkat privasi yang sama seperti percakapan wajah-ke-wajah di dalam rumah Anda sendiri.</string>
     <string name="attachment_type_location">Lokasi</string>
     <string name="room_unsupported_e2e_algorithm_as_admin">Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Klik untuk membuka pengaturan.</string>
-    <string name="room_unsupported_e2e_algorithm">Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang valid.</string>
+    <string name="room_unsupported_e2e_algorithm">Enkripsi telah dikonfigurasi dengan salah sehingga Anda tidak dapat mengirim pesan. Mohon hubungi sebuah admin untuk memulihkan enkripsi ke status yang absah.</string>
     <string name="ftue_auth_use_case_skip">Belum yakin\? %s</string>
     <string name="message_bubbles">Tampilkan gelembung pesan</string>
     <string name="location_timeline_failed_to_load_map">Gagal untuk memuat peta</string>
@@ -2429,7 +2429,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="settings_show_latest_profile_description">Tampilkan info profil (avatar dan nama tampilan) terkini untuk semua pesan.</string>
     <string name="settings_show_latest_profile">Tampilkan info pengguna terkini</string>
     <string name="a11y_presence_busy">Sibuk</string>
-    <string name="keys_backup_settings_signature_from_this_user">Cadangan memiliki tandatangan yang valid dari pengguna ini.</string>
+    <string name="keys_backup_settings_signature_from_this_user">Cadangan memiliki tandatangan yang absah dari pengguna ini.</string>
     <string name="location_share_live_until">Langsung sampai %1$s</string>
     <string name="live_location_bottom_sheet_last_updated_at">Diperbarui %1$s yang lalu</string>
     <string name="labs_enable_live_location_summary">Implementasi sementara: lokasi tetap di riwayat ruangan</string>
@@ -2480,7 +2480,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="labs_enable_msc3061_share_history_desc">Ketika mengundang ke ruangan terenkripsi yang juga membagikan riwayat, riwayat terenkripsi akan dapat dilihat.</string>
     <string name="labs_enable_msc3061_share_history">MSC3061: Pembagian kunci ruangan untuk pesan lama</string>
     <string name="send_your_first_msg_to_invite">Kirim pesan pertama Anda untuk mengundang %s ke obrolan</string>
-    <string name="direct_room_encryption_enabled_tile_description_future">Pesan di obrolan ini akan dienkripsi secara ujung-ke-ujung.</string>
+    <string name="direct_room_encryption_enabled_tile_description_future">Pesan di obrolan ini akan dienkripsi secara ujung ke ujung.</string>
     <string name="create_room_action_go">Mulai</string>
     <string name="ftue_auth_password_reset_email_confirmation_subtitle">Ikuti petunjuk yang terkirim ke %s</string>
     <string name="ftue_auth_email_verification_subtitle">Ikuti petunjuk yang terkirim ke %s</string>
@@ -2658,4 +2658,37 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="labs_enable_deferred_dm_title">Aktifkan pesan langsung tangguhan</string>
     <string name="labs_enable_new_app_layout_summary">Sebuah Element yang sederhana dengan fitur tab opsional</string>
     <string name="labs_enable_new_app_layout_title">Aktifkan tata letak baru</string>
+    <string name="device_manager_learn_more_session_rename">Pengguna lain dalam pesan langsung dan ruangan yang Anda bergabung dapat melihat daftar sesi Anda yang lengkap.
+\n
+\nIni memberikan mereka kepastian bahwa mereka berbicara dengan Anda, tetapi ini juga berarti bahwa mereka dapat melihat nama sesi yang Anda masukkan di sini.</string>
+    <string name="device_manager_learn_more_session_rename_title">Mengubah nama sesi</string>
+    <string name="device_manager_learn_more_sessions_verified">Sesi yang terverifikasi telah masuk dengan kredensial Anda dan juga telah diverifikasi, menggunakan frasa sandi atau memverifikasi secara silang.
+\n
+\nIni berarti mereka memegang kunci enkripsi ke pesan Anda sebelumnya, dan mengonfirmasi pengguna lain yang Anda berkomunikasi bahwa sesi ini memang Anda.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Sesi tidak aktif</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Sesi belum diverifikasi</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Sesi terverifikasi</string>
+    <string name="device_manager_learn_more_sessions_unverified">Sesi yang belum diverifikasi adalah sesi yang telah masuk dengan kredensial Anda tetapi belum diverifikasi secara silang.
+\n
+\nAnda seharusnya yakin bahwa Anda mengenal sesi ini karena mereka bisa saja berarti seseorang menggunakan akun Anda secara tidak sah.</string>
+    <string name="device_manager_learn_more_sessions_inactive">Sesi yang tidak aktif adalah sesi yang Anda tidak gunakan dalam beberapa waktu, tetapi mereka masih mendapatkan kunci enkripsi.
+\n
+\nMenghapus sesi yang sudah tidak aktif meningkatkan keamanan dan performa, dan membuatnya lebih mudah untuk mengenal jika sebuah sesi baru mencurigakan.</string>
+    <string name="device_manager_session_rename_warning">Harap diketahui bahwa nama sesi juga terlihat ke orang-orang yang Anda berkomunikasi.</string>
+    <string name="device_manager_session_rename_description">Nama sesi khusus dapat membantu Anda mengenal perangkat Anda dengan lebih mudah.</string>
+    <string name="device_manager_session_rename_edit_hint">Nama sesi</string>
+    <string name="device_manager_session_rename">Ubah nama sesi</string>
+    <string name="device_manager_session_overview_signout">Keluar dari sesi ini</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Belum diverifikasi · Sesi Anda saat ini</string>
+    <string name="tooltip_attachment_voice_broadcast">Mulai sebuah siaran suara</string>
+    <string name="key_authenticity_not_guaranteed">Keaslian pesan terenkripsi ini tidak dapat dijamin pada perangkat ini.</string>
+    <string name="settings_security_incognito_keyboard_summary">Minta papan ketik untuk tidak memperbarui data yang dipersonalisasi seperti riwayat pengetikan dan kamus berdasarkan apa yang Anda ketik dalam percakapan. Dicatat bahwa beberapa papan ketik mungkin tidak menghormati pengaturan ini.</string>
+    <string name="settings_security_incognito_keyboard_title">Papan ketik samaran</string>
+    <string name="command_description_table_flip">Menambahkan (╯°□°)╯︵ ┻━┻ ke pesan teks biasa</string>
+    <string name="attachment_type_voice_broadcast">Siaran Suara</string>
+    <string name="command_description_devtools">Buka layar alat pengembang</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Anda telah mengaktifkan enkripsi ke sesi yang terverifikasi hanya untuk semua ruangan di Pengaturan Keamanan.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Ada perangkat yang belum diverifikasi di ruangan ini, mereka tidak akan mendekripsikan pesan yang Anda kirim.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Jangan kirim pesan terenkripsi ke sesi yang belum diverifikasi di ruangan ini.</string>
+    <string name="action_got_it">Saya mengerti</string>
 </resources>
\ No newline at end of file

From 0e8c3e99ec7c5ed1a018e64004a2a8b1b9a56027 Mon Sep 17 00:00:00 2001
From: lvre <7uu3qrbvm@relay.firefox.com>
Date: Sun, 9 Oct 2022 20:26:56 +0000
Subject: [PATCH 069/400] Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/
---
 .../src/main/res/values-pt-rBR/strings.xml    | 63 ++++++++++++++-----
 1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
index 108ecc7e38..8d89aa29bd 100644
--- a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
+++ b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
@@ -1053,12 +1053,12 @@
     <string name="settings_discovery_identity_server_info">Você está atualmente usando %1$s para descobrir e ser descobertável por contatos existentes que você conhece.</string>
     <string name="settings_discovery_identity_server_info_none">Você não está atualmente usando um servidor de identidade. Para descobrir e ser descobertável por contatos existentes que você conhece, configure um abaixo.</string>
     <string name="settings_discovery_emails_title">Endereços de email descobertáveis</string>
-    <string name="settings_discovery_no_mails">Opções de descoberta vão aparecer uma vez que você tenha adicionado um email.</string>
+    <string name="settings_discovery_no_mails">Opções de descoberta vão aparecer uma vez que você tenha adicionado um endereço de email.</string>
     <string name="settings_discovery_no_msisdn">Opções de descoberta vão aparecer uma vez que você tenha adicionado um número de telefone.</string>
     <string name="settings_discovery_disconnect_identity_server_info">Desconectar-se de seu servidor de identidade vai significar que você não vai ser descobertável por outras(os) usuárias(os) e você não vai ser capaz de convidar outras(os) por email ou telefone.</string>
     <string name="settings_discovery_msisdn_title">Números de telefone descobertáveis</string>
-    <string name="settings_discovery_confirm_mail">Nós enviamos a você um email de confirmar para %s, cheque seu email e clique no link de confirmação</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Nós enviamos a você um email de confirmar para %s, por favor primeiro cheque seu email e clique no link de confirmação</string>
+    <string name="settings_discovery_confirm_mail">Nós enviamos um email para %s, cheque seu email e clique no link de confirmação</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Nós enviamos um email para %s, por favor primeiro cheque seu email e clique no link de confirmação</string>
     <string name="settings_discovery_enter_identity_server">Entre um URL de servidor de identidade</string>
     <string name="settings_discovery_bad_identity_server">Não foi possível conectar-se a servidor de identidade</string>
     <string name="settings_discovery_please_enter_server">Por favor entre o url de servidor de identidade</string>
@@ -1171,7 +1171,7 @@
     <string name="login_registration_not_supported">O aplicativo não é capaz de criar uma conta neste servidorcasa.
 \n
 \nVocê quer fazer signup usando um cliente web\?</string>
-    <string name="login_login_with_email_error">Este email não está associado com nenhuma conta.</string>
+    <string name="login_login_with_email_error">Este endereço de email não está associado a nenhuma conta.</string>
     <string name="login_reset_password_on">Resettar senha em %1$s</string>
     <string name="login_reset_password_notice">Um email de verificação vai ser enviado para sua inbox para confirmar definição de sua nova senha.</string>
     <string name="login_reset_password_submit">Próximo</string>
@@ -1180,7 +1180,7 @@
     <string name="login_reset_password_warning_title">Aviso!</string>
     <string name="login_reset_password_warning_content">Mudar sua senha vai resettar quaisquer chaves de encriptação ponta-a-ponta em todas as suas sessões, fazendo histórico de chat encriptado ilegível. Configure Backup de Chave ou exporte suas chaves de sala de uma outra sessão antes de resettar sua senha.</string>
     <string name="login_reset_password_warning_submit">Continuar</string>
-    <string name="login_reset_password_error_not_found">Este email não está linkado a nenhuma conta</string>
+    <string name="login_reset_password_error_not_found">Este endereço de email não está linkado a nenhuma conta</string>
     <string name="login_reset_password_mail_confirmation_title">Cheque sua inbox</string>
     <string name="login_reset_password_mail_confirmation_notice">Um email de verificação foi enviado para %1$s.</string>
     <string name="login_reset_password_mail_confirmation_notice_2">Toque no link para confirmar sua nova senha. Uma vez que você tenha seguido o link que ele contém, clique abaixo.</string>
@@ -1194,7 +1194,7 @@
 \n
 \nPara o processo de mudança de senha\?</string>
     <string name="login_set_email_title">Definir endereço de email</string>
-    <string name="login_set_email_notice">Defina um email para recuperar sua conta. Mais tarde, você pode opcionalmente permitir pessoas que você conhece descobrirem você por seu email.</string>
+    <string name="login_set_email_notice">Defina um endereço de email para recuperar sua conta. Mais tarde, você pode opcionalmente permitir pessoas que você conhece descobrirem você por este endereço.</string>
     <string name="login_set_email_mandatory_hint">Email</string>
     <string name="login_set_email_optional_hint">Email (opcional)</string>
     <string name="login_set_email_submit">Próximo</string>
@@ -1560,7 +1560,7 @@
     <string name="identity_server_error_outdated_home_server">Esta operação não é possível. O servidorcasa está desatualizado.</string>
     <string name="identity_server_error_no_identity_server_configured">Por favor primeiro configure um servidor de identidade.</string>
     <string name="identity_server_error_terms_not_signed">Por favor primeiro aceite os termos do servidor de identidade nas configurações.</string>
-    <string name="identity_server_error_bulk_sha256_not_supported">Para sua privacidade, ${app_name} somente suporta enviar emails e números de telefone de usuária(o) hashados.</string>
+    <string name="identity_server_error_bulk_sha256_not_supported">Para sua privacidade, ${app_name} somente suporta enviar endereços de email e números de telefone de usuária(o) hashados.</string>
     <string name="identity_server_error_binding_error">A associação tem falhado.</string>
     <string name="identity_server_error_no_current_binding_error">Não há nenhuma associação atual com este identificador.</string>
     <string name="identity_server_set_default_notice">Seu servidorcasa (%1$s) propõe usar %2$s para seu servidor de identidade</string>
@@ -1654,7 +1654,7 @@
     <string name="no_permissions_to_start_webrtc_call">Você não tem permissão para começar uma chamada nesta sala</string>
     <string name="settings_phone_number_empty">Nenhum número de telefone tem sido adicionado a sua conta</string>
     <string name="settings_emails">Endereços de email</string>
-    <string name="settings_emails_empty">Nenhum email tem sido adicionado a sua conta</string>
+    <string name="settings_emails_empty">Nenhum endereço de email tem sido adicionado a sua conta</string>
     <string name="settings_phone_numbers">Números de telefone</string>
     <string name="settings_remove_three_pid_confirmation_content">Remover %s\?</string>
     <string name="error_threepid_auth_failed">Assegure-se que você tem clicado no link no email que enviamos para você.</string>
@@ -1663,7 +1663,7 @@
         <item quantity="other">%d segundos</item>
     </plurals>
     <string name="settings_emails_and_phone_numbers_title">Emails e números de telefone</string>
-    <string name="settings_emails_and_phone_numbers_summary">Gerenciar emails e números de telefone linkados a sua conta Matrix</string>
+    <string name="settings_emails_and_phone_numbers_summary">Gerenciar endereços de email e números de telefone linkados a sua conta Matrix</string>
     <string name="settings_text_message_sent_hint">Código</string>
     <string name="login_msisdn_notice">Por favor use o formato internacional (número de telefone deve começar com \'+\')</string>
     <string name="confirm_your_identity_quad_s">Confirme sua identidade ao verificar este login, concedendo-lhe acesso a mensagens encriptadas.</string>
@@ -1778,7 +1778,7 @@
     <string name="attachment_viewer_item_x_of_y">%1$d de %2$d</string>
     <string name="settings_discovery_consent_action_give_consent">Dar consentimento</string>
     <string name="settings_discovery_consent_action_revoke">Revogar meu consentimento</string>
-    <string name="settings_discovery_consent_notice_on">Você tem dado seu consentimento para enviar emails e números de telefone para este servidor de identidade para descobrir outras(os) usuárias(os) de seus contatos.</string>
+    <string name="settings_discovery_consent_notice_on">Você tem dado seu consentimento para enviar endereços de email e números de telefone para este servidor de identidade para descobrir outras(os) usuárias(os) de seus contatos.</string>
     <string name="settings_discovery_consent_title">Enviar emails e números de telefone</string>
     <string name="direct_room_user_list_suggestions_title">Sugestões</string>
     <string name="direct_room_user_list_known_title">Usuárias(os) Conhecidas(os)</string>
@@ -2142,7 +2142,7 @@
     <string name="settings_notification_mentions_and_keywords">Menções e Palavrachaves</string>
     <string name="settings_notification_default">Notificações Default</string>
     <string name="link_this_email_with_your_account">%s em Configurações para receber convites diretamente em ${app_name}.</string>
-    <string name="link_this_email_settings_link">Linkar este email com sua conta</string>
+    <string name="link_this_email_settings_link">Linkar este endereço de email com sua conta</string>
     <string name="this_invite_to_this_space_was_sent">Este convite para este espaço foi enviado para %s que não está associado com sua conta</string>
     <string name="this_invite_to_this_room_was_sent">Este convite para esta sala foi enviado para %s que não está associado com sua conta</string>
     <string name="all_rooms_youre_in_will_be_shown_in_home">Todas as salas em que você está vão ser mostradas em Home.</string>
@@ -2211,7 +2211,7 @@
     <string name="room_settings_space_access_title">Acesso a espaço</string>
     <string name="room_settings_access_rules_pref_dialog_title">Quem pode acessar\?</string>
     <string name="settings_notification_emails_enable_for_email">Habilitar notificações de email para %s</string>
-    <string name="settings_notification_emails_no_emails">Para receber email com notificação, por favor associe um email a sua conta Matrix</string>
+    <string name="settings_notification_emails_no_emails">Para receber email com notificação, por favor associe um endereço de email a sua conta Matrix</string>
     <string name="settings_notification_emails_category">Notificação de email</string>
     <string name="room_permissions_upgrade_the_space">Fazer upgrade do espaço</string>
     <string name="room_permissions_change_space_name">Mudar nome de espaço</string>
@@ -2258,12 +2258,12 @@
     <string name="create_poll_question_title">Sondar pergunta ou tópico</string>
     <string name="create_poll_title">Criar Sondagem</string>
     <string name="attachment_type_poll">Sondagem</string>
-    <string name="identity_server_consent_dialog_title_2">Enviar emails e números de telefone para %s</string>
+    <string name="identity_server_consent_dialog_title_2">Enviar endereços de email e números de telefone para %s</string>
     <string name="settings_discovery_consent_notice_off_2">Seus contatos são privados. Para descobrir usuárias(os) de seus contatos, você precisa de permissão para enviar info de contato a seu servidor de identidade.</string>
     <string name="shortcut_disabled_reason_sign_out">O signout desta sessão tem sido feito!</string>
     <string name="shortcut_disabled_reason_room_left">Esta sala tem sido saída!</string>
     <string name="identity_server_consent_dialog_content_question">Você concorda em enviar esta info\?</string>
-    <string name="identity_server_consent_dialog_content_3">Para descobrir contatos existentes, você precisa enviar info de contato (emails e números de telefone) para seu servidor de identidade. Nós hashamos seus dados antes de enviar por privacidade.</string>
+    <string name="identity_server_consent_dialog_content_3">Para descobrir contatos existentes, você precisa enviar info de contato (endereços de email e números de telefone) para seu servidor de identidade. Nós hashamos seus dados antes de enviar por privacidade.</string>
     <string name="action_not_now">Não agora</string>
     <string name="delete_poll_dialog_content">Você tem certeza que você quer remover esta sondagem\? Você não vai ser capaz de recuperá-la uma vez removida.</string>
     <string name="delete_poll_dialog_title">Remover sondagem</string>
@@ -2710,4 +2710,37 @@
     <string name="labs_enable_deferred_dm_title">Habilitar DMs diferidas</string>
     <string name="labs_enable_new_app_layout_summary">Um Element simplificado com abas opcionais</string>
     <string name="labs_enable_new_app_layout_title">Habilitar novo layout</string>
-</resources>
+    <string name="device_manager_learn_more_session_rename">Outras(os) usuárias(os) em mensagens diretas e salas a que você se junta são capazes de visualizar uma lista completa de suas sessões.
+\n
+\nIsto as/os provê com confiança que elas(es) são estão realmente falando com você, mas também significa que elas(es) veem o nome da sessão que você entrar aqui.</string>
+    <string name="device_manager_learn_more_session_rename_title">Renomeando sessões</string>
+    <string name="device_manager_learn_more_sessions_verified">Sessões verificadas têm feito login com suas credenciais e então têm sido verificadas, ou usando sua frasepasse segura ou por verificação cruzada.
+\n
+\nIsto significa que elas mantêm chaves de encriptação para suas mensagens anteriores, e confirmam a outras(os) usuárias(os) com quem você está comunicando que estas sessões são realmente você.</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Sessões verificadas</string>
+    <string name="device_manager_learn_more_sessions_unverified">Sessões não-verificadas são sessões que você tem feito login com suas credenciais mas não têm sido verificadas cruzado.
+\n
+\nVocê devia especialmente se certificar que você reconhece estas sessões já que elas podiam representar um uso não-autorizado de sua conta.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Sessões não-verificadas</string>
+    <string name="device_manager_learn_more_sessions_inactive">Sessões inativas são sessões que você não tem usado em algum tempo, mas elas continuam a receber chaves de encriptação.
+\n
+\nRemover sessões inativas melhora segurança e performance, e torna-o mais fácil para você identificar se uma nova sessão é suspeita.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Sessões inativas</string>
+    <string name="device_manager_session_rename_warning">Por favor esteja ciente que nomes de sessões também são visíveis a pessoas com quem você se comunica.</string>
+    <string name="device_manager_session_rename_description">Nomes de sessões personalizadas podem ajudar você a reconhecer seus dispositivos mais facilmente.</string>
+    <string name="device_manager_session_rename_edit_hint">Nome da sessão</string>
+    <string name="device_manager_session_rename">Renomear sessão</string>
+    <string name="device_manager_session_overview_signout">Fazer signout desta sessão</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Não-verificada · Sua sessão atual</string>
+    <string name="tooltip_attachment_voice_broadcast">Começar um broadcast de voz</string>
+    <string name="key_authenticity_not_guaranteed">A autenticidade desta mensagem encriptada não pode ser garantida neste dispositivo.</string>
+    <string name="settings_security_incognito_keyboard_summary">Requisitar que o teclado não devia atualizar quaisquer dados personalizados tais como histórico de digitação e dicionário baseado no que você tem digitado em conversas. Note que alguns teclados podem não respeitar esta configuração.</string>
+    <string name="settings_security_incognito_keyboard_title">Teclado incognito</string>
+    <string name="command_description_table_flip">Prepende (╯°□°)╯︵ ┻━┻ a uma mensagem de texto puro</string>
+    <string name="attachment_type_voice_broadcast">Broadcast de Voz</string>
+    <string name="command_description_devtools">Abrir tela de ferramentas de desenvolvedor(a)</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Você tem habilitado encriptar para sessões verificadas somente para todas as salas em Configurações de Segurança.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Existem dispositivos não-verificados nesta sala, eles não vão ser capazes de decriptar mensagens que você enviar.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Nunca enviar mensagens encriptadas a sessões não-verificadas nesta sala.</string>
+    <string name="action_got_it">Entendido</string>
+</resources>
\ No newline at end of file

From 06664fb15e7c6576d6ff957826e53edfad502d16 Mon Sep 17 00:00:00 2001
From: Nui Harime <harime.nui@yandex.ru>
Date: Tue, 11 Oct 2022 09:56:53 +0000
Subject: [PATCH 070/400] Translated using Weblate (Russian)

Currently translated at 97.0% (2372 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ru/
---
 library/ui-strings/src/main/res/values-ru/strings.xml | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml
index 409cea7b13..cd1a63a7ac 100644
--- a/library/ui-strings/src/main/res/values-ru/strings.xml
+++ b/library/ui-strings/src/main/res/values-ru/strings.xml
@@ -1368,7 +1368,7 @@
     <string name="autocomplete_limited_results">Показываем только первые результаты, наберите больше букв…</string>
     <string name="settings_developer_mode_fail_fast_title">Раннее падение</string>
     <string name="settings_developer_mode_fail_fast_summary">${app_name} может падать чаще, когда происходит непредвиденная ошибка</string>
-    <string name="command_description_shrug">Добавляет смайл ¯\\_(ツ)_/¯ в начало сообщения</string>
+    <string name="command_description_shrug">Добавляет ¯\\_(ツ)_/¯ в начало сообщения</string>
     <string name="create_room_encryption_description">После включения шифрования его нельзя отключить.</string>
     <string name="login_error_threepid_denied">Ваш почтовый домен не имеет права регистрироваться на этом сервере</string>
     <string name="verification_conclusion_not_secure">Не безопасно</string>
@@ -2275,7 +2275,7 @@
     <string name="room_settings_space_access_title">Доступ к пространству</string>
     <string name="room_settings_access_rules_pref_dialog_title">Кто имеет к этому доступ\?</string>
     <string name="settings_notification_emails_enable_for_email">Включить уведомления по электронной почте для %s</string>
-    <string name="settings_notification_emails_no_emails">Чтобы получать уведомления по электронной почте, пожалуйста, привяжите электронную почту к вашей учетной записи Matrix</string>
+    <string name="settings_notification_emails_no_emails">Чтобы получать уведомления по электронной почте, пожалуйста, привяжите адрес электронной почты к своей учётной записи Matrix</string>
     <string name="settings_notification_emails_category">Уведомление по эл. почте</string>
     <string name="room_permissions_upgrade_the_space">Обновить пространство</string>
     <string name="room_permissions_change_space_name">Изменить название пространства</string>
@@ -2753,4 +2753,7 @@
     <string name="device_manager_learn_more_sessions_verified_title">Заверенные сессии</string>
     <string name="device_manager_learn_more_sessions_unverified_title">Незаверенные сессии</string>
     <string name="device_manager_learn_more_sessions_inactive_title">Неактивные сессии</string>
+    <string name="command_description_table_flip">Добавляет (╯°□°)╯︵ ┻━┻ в начало сообщения</string>
+    <string name="settings_security_incognito_keyboard_title">Приватная клавиатура</string>
+    <string name="settings_security_incognito_keyboard_summary">Запрещает клавиатуре обновлять персональные данные, такие как история набора текста и словарь, на основе того, что вы набрали при общении. Обратите внимание, что некоторые клавиатуры могут не соблюдать эту настройку.</string>
 </resources>
\ No newline at end of file

From 02d6dca96a17589a8a6e0d78a476ad1f05d01193 Mon Sep 17 00:00:00 2001
From: Jozef Gaal <preklady@mayday.sk>
Date: Mon, 10 Oct 2022 20:45:36 +0000
Subject: [PATCH 071/400] Translated using Weblate (Slovak)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/
---
 library/ui-strings/src/main/res/values-sk/strings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values-sk/strings.xml b/library/ui-strings/src/main/res/values-sk/strings.xml
index e479e932d4..467fc8b06f 100644
--- a/library/ui-strings/src/main/res/values-sk/strings.xml
+++ b/library/ui-strings/src/main/res/values-sk/strings.xml
@@ -2651,7 +2651,7 @@
     <string name="a11y_open_settings">Otvoriť nastavenia</string>
     <string name="all_chats">Všetky konverzácie</string>
     <string name="device_manager_settings_active_sessions_show_all">Zobraziť všetky relácie (V2, WIP)</string>
-    <string name="device_manager_sessions_other_description">V záujme čo najlepšieho zabezpečenia overte svoje relácie a odhláste sa z každej relácie, ktorú už nepoznáte alebo nepoužívate.</string>
+    <string name="device_manager_sessions_other_description">V záujme čo najlepšieho zabezpečenia, overte svoje relácie a odhláste sa z každej relácie, ktorú už nepoznáte alebo nepoužívate.</string>
     <string name="device_manager_sessions_other_title">Iné relácie</string>
     <string name="settings_sessions_list">Relácie</string>
     <string name="a11y_open_spaces">Otvoriť zoznam priestorov</string>

From 05d2f2a8575899dba46c740315a9ec7420839aa3 Mon Sep 17 00:00:00 2001
From: LinAGKar <linus.kardell@gmail.com>
Date: Sun, 9 Oct 2022 18:48:53 +0000
Subject: [PATCH 072/400] Translated using Weblate (Swedish)

Currently translated at 97.1% (2375 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/sv/
---
 .../src/main/res/values-sv/strings.xml        | 40 ++++++++++++++++++-
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-sv/strings.xml b/library/ui-strings/src/main/res/values-sv/strings.xml
index c34f302a4f..89697e74ea 100644
--- a/library/ui-strings/src/main/res/values-sv/strings.xml
+++ b/library/ui-strings/src/main/res/values-sv/strings.xml
@@ -570,8 +570,8 @@
     <string name="settings_discovery_no_msisdn">Upptäckbarhetsalternativ kommer att synas när du har lagt till ett telefonnummer.</string>
     <string name="settings_discovery_disconnect_identity_server_info">Om du kopplar bort från din identitetsserver så kommer du inte att vara upptäckbar av andra användare och du kommer inte kunna bjuda in folk med hjälp av deras e-postadresser eller telefonnummer.</string>
     <string name="settings_discovery_msisdn_title">Upptäckbara telefonnummer</string>
-    <string name="settings_discovery_confirm_mail">Vi skickade ett bekräftelse-e-brev till %s, kolla din e-post och klicka på bekräftelselänken</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Vi skickade ett bekräftelse-e-brev till %s, vänligen kolla din e-post och klicka på bekräftelselänken</string>
+    <string name="settings_discovery_confirm_mail">Vi skickade ett e-brev till %s, kolla din e-post och klicka på bekräftelselänken</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">Vi skickade ett e-brev till %s, vänligen kolla din e-post och klicka på bekräftelselänken</string>
     <string name="settings_discovery_enter_identity_server">Skriv in en identitetsserver-URL</string>
     <string name="settings_discovery_bad_identity_server">Kunde inte ansluta till identitetsserver</string>
     <string name="settings_discovery_please_enter_server">Vänligen ange en identitetsserver-URL</string>
@@ -2616,4 +2616,40 @@
     <string name="create_room">Skapa rum</string>
     <string name="start_chat">Starta chatt</string>
     <string name="all_chats">Alla chattar</string>
+    <string name="device_manager_verification_status_detail_other_session_verified">Den här sessionen är redo för säkra meddelanden.</string>
+    <string name="device_manager_verification_status_detail_current_session_verified">Din nuvarande session är redo för säkra meddelanden.</string>
+    <string name="device_manager_verification_status_unverified">Overifierad session</string>
+    <string name="device_manager_verification_status_verified">Verifierad session</string>
+    <string name="a11y_device_manager_device_type_unknown">Okänd enhetstyp</string>
+    <string name="a11y_device_manager_device_type_desktop">Skrivbord</string>
+    <string name="a11y_device_manager_device_type_web">Webb</string>
+    <string name="a11y_device_manager_device_type_mobile">Mobil</string>
+    <string name="device_manager_sessions_other_description">För bäst säkerhet, verifiera dina sessioner och logga ut från alla sessioner du inte känner igen eller använder längre.</string>
+    <string name="device_manager_sessions_other_title">Andra sessioner</string>
+    <string name="device_manager_settings_active_sessions_show_all">Visa alla sessioner (V2, WIP)</string>
+    <string name="labs_enable_element_call_permission_shortcuts_summary">Godkänn automatiskt Element Call-widgets och ge kamera-/mikrofonåtkomst</string>
+    <string name="labs_enable_element_call_permission_shortcuts">Aktivera Element Call-behörighetsgenvägar</string>
+    <string name="tooltip_attachment_voice_broadcast">Starta en röstsändning</string>
+    <string name="live_location_description">Realtidsplats</string>
+    <string name="location_share_loading_map_error">Kunde inte ladda kartan.
+\nDen här hemservern kanske inte är konfigurerad för at visa kartor.</string>
+    <string name="a11y_open_settings">Öppna inställningar</string>
+    <string name="key_authenticity_not_guaranteed">Autenticiteten för det krypterade meddelanden kan inte garanteras på den här enheten.</string>
+    <string name="settings_security_incognito_keyboard_summary">Begär att tangentbordet inte ska uppdatera någon personanpassad data som skrivhistorik och ordlista baserat på vad du har skrivit i konversationer. Observera att vissa tangentbord inte respekterar den här inställningen.</string>
+    <string name="settings_security_incognito_keyboard_title">Inkognitotangentbord</string>
+    <string name="verify_invalid_qr_notice">Det här QR-koden ser ogiltig ut. Vänligen pröva igen för att verifiera med en annan metod.</string>
+    <string name="crosssigning_cannot_verify_this_session_desc">Du kommer inte komma åt krypterad meddelandehistorik. Återställ din säkra meddelandesäkerhetskopia och verifieringsnycklar för att börja om.</string>
+    <string name="crosssigning_cannot_verify_this_session">Kunde inte verifiera den här enheten</string>
+    <string name="settings_sessions_list">Sessioner</string>
+    <string name="command_description_table_flip">Lägger till (╯°□°)╯︵ ┻━┻ till början av ett textmeddelande</string>
+    <string name="ftue_auth_choose_server_sign_in_subtitle">Vad är din servers adress\?</string>
+    <string name="ftue_auth_sign_in_choose_server_header">Där dina konversationer bor</string>
+    <string name="attachment_type_voice_broadcast">Röstsändning</string>
+    <string name="a11y_open_spaces">Öppna utrymmeslista</string>
+    <string name="a11y_create_message">Skapa den ny konversation eller ett nytt rum</string>
+    <string name="updating_your_data">Uppdaterar din data…</string>
+    <string name="room_list_filter_people">Personer</string>
+    <string name="room_list_filter_favourites">Favoriter</string>
+    <string name="room_list_filter_unreads">Olästa</string>
+    <string name="room_list_filter_all">Alla</string>
 </resources>
\ No newline at end of file

From e28f3438acdb7e5cd68b64867bc93a72ceae5b9f Mon Sep 17 00:00:00 2001
From: lvre <7uu3qrbvm@relay.firefox.com>
Date: Sun, 9 Oct 2022 19:54:12 +0000
Subject: [PATCH 073/400] Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/pt_BR/
---
 fastlane/metadata/android/pt-BR/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/pt-BR/changelogs/40105020.txt | 2 ++
 2 files changed, 4 insertions(+)
 create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/pt-BR/changelogs/40105000.txt b/fastlane/metadata/android/pt-BR/changelogs/40105000.txt
new file mode 100644
index 0000000000..d8f3a953a3
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Principais mudanças nesta versão: DM diferida habilitada por default.
+Changelog completo: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/pt-BR/changelogs/40105020.txt b/fastlane/metadata/android/pt-BR/changelogs/40105020.txt
new file mode 100644
index 0000000000..d7cca674fa
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Principais mudanças nesta versão: Novo layout de app habilitado por default!
+Changelog completo: https://github.com/vector-im/element-android/releases

From 6558afea1ce9287b58025a24313e75ee67a8fb01 Mon Sep 17 00:00:00 2001
From: LinAGKar <linus.kardell@gmail.com>
Date: Sun, 9 Oct 2022 18:16:57 +0000
Subject: [PATCH 074/400] Translated using Weblate (Swedish)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/sv/
---
 fastlane/metadata/android/sv-SE/changelogs/40104300.txt | 2 ++
 fastlane/metadata/android/sv-SE/changelogs/40104310.txt | 2 ++
 fastlane/metadata/android/sv-SE/changelogs/40104320.txt | 2 ++
 fastlane/metadata/android/sv-SE/changelogs/40104340.txt | 2 ++
 fastlane/metadata/android/sv-SE/changelogs/40104360.txt | 3 +++
 fastlane/metadata/android/sv-SE/changelogs/40105000.txt | 2 ++
 6 files changed, 13 insertions(+)
 create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104300.txt
 create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104310.txt
 create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104320.txt
 create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104340.txt
 create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104360.txt
 create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40105000.txt

diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104300.txt b/fastlane/metadata/android/sv-SE/changelogs/40104300.txt
new file mode 100644
index 0000000000..dcf7fd896c
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40104300.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Aktiverar förbättrad inloggnings- och registreringsprocedur.
+Full ändringslogg: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104310.txt b/fastlane/metadata/android/sv-SE/changelogs/40104310.txt
new file mode 100644
index 0000000000..dcf7fd896c
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40104310.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Aktiverar förbättrad inloggnings- och registreringsprocedur.
+Full ändringslogg: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104320.txt b/fastlane/metadata/android/sv-SE/changelogs/40104320.txt
new file mode 100644
index 0000000000..d8db452b51
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40104320.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Diverse buggfixar och stabilitetsförbättringar.
+Full ändringslogg: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104340.txt b/fastlane/metadata/android/sv-SE/changelogs/40104340.txt
new file mode 100644
index 0000000000..d8db452b51
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40104340.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Diverse buggfixar och stabilitetsförbättringar.
+Full ändringslogg: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104360.txt b/fastlane/metadata/android/sv-SE/changelogs/40104360.txt
new file mode 100644
index 0000000000..1f2c984748
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40104360.txt
@@ -0,0 +1,3 @@
+Ny applayout kan aktiveras in experimentinställningarna. Ge det ett försök!
+Fixa problem med missade aviseringar, och long inkrementell synk.
+Full ändringslogg: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/sv-SE/changelogs/40105000.txt b/fastlane/metadata/android/sv-SE/changelogs/40105000.txt
new file mode 100644
index 0000000000..f425ec24e8
--- /dev/null
+++ b/fastlane/metadata/android/sv-SE/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Huvudsakliga ändringar i den här versionen: Fördröjda DM:er aktiverad som förval.
+Full ändringslogg: https://github.com/vector-im/element-android/releases

From 2efd3b4a33185d94d5c0f1c847ef1d12913aef44 Mon Sep 17 00:00:00 2001
From: Linerly <linerly@protonmail.com>
Date: Mon, 10 Oct 2022 11:12:19 +0000
Subject: [PATCH 075/400] Translated using Weblate (Indonesian)

Currently translated at 100.0% (78 of 78 strings)

Translation: Element Android/Element Android Store
Translate-URL: https://translate.element.io/projects/element-android/element-store/id/
---
 fastlane/metadata/android/id/changelogs/40105000.txt | 2 ++
 fastlane/metadata/android/id/changelogs/40105020.txt | 2 ++
 fastlane/metadata/android/id/full_description.txt    | 6 +++---
 3 files changed, 7 insertions(+), 3 deletions(-)
 create mode 100644 fastlane/metadata/android/id/changelogs/40105000.txt
 create mode 100644 fastlane/metadata/android/id/changelogs/40105020.txt

diff --git a/fastlane/metadata/android/id/changelogs/40105000.txt b/fastlane/metadata/android/id/changelogs/40105000.txt
new file mode 100644
index 0000000000..08e3e78300
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/40105000.txt
@@ -0,0 +1,2 @@
+Perubahan utama dalam versi ini: Pesan langsung yang ditangguhkan diaktifkan secara bawaan.
+Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/id/changelogs/40105020.txt b/fastlane/metadata/android/id/changelogs/40105020.txt
new file mode 100644
index 0000000000..736d2c48a6
--- /dev/null
+++ b/fastlane/metadata/android/id/changelogs/40105020.txt
@@ -0,0 +1,2 @@
+Perubahan utama dalam versi ini: Tata letak aplikasi baru diaktifkan secara bawaan!
+Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases
diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt
index 20d805c582..79baa1f9ac 100644
--- a/fastlane/metadata/android/id/full_description.txt
+++ b/fastlane/metadata/android/id/full_description.txt
@@ -1,4 +1,4 @@
-Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas yang ideal untuk obrolan grup saat bekerja jarak jauh. Aplikasi perpesanan ini menggunakan enkripsi ujung-ke-ujung untuk menyediakan konferensi video, pembagian berkas, dan panggilan suara yang aman.
+Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas yang ideal untuk obrolan grup saat bekerja jarak jauh. Aplikasi perpesanan ini menggunakan enkripsi ujung ke ujung untuk menyediakan konferensi video, pembagian berkas, dan panggilan suara yang aman.
 
 <b>Fitur Element termasuk:</b>
 - Alat komunikasi daring yang canggih
@@ -11,7 +11,7 @@ Element adalah perpesanan yang aman dan aplikasi kolaborasi tim produktivitas ya
 Element benar-benar berbeda dari aplikasi perpesanan dan aplikasi kolaborasi lainnya. Element beroperasi pada Matrix, jaringan terbuka untuk pengiriman pesan yang aman dan komunikasi yang terdesentralisasi.
 
 <b>Perpesanan dengan privasi dan enkripsi</b>
-Element melindungi Anda dari iklan yang tidak diinginkan, penambangan data, dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu dengan enkripsi ujung-ke-ujung, dan verifikasi perangkat menggunakan penandatanganan silang.
+Element melindungi Anda dari iklan yang tidak diinginkan, penambangan data, dan taman berdinding. Element juga mengamankan semua data Anda, komunikasi video dan suara satu-ke-satu dengan enkripsi ujung ke ujung, dan verifikasi perangkat menggunakan penandatanganan silang.
 
 Element memberikan Anda kendali atas privasi Anda sambil memungkinkan Anda untuk berkomunikasi dengan siapa saja secara aman di jaringan Matrix, atau alat kolaborasi bisnis lainnya dengan mengintegrasikan aplikasi seperti Slack.
 
@@ -30,7 +30,7 @@ Element menempatkan Anda dalam kendali dengan cara yang berbeda:
 Anda dapat mengobrol dengan siapa saja di jaringan Matrix, jika mereka menggunakan Element, aplikasi Matrix lain, atau bahkan menggunakan aplikasi perpesanan yang berbeda.
 
 <b>Sangat aman</b>
-Enkripsi ujung-ke-ujung yang nyata (hanya mereka yang di dalam obrolan dapat mendekripsikan pesan), dan verifikasi perangkat menggunakan penandatanganan silang.
+Enkripsi ujung ke ujung yang nyata (hanya mereka yang di dalam obrolan dapat mendekripsikan pesan), dan verifikasi perangkat menggunakan penandatanganan silang.
 
 <b>Komunikasi dan integrasi lengkap</b>
 Perpesanan, panggilan suara dan video, pembagian berkas, pembagian layar dan banyak integrasi bot dan widget. Buat ruangan dan komunitas, tetap terhubung, dan selesaikan hal-hal penting.

From f272e566b072f266815fd035bec67b8c3b36ff5f Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 11 Oct 2022 19:03:15 +0300
Subject: [PATCH 076/400] Implement link a device flow.

---
 .../src/main/res/values/strings.xml           | 16 ++++---
 .../features/login/qr/QrCodeLoginActivity.kt  | 35 +++++++++++----
 .../app/features/login/qr/QrCodeLoginArgs.kt  |  1 +
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  2 +-
 .../qr/QrCodeLoginInstructionsFragment.kt     | 20 ++++++---
 .../login/qr/QrCodeLoginInstructionsView.kt   |  5 ---
 .../login/qr/QrCodeLoginShowQrCodeFragment.kt | 23 ++++++++++
 .../login/qr/QrCodeLoginStatusFragment.kt     | 17 +++++--
 .../features/login/qr/QrCodeLoginViewModel.kt |  3 +-
 .../ftueauth/FtueAuthCombinedLoginFragment.kt | 11 ++++-
 .../v2/VectorSettingsDevicesFragment.kt       | 27 +++++++++++
 .../fragment_qr_code_login_instructions.xml   |  5 ++-
 .../fragment_qr_code_login_show_qr_code.xml   |  6 +--
 .../layout/fragment_qr_code_login_status.xml  | 45 +++++++++++++++++++
 .../view_qr_code_login_instructions.xml       |  9 ----
 15 files changed, 179 insertions(+), 46 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index bf0bce8a8c..8fb019dcd8 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3336,7 +3336,8 @@
     <string name="qr_code_login_header_scan_qr_code_title">Scan QR code</string>
     <string name="qr_code_login_header_scan_qr_code_description">Use the camera on this device to scan the QR code shown on your other device:</string>
     <string name="qr_code_login_header_show_qr_code_title">Sign in with QR code</string>
-    <string name="qr_code_login_header_show_qr_code_description">Use your signed in device to scan the QR code below:</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Use your signed in device to scan the QR code below:</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Scan the QR code below with your device that’s signed out.</string>
     <string name="qr_code_login_header_connected_title">Secure connection established</string>
     <string name="qr_code_login_header_connected_description">Check your signed in device, the code below should be displayed. Confirm that the code below matches with that device:</string>
     <string name="qr_code_login_header_failed_title">Unsuccessful connection</string>
@@ -3344,11 +3345,12 @@
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
-    <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
-    <string name="qr_code_login_new_device_instruction_3">Are you on web? -> Select \'Show QR code\'</string>
-    <string name="qr_code_login_new_device_instruction_4">Are you on a mobile device? -> Select \'Scan QR code\' and then \'Show QR code\'</string>
-    <string name="qr_code_login_link_a_device_instruction_1">Open ${app_name} on your other device</string>
-    <string name="qr_code_login_link_a_device_instruction_2">Select \'Sign in with QR code\'</string>
+    <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
+    <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Start at the sign in screen</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Select \'Sign in with QR code\'</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Start at the sign in screen</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Select \'Scan QR code\'</string>
     <string name="qr_code_login_show_qr_code_button">Show QR code in this device</string>
     <string name="qr_code_login_signing_in_a_mobile_device">Signing in a mobile device?</string>
     <string name="qr_code_login_scan_qr_code_button">Scan QR code</string>
@@ -3356,5 +3358,7 @@
     <string name="qr_code_login_signing_in">Signing you in</string>
     <string name="qr_code_login_status_no_match">No match?</string>
     <string name="qr_code_login_try_again">Try again</string>
+    <string name="qr_code_login_confirm_security_code">Confirm</string>
+    <string name="qr_code_login_confirm_security_code_description">Please ensure that you know the origin of this code. By linking devices, you will provide someone with full access to your account.</string>
 
 </resources>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 7ca58088f8..e0323fdc2d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -26,6 +26,8 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
+import org.matrix.android.sdk.api.extensions.orFalse
+import timber.log.Timber
 
 @AndroidEntryPoint
 class QrCodeLoginActivity : SimpleFragmentActivity() {
@@ -38,19 +40,36 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
 
         val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
-            if (qrCodeLoginArgs?.loginType == QrCodeLoginType.LOGIN) {
-                addFragment(
-                        views.container,
-                        QrCodeLoginInstructionsFragment::class.java,
-                        qrCodeLoginArgs,
-                        tag = FRAGMENT_QR_CODE_INSTRUCTIONS_TAG
-                )
+            when (qrCodeLoginArgs?.loginType) {
+                QrCodeLoginType.LOGIN -> {
+                    showInstructionsFragment(qrCodeLoginArgs)
+                }
+                QrCodeLoginType.LINK_A_DEVICE -> {
+                    if (qrCodeLoginArgs.showQrCodeByDefault.orFalse()) {
+                        handleNavigateToShowQrCodeScreen()
+                    } else {
+                        showInstructionsFragment(qrCodeLoginArgs)
+                    }
+                }
+                null -> {
+                    Timber.i("QrCodeLoginArgs is null. This is not expected.")
+                    finish()
+                }
             }
         }
 
         observeViewEvents()
     }
 
+    private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) {
+        addFragment(
+                views.container,
+                QrCodeLoginInstructionsFragment::class.java,
+                qrCodeLoginArgs,
+                tag = FRAGMENT_QR_CODE_INSTRUCTIONS_TAG
+        )
+    }
+
     private fun observeViewEvents() {
         viewModel.observeViewEvents {
             when (it) {
@@ -61,7 +80,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
     }
 
     private fun handleNavigateToShowQrCodeScreen() {
-        addFragmentToBackstack(
+        addFragment(
                 views.container,
                 QrCodeLoginShowQrCodeFragment::class.java,
                 tag = FRAGMENT_SHOW_QR_CODE_TAG
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
index 2f40b3ec4d..c7b681c488 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
@@ -22,4 +22,5 @@ import kotlinx.parcelize.Parcelize
 @Parcelize
 data class QrCodeLoginArgs(
         val loginType: QrCodeLoginType,
+        val showQrCodeByDefault: Boolean,
 ) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index f2d50eafc4..330562b874 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -18,7 +18,7 @@ package im.vector.app.features.login.qr
 
 sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
-    data class Connected(val securityCode: String) : QrCodeLoginConnectionStatus()
+    data class Connected(val securityCode: String, val canConfirmSecurityCode: Boolean) : QrCodeLoginConnectionStatus()
     object SigningIn : QrCodeLoginConnectionStatus()
     data class Failed(val errorType: QrCodeLoginErrorType, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index 6b48975e15..ae3ba9574b 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -21,10 +21,9 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
-import androidx.core.view.isVisible
 import com.airbnb.mvrx.activityViewModel
-import com.airbnb.mvrx.fragmentViewModel
 import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginInstructionsBinding
@@ -50,11 +49,20 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
     private fun observeViewState() {
         viewModel.onEach {
             if (it.loginType == QrCodeLoginType.LOGIN) {
-                views.qrCodeLoginInstructionsShowQrCodeButton.isVisible = false
-                views.qrCodeLoginInstructionsAlternativeLayout.isVisible = false
+                views.qrCodeLoginInstructionsView.setInstructions(
+                        listOf(
+                                getString(R.string.qr_code_login_new_device_instruction_1),
+                                getString(R.string.qr_code_login_new_device_instruction_2),
+                                getString(R.string.qr_code_login_new_device_instruction_3),
+                        )
+                )
             } else {
-                views.qrCodeLoginInstructionsShowQrCodeButton.isVisible = true
-                views.qrCodeLoginInstructionsAlternativeLayout.isVisible = true
+                views.qrCodeLoginInstructionsView.setInstructions(
+                        listOf(
+                                getString(R.string.qr_code_login_link_a_device_scan_qr_code_instruction_1),
+                                getString(R.string.qr_code_login_link_a_device_scan_qr_code_instruction_2),
+                        )
+                )
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
index 30a24090c6..7ef1706b37 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
@@ -26,7 +26,6 @@ import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.content.res.use
 import androidx.core.view.isVisible
 import im.vector.app.R
-import im.vector.app.core.extensions.setTextOrHide
 import im.vector.app.databinding.ViewQrCodeLoginInstructionsBinding
 
 class QrCodeLoginInstructionsView @JvmOverloads constructor(
@@ -55,22 +54,18 @@ class QrCodeLoginInstructionsView @JvmOverloads constructor(
         val instruction1 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction1)
         val instruction2 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction2)
         val instruction3 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction3)
-        val instruction4 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction4)
         binding.instructions1Layout.isVisible = instruction1 != null
         binding.instructions2Layout.isVisible = instruction2 != null
         binding.instructions3Layout.isVisible = instruction3 != null
-        binding.instructions4Layout.isVisible = instruction4 != null
         binding.instruction1TextView.text = instruction1
         binding.instruction2TextView.text = instruction2
         binding.instruction3TextView.text = instruction3
-        binding.instruction4TextView.text = instruction4
     }
 
     fun setInstructions(instructions: List<String>) {
         setInstruction(binding.instructions1Layout, binding.instruction1TextView, instructions.getOrNull(0))
         setInstruction(binding.instructions2Layout, binding.instruction2TextView, instructions.getOrNull(1))
         setInstruction(binding.instructions3Layout, binding.instruction3TextView, instructions.getOrNull(2))
-        setInstruction(binding.instructions4Layout, binding.instruction4TextView, instructions.getOrNull(3))
     }
 
     private fun setInstruction(instructionLayout: LinearLayout, instructionTextView: TextView, instruction: String?) {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
index b5437626bc..9741b79f7a 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
@@ -22,6 +22,7 @@ import android.view.View
 import android.view.ViewGroup
 import com.airbnb.mvrx.activityViewModel
 import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginShowQrCodeBinding
 
@@ -49,12 +50,34 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
 
     private fun observeViewState() {
         viewModel.onEach {
+            setInstructions(it.loginType)
             it.generatedQrCodeData?.let { qrCodeData ->
                 showQrCode(qrCodeData)
             }
         }
     }
 
+    private fun setInstructions(loginType: QrCodeLoginType) {
+        if (loginType == QrCodeLoginType.LOGIN) {
+            views.qrCodeLoginShowQrCodeHeaderView.setDescription(getString(R.string.qr_code_login_header_show_qr_code_new_device_description))
+            views.qrCodeLoginShowQrCodeInstructionsView.setInstructions(
+                    listOf(
+                            getString(R.string.qr_code_login_new_device_instruction_1),
+                            getString(R.string.qr_code_login_new_device_instruction_2),
+                            getString(R.string.qr_code_login_new_device_instruction_3),
+                    )
+            )
+        } else {
+            views.qrCodeLoginShowQrCodeHeaderView.setDescription(getString(R.string.qr_code_login_header_show_qr_code_link_a_device_description))
+            views.qrCodeLoginShowQrCodeInstructionsView.setInstructions(
+                    listOf(
+                            getString(R.string.qr_code_login_link_a_device_show_qr_code_instruction_1),
+                            getString(R.string.qr_code_login_link_a_device_show_qr_code_instruction_2),
+                    )
+            )
+        }
+    }
+
     private fun showQrCode(qrCodeData: String) {
         views.qrCodeLoginSHowQrCodeImageView.setData(qrCodeData)
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 26d24d9ed0..c1db1832ef 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -52,7 +52,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     private fun observeViewState() {
         viewModel.onEach {
             when (it.connectionStatus) {
-                is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(it.connectionStatus)
+                is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(it.connectionStatus, it.loginType)
                 QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice()
                 QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn()
                 is QrCodeLoginConnectionStatus.Failed -> handleFailed(it.connectionStatus)
@@ -62,6 +62,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     }
 
     private fun handleFailed(connectionStatus: QrCodeLoginConnectionStatus.Failed) {
+        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = false
         views.qrCodeLoginStatusLoadingLayout.isVisible = false
         views.qrCodeLoginStatusHeaderView.isVisible = true
         views.qrCodeLoginStatusSecurityCode.isVisible = false
@@ -85,6 +86,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     }
 
     private fun handleConnectingToDevice() {
+        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = false
         views.qrCodeLoginStatusLoadingLayout.isVisible = true
         views.qrCodeLoginStatusHeaderView.isVisible = false
         views.qrCodeLoginStatusSecurityCode.isVisible = false
@@ -95,8 +97,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     }
 
     private fun handleSigningIn() {
+        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = false
         views.qrCodeLoginStatusLoadingLayout.isVisible = true
-        views.qrCodeLoginStatusHeaderView.isVisible = false
+        views.qrCodeLoginStatusHeaderView.apply {
+            isVisible = true
+            setTitle(getString(R.string.dialog_title_success))
+            setDescription("")
+            setImage(R.drawable.ic_tick, ThemeUtils.getColor(requireContext(), R.attr.colorPrimary))
+        }
         views.qrCodeLoginStatusSecurityCode.isVisible = false
         views.qrCodeLoginStatusNoMatchLayout.isVisible = false
         views.qrCodeLoginStatusCancelButton.isVisible = false
@@ -104,11 +112,12 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         views.qrCodeLoginStatusLoadingTextView.setText(R.string.qr_code_login_signing_in)
     }
 
-    private fun handleConnectionEstablished(connectionStatus: QrCodeLoginConnectionStatus.Connected) {
+    private fun handleConnectionEstablished(connectionStatus: QrCodeLoginConnectionStatus.Connected, loginType: QrCodeLoginType) {
+        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = true
         views.qrCodeLoginStatusLoadingLayout.isVisible = false
         views.qrCodeLoginStatusHeaderView.isVisible = true
         views.qrCodeLoginStatusSecurityCode.isVisible = true
-        views.qrCodeLoginStatusNoMatchLayout.isVisible = true
+        views.qrCodeLoginStatusNoMatchLayout.isVisible = loginType == QrCodeLoginType.LOGIN
         views.qrCodeLoginStatusCancelButton.isVisible = true
         views.qrCodeLoginStatusTryAgainButton.isVisible = false
         views.qrCodeLoginStatusSecurityCode.text = connectionStatus.securityCode
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index a6d991fb1a..da3348653c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -90,9 +90,10 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun onConnectionEstablished(securityCode: String) {
+        val canConfirmSecurityCode = initialState.loginType == QrCodeLoginType.LINK_A_DEVICE
         setState {
             copy(
-                    connectionStatus = QrCodeLoginConnectionStatus.Connected(securityCode)
+                    connectionStatus = QrCodeLoginConnectionStatus.Connected(securityCode, canConfirmSecurityCode)
             )
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
index 301e41be4b..50a9d2c1ae 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
@@ -72,7 +72,16 @@ class FtueAuthCombinedLoginFragment :
             viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content()))
         }
         views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) }
-        views.loginWithQrCode.debouncedClicks { navigator.openLoginWithQrCode(requireActivity(), QrCodeLoginArgs(loginType = QrCodeLoginType.LOGIN)) }
+        views.loginWithQrCode.debouncedClicks {
+            navigator
+                    .openLoginWithQrCode(
+                            requireActivity(),
+                            QrCodeLoginArgs(
+                                    loginType = QrCodeLoginType.LOGIN,
+                                    showQrCodeByDefault = false,
+                            )
+                    )
+        }
     }
 
     private fun setupSubmitButton() {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index 1e5c4d88e0..cfd9703773 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -37,6 +37,8 @@ import im.vector.app.core.resources.DrawableProvider
 import im.vector.app.databinding.FragmentSettingsDevicesBinding
 import im.vector.app.features.crypto.recover.SetupMode
 import im.vector.app.features.crypto.verification.VerificationBottomSheet
+import im.vector.app.features.login.qr.QrCodeLoginArgs
+import im.vector.app.features.login.qr.QrCodeLoginType
 import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
 import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER
 import im.vector.app.features.settings.devices.v2.list.OtherSessionsView
@@ -86,6 +88,7 @@ class VectorSettingsDevicesFragment :
         initWaitingView()
         initOtherSessionsView()
         initSecurityRecommendationsView()
+        initQrLoginView()
         observeViewEvents()
     }
 
@@ -150,6 +153,30 @@ class VectorSettingsDevicesFragment :
         }
     }
 
+    private fun initQrLoginView() {
+        views.deviceListHeaderScanQrCodeButton.debouncedClicks {
+            navigator
+                    .openLoginWithQrCode(
+                            requireActivity(),
+                            QrCodeLoginArgs(
+                                    loginType = QrCodeLoginType.LINK_A_DEVICE,
+                                    showQrCodeByDefault = false,
+                            )
+                    )
+        }
+
+        views.deviceListHeaderShowQrCodeButton.debouncedClicks {
+            navigator
+                    .openLoginWithQrCode(
+                            requireActivity(),
+                            QrCodeLoginArgs(
+                                    loginType = QrCodeLoginType.LINK_A_DEVICE,
+                                    showQrCodeByDefault = true,
+                            )
+                    )
+        }
+    }
+
     override fun onDestroyView() {
         cleanUpLearnMoreButtonsListeners()
         super.onDestroyView()
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
index 7983d784af..a13e460a5a 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_instructions.xml
@@ -27,8 +27,7 @@
         app:layout_constraintTop_toBottomOf="@id/qrCodeLoginInstructionsHeaderView"
         app:qrCodeLoginInstruction1="@string/qr_code_login_new_device_instruction_1"
         app:qrCodeLoginInstruction2="@string/qr_code_login_new_device_instruction_2"
-        app:qrCodeLoginInstruction3="@string/qr_code_login_new_device_instruction_3"
-        app:qrCodeLoginInstruction4="@string/qr_code_login_new_device_instruction_4" />
+        app:qrCodeLoginInstruction3="@string/qr_code_login_new_device_instruction_3" />
 
     <Button
         android:id="@+id/qrCodeLoginInstructionsShowQrCodeButton"
@@ -37,6 +36,7 @@
         android:layout_height="wrap_content"
         android:layout_marginBottom="40dp"
         android:text="@string/qr_code_login_show_qr_code_button"
+        android:visibility="gone"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent" />
@@ -46,6 +46,7 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginBottom="12dp"
+        android:visibility="gone"
         app:layout_constraintBottom_toTopOf="@id/qrCodeLoginInstructionsShowQrCodeButton"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent">
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml b/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml
index 060d02498e..539f019848 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml
@@ -13,13 +13,13 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        app:qrCodeLoginHeaderDescription="@string/qr_code_login_header_show_qr_code_description"
+        app:qrCodeLoginHeaderDescription="@string/qr_code_login_header_show_qr_code_link_a_device_description"
         app:qrCodeLoginHeaderImageBackgroundTint="?colorPrimary"
         app:qrCodeLoginHeaderImageResource="@drawable/ic_camera"
         app:qrCodeLoginHeaderTitle="@string/qr_code_login_header_show_qr_code_title" />
 
     <im.vector.app.features.login.qr.QrCodeLoginInstructionsView
-        android:id="@+id/qrCodeLoginShowQrCodeView"
+        android:id="@+id/qrCodeLoginShowQrCodeInstructionsView"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginTop="24dp"
@@ -36,7 +36,7 @@
         android:layout_height="240dp"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginShowQrCodeView"
+        app:layout_constraintTop_toBottomOf="@id/qrCodeLoginShowQrCodeInstructionsView"
         app:layout_constraintBottom_toTopOf="@id/qrCodeLoginShowQrCodeCancelButton"/>
 
     <Button
diff --git a/vector/src/main/res/layout/fragment_qr_code_login_status.xml b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
index 240ccaf070..59e1e60cd5 100644
--- a/vector/src/main/res/layout/fragment_qr_code_login_status.xml
+++ b/vector/src/main/res/layout/fragment_qr_code_login_status.xml
@@ -15,6 +15,7 @@
         android:gravity="center"
         android:orientation="vertical"
         android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
@@ -96,6 +97,50 @@
         app:layout_constraintStart_toStartOf="parent"
         tools:visibility="visible" />
 
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/qrCodeLoginConfirmSecurityCodeLayout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:visibility="gone"
+        app:layout_constraintBottom_toTopOf="@id/qrCodeLoginStatusCancelButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:visibility="visible">
+
+        <ImageView
+            android:id="@+id/qrCodeLoginConfirmSecurityCodeImageView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:importantForAccessibility="no"
+            android:src="@drawable/ic_info"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/qrCodeLoginConfirmSecurityCodeTextView"
+            style="@style/TextAppearance.Vector.Body"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:includeFontPadding="false"
+            android:layout_marginStart="8dp"
+            android:text="@string/qr_code_login_confirm_security_code_description"
+            app:layout_constraintStart_toEndOf="@id/qrCodeLoginConfirmSecurityCodeImageView"
+            app:layout_constraintTop_toTopOf="@id/qrCodeLoginConfirmSecurityCodeImageView" />
+
+        <Button
+            android:id="@+id/qrCodeLoginConfirmSecurityCodeButton"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="16dp"
+            android:text="@string/qr_code_login_confirm_security_code"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/qrCodeLoginConfirmSecurityCodeTextView" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
     <Button
         android:id="@+id/qrCodeLoginStatusCancelButton"
         style="@style/Widget.Vector.Button.Outlined.Login"
diff --git a/vector/src/main/res/layout/view_qr_code_login_instructions.xml b/vector/src/main/res/layout/view_qr_code_login_instructions.xml
index 4b23c0d73d..6ff4b306a3 100644
--- a/vector/src/main/res/layout/view_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/view_qr_code_login_instructions.xml
@@ -118,15 +118,6 @@
             android:padding="6dp"
             android:text="@string/four"
             android:textColor="?colorPrimary" />
-
-        <TextView
-            android:id="@+id/instruction4TextView"
-            style="@style/TextAppearance.Vector.Body"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:layout_marginStart="8dp"
-            tools:text="@string/qr_code_login_new_device_instruction_4" />
     </LinearLayout>
 
 </merge>

From d8ea9c8215bb6a2ef6831843aa7b759e6cafcdc0 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 11 Oct 2022 20:04:18 +0300
Subject: [PATCH 077/400] Add flag for qr code login.

---
 .../features/DebugFeaturesStateFactory.kt     |  5 ++++
 .../debug/features/DebugVectorFeatures.kt     |  4 ++++
 .../im/vector/app/features/VectorFeatures.kt  |  2 ++
 .../ftueauth/FtueAuthCombinedLoginFragment.kt | 24 ++++++++++++-------
 .../v2/VectorSettingsDevicesFragment.kt       | 14 +++++++++++
 5 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 9118dea1e3..be25ffe0af 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -90,6 +90,11 @@ class DebugFeaturesStateFactory @Inject constructor(
                                 key = DebugFeatureKeys.newDeviceManagementEnabled,
                                 factory = VectorFeatures::isNewDeviceManagementEnabled
                         ),
+                        createBooleanFeature(
+                                label = "Enable QR Code Login",
+                                key = DebugFeatureKeys.qrCodeLoginEnabled,
+                                factory = VectorFeatures::isQrCodeLoginEnabled
+                        ),
                 )
         )
     }
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index c01c058fc6..e4b151329d 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -79,6 +79,9 @@ class DebugVectorFeatures(
     override fun isNewDeviceManagementEnabled(): Boolean = read(DebugFeatureKeys.newDeviceManagementEnabled)
             ?: vectorFeatures.isNewDeviceManagementEnabled()
 
+    override fun isQrCodeLoginEnabled() = read(DebugFeatureKeys.qrCodeLoginEnabled)
+            ?: vectorFeatures.isQrCodeLoginEnabled()
+
     fun <T> override(value: T?, key: Preferences.Key<T>) = updatePreferences {
         if (value == null) {
             it.remove(key)
@@ -140,4 +143,5 @@ object DebugFeatureKeys {
     val startDmOnFirstMsg = booleanPreferencesKey("start-dm-on-first-msg")
     val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled")
     val newDeviceManagementEnabled = booleanPreferencesKey("new-device-management-enabled")
+    val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled")
 }
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index e1c083db29..df952d8592 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -41,6 +41,7 @@ interface VectorFeatures {
      */
     fun isNewAppLayoutFeatureEnabled(): Boolean
     fun isNewDeviceManagementEnabled(): Boolean
+    fun isQrCodeLoginEnabled(): Boolean
 }
 
 class DefaultVectorFeatures : VectorFeatures {
@@ -57,4 +58,5 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun forceUsageOfOpusEncoder(): Boolean = false
     override fun isNewAppLayoutFeatureEnabled(): Boolean = true
     override fun isNewDeviceManagementEnabled(): Boolean = false
+    override fun isQrCodeLoginEnabled(): Boolean = false
 }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
index 50a9d2c1ae..0a7ce22853 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
@@ -36,6 +36,7 @@ import im.vector.app.core.extensions.setOnFocusLostListener
 import im.vector.app.core.extensions.setOnImeDoneListener
 import im.vector.app.core.extensions.toReducedUrl
 import im.vector.app.databinding.FragmentFtueCombinedLoginBinding
+import im.vector.app.features.VectorFeatures
 import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.SSORedirectRouterActivity
 import im.vector.app.features.login.SocialLoginButtonsView
@@ -57,6 +58,7 @@ class FtueAuthCombinedLoginFragment :
 
     @Inject lateinit var loginFieldsValidation: LoginFieldsValidation
     @Inject lateinit var loginErrorParser: LoginErrorParser
+    @Inject lateinit var vectorFeatures: VectorFeatures
 
     override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueCombinedLoginBinding {
         return FragmentFtueCombinedLoginBinding.inflate(inflater, container, false)
@@ -72,15 +74,19 @@ class FtueAuthCombinedLoginFragment :
             viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content()))
         }
         views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) }
-        views.loginWithQrCode.debouncedClicks {
-            navigator
-                    .openLoginWithQrCode(
-                            requireActivity(),
-                            QrCodeLoginArgs(
-                                    loginType = QrCodeLoginType.LOGIN,
-                                    showQrCodeByDefault = false,
-                            )
-                    )
+        if (vectorFeatures.isQrCodeLoginEnabled()) {
+            views.loginWithQrCode.debouncedClicks {
+                navigator
+                        .openLoginWithQrCode(
+                                requireActivity(),
+                                QrCodeLoginArgs(
+                                        loginType = QrCodeLoginType.LOGIN,
+                                        showQrCodeByDefault = false,
+                                )
+                        )
+            }
+        } else {
+            views.loginWithQrCode.isVisible = false
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index cfd9703773..937d41e091 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -35,6 +35,7 @@ import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.core.resources.ColorProvider
 import im.vector.app.core.resources.DrawableProvider
 import im.vector.app.databinding.FragmentSettingsDevicesBinding
+import im.vector.app.features.VectorFeatures
 import im.vector.app.features.crypto.recover.SetupMode
 import im.vector.app.features.crypto.verification.VerificationBottomSheet
 import im.vector.app.features.login.qr.QrCodeLoginArgs
@@ -64,6 +65,8 @@ class VectorSettingsDevicesFragment :
 
     @Inject lateinit var colorProvider: ColorProvider
 
+    @Inject lateinit var vectorFeatures: VectorFeatures
+
     private val viewModel: DevicesViewModel by fragmentViewModel()
 
     override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSettingsDevicesBinding {
@@ -154,6 +157,17 @@ class VectorSettingsDevicesFragment :
     }
 
     private fun initQrLoginView() {
+        if (!vectorFeatures.isQrCodeLoginEnabled()) {
+            views.deviceListHeaderSignInWithQrCode.isVisible = false
+            views.deviceListHeaderScanQrCodeButton.isVisible = false
+            views.deviceListHeaderShowQrCodeButton.isVisible = false
+            return
+        }
+
+        views.deviceListHeaderSignInWithQrCode.isVisible = true
+        views.deviceListHeaderScanQrCodeButton.isVisible = true
+        views.deviceListHeaderShowQrCodeButton.isVisible = true
+
         views.deviceListHeaderScanQrCodeButton.debouncedClicks {
             navigator
                     .openLoginWithQrCode(

From 87956e943897333f3f7e9be9a6e59d9a7f0c4547 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:32:49 +0100
Subject: [PATCH 078/400] Retry scanning if not a QR code

---
 .../app/features/login/qr/QrCodeLoginInstructionsFragment.kt    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index ae3ba9574b..17b4ff2409 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -83,6 +83,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
         if (activityResult.resultCode == Activity.RESULT_OK) {
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
+            Timber.d("Scanned QR code: $scannedQrCode $wasQrCode")
 
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
@@ -98,5 +99,6 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     private fun onQrCodeScannerFailed() {
         Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
+        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 }

From 1235db7895fc92c05d220abaf30433d56dd7b2f8 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:33:30 +0100
Subject: [PATCH 079/400] Implementations of MSC3886 and MSC3903

---
 .../android/sdk/api/logger/LoggerTag.kt       |   1 +
 .../sdk/internal/rendezvous/Rendezvous.kt     | 217 +++++++++++++++++
 .../internal/rendezvous/RendezvousChannel.kt  |  45 ++++
 .../rendezvous/RendezvousFailureReason.kt     |  31 +++
 .../rendezvous/RendezvousTransport.kt         |  29 +++
 .../channels/ECDHRendezvousChannel.kt         | 218 ++++++++++++++++++
 .../rendezvous/model/ECDHRendezvous.kt        |  34 +++
 .../rendezvous/model/EmbeddedRendezvous.kt    |  26 +++
 .../rendezvous/model/RendezvousError.kt       |  22 ++
 .../rendezvous/model/RendezvousIntent.kt      |  24 ++
 .../model/RendezvousTransportDetails.kt       |  25 ++
 .../model/RendezvousTransportType.kt          |  23 ++
 .../model/SecureRendezvousChannelAlgorithm.kt |  23 ++
 .../SimpleHttpRendezvousTransport.kt          | 185 +++++++++++++++
 14 files changed, 903 insertions(+)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
index ae65963f37..22af8cebbd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
@@ -27,6 +27,7 @@ open class LoggerTag(name: String, parentTag: LoggerTag? = null) {
     object SYNC : LoggerTag("SYNC")
     object VOIP : LoggerTag("VOIP")
     object CRYPTO : LoggerTag("CRYPTO")
+    object RENDEZVOUS : LoggerTag("RZ")
 
     val value: String = if (parentTag == null) {
         name
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
new file mode 100644
index 0000000000..dd7b91582b
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
@@ -0,0 +1,217 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
+import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport
+import timber.log.Timber
+
+internal enum class PayloadType(val value: String) {
+    @Json(name = "m.login.start") Start("m.login.start"),
+    @Json(name = "m.login.finish") Finish("m.login.finish"),
+    @Json(name = "m.login.progress") Progress("m.login.progress")
+}
+
+@JsonClass(generateAdapter = true)
+internal data class Payload(
+        @Json val type: PayloadType,
+        @Json val intent: RendezvousIntent? = null,
+        @Json val outcome: String? = null,
+        @Json val protocols: List<String>? = null,
+        @Json val protocol: String? = null,
+        @Json val homeserver: String? = null,
+        @Json val login_token: String? = null,
+        @Json val device_id: String? = null,
+        @Json val device_key: String? = null,
+        @Json val verifying_device_id: String? = null,
+        @Json val verifying_device_key: String? = null,
+        @Json val master_key: String? = null
+)
+
+private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+data class Rendezvous(
+        val channel: RendezvousChannel,
+        val theirIntent: RendezvousIntent
+) {
+    companion object {
+        fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
+            val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
+
+            val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri)
+
+            return Rendezvous(
+                    ECDHRendezvousChannel(transport, parsed.rendezvous.key),
+                    parsed.intent
+            )
+        }
+    }
+
+    private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java)
+    // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
+    val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
+
+    private suspend fun areIntentsIncompatible(): Boolean {
+        val incompatible = theirIntent == ourIntent
+
+        Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
+
+        if (incompatible) {
+            send(Payload(PayloadType.Finish, intent = ourIntent))
+            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn
+            channel.cancel(reason)
+        }
+
+        return incompatible
+    }
+
+    suspend fun startAfterScanningCode(): String? {
+        val checksum = channel.connect();
+
+        Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
+
+        if (areIntentsIncompatible()) {
+            return null
+        }
+
+        // get protocols
+        Timber.tag(TAG).i("Waiting for protocols");
+        val protocolsResponse = receive()
+
+        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
+            send(Payload(PayloadType.Finish, outcome = "unsupported"))
+            Timber.tag(TAG).i("No supported protocol")
+            cancel(RendezvousFailureReason.Unknown)
+            return null
+        }
+
+        send(Payload(PayloadType.Progress, protocol = "login_token"))
+
+        return checksum
+    }
+
+    suspend fun completeOnNewDevice(): Session? {
+        Timber.tag(TAG).i("Waiting for login_token");
+
+        val loginToken = receive()
+
+        if (loginToken?.type == PayloadType.Finish) {
+            when (loginToken.outcome) {
+                "declined" -> {
+                    Timber.tag(TAG).i("Login declined by other device")
+                    channel.cancel(RendezvousFailureReason.UserDeclined)
+                    return null
+                }
+                "unsupported" -> {
+                    Timber.tag(TAG).i("Not supported")
+                    channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
+                    return null
+                }
+            }
+            channel.cancel(RendezvousFailureReason.Unknown)
+            return null
+        }
+
+        val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
+        val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
+
+        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
+
+        // TODO: set view to be state logging in?
+
+        // use token to login
+//        const login = await sendLoginRequest(homeserver, undefined, "m.login.token", { token: login_token });
+//
+//        await setLoggedIn(login);
+//
+//        const { deviceId, userId } = login;
+//
+//        const client = MatrixClientPeg.get();
+//
+
+        val newSession: Session? = null
+
+        newSession ?.let {
+            session ->
+            val userId = session.myUserId
+            val crypto = session.cryptoService()
+            val deviceId = crypto.getMyDevice().deviceId
+            val deviceKey = crypto.getMyDevice().fingerprint()
+            send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
+
+            // await confirmation of verification
+
+            val verificationResponse = receive()
+            val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
+            val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
+            if (verifyingDeviceFromServer?.fingerprint() == verificationResponse.verifying_device_key) {
+                // set other device as verified
+                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
+                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+
+                verificationResponse.master_key ?.let {
+                    // set master key as trusted
+                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
+
+                }
+
+                // request secrets from the verifying device
+                Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+
+                session.sharedSecretStorageService() .let {
+                    it.requestSecret(verifyingDeviceId, MASTER_KEY_SSSS_NAME)
+                    it.requestSecret(verifyingDeviceId, SELF_SIGNING_KEY_SSSS_NAME)
+                    it.requestSecret(verifyingDeviceId, USER_SIGNING_KEY_SSSS_NAME)
+                    it.requestSecret(verifyingDeviceId, KEYBACKUP_SECRET_SSSS_NAME)
+                }
+            } else {
+                Timber.tag(TAG).i("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
+            }
+        }
+
+        return newSession
+    }
+
+    private suspend fun receive(): Payload? {
+        val data = channel.receive()?: return null
+        return adapter.fromJson(data.toString(Charsets.UTF_8))
+    }
+
+    private suspend fun send(payload: Payload) {
+        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8));
+    }
+
+    suspend fun cancel(reason: RendezvousFailureReason) {
+        channel.cancel(reason)
+    }
+
+    suspend fun close() {
+        channel.close()
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
new file mode 100644
index 0000000000..43552f46be
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
@@ -0,0 +1,45 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous
+
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+
+interface RendezvousChannel {
+    var transport: RendezvousTransport;
+    /**
+     * @returns the checksum/confirmation digits to be shown to the user
+     */
+    suspend fun connect(): String
+    /**
+     * Send a payload via the channel.
+     * @param data payload to send
+     */
+    suspend fun send(data: ByteArray)
+    /**
+     * Receive a payload from the channel.
+     * @returns the received payload
+     */
+    suspend fun receive(): ByteArray?
+    /**
+     * @returns a representation of the channel that can be encoded in a QR or similar
+     */
+    suspend fun close()
+    // TODO: this should be transport independent in the future
+    suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
+    suspend fun cancel(reason: RendezvousFailureReason)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
new file mode 100644
index 0000000000..0e2ea8c758
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.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 org.matrix.android.sdk.internal.rendezvous
+
+enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
+    UserDeclined("user_declined"),
+    OtherDeviceNotSignedIn("other_device_not_signed_in"),
+    OtherDeviceAlreadySignedIn("other_device_already_signed_in"),
+    Unknown("unknown"),
+    Expired("expired"),
+    UserCancelled("user_cancelled"),
+    InvalidCode("invalid_code"),
+    UnsupportedAlgorithm("unsupported_algorithm", false),
+    DataMismatch("data_mismatch"),
+    UnsupportedTransport("unsupported_transport", false),
+    HomeserverLacksSupport("homeserver_lacks_support", false)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
new file mode 100644
index 0000000000..753b0bc6fa
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
@@ -0,0 +1,29 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous
+
+import okhttp3.MediaType
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+
+interface RendezvousTransport {
+    var ready: Boolean;
+    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?;
+    suspend fun details(): RendezvousTransportDetails;
+    suspend fun send(contentType: MediaType, data: ByteArray);
+    suspend fun receive(): ByteArray?;
+    suspend fun cancel(reason: RendezvousFailureReason);
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
new file mode 100644
index 0000000000..33837bc425
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -0,0 +1,218 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.channels
+
+import android.util.Base64
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import okhttp3.MediaType.Companion.toMediaType
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.extensions.toUnsignedInt
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel
+import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.olm.OlmSAS
+import timber.log.Timber
+import java.security.SecureRandom
+import java.util.LinkedList
+import javax.crypto.Cipher
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+@JsonClass(generateAdapter = true)
+data class ECDHPayload(
+        @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
+        @Json val key: String? = null,
+        @Json val ciphertext: String? = null,
+        @Json val iv: String? = null
+)
+
+private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+    val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
+    val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
+    val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
+    val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
+    val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
+    // (B0 << 5 | B1 >> 3) + 1000
+    val first = (b0.shl(5) or b1.shr(3)) + 1000
+    // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
+    val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
+    // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
+    val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
+    return "$first-$second-$third"
+}
+
+const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
+const val KEY_SPEC = "AES"
+
+/**
+ *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
+ *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
+ */
+class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel {
+    private var olmSAS: OlmSAS?
+    private val ourPublicKey: ByteArray
+    private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
+    private var theirPublicKey: ByteArray? = null
+    private var aesKey: ByteArray? = null
+
+    init {
+        theirPublicKeyBase64 ?.let {
+            theirPublicKey = Base64.decode(it, Base64.NO_WRAP)
+        }
+        olmSAS = OlmSAS()
+        ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP)
+    }
+
+    override suspend fun connect(): String {
+        if (olmSAS == null) {
+            throw RuntimeException("Channel closed")
+        }
+        val isInitiator = theirPublicKey == null
+
+        if (isInitiator) {
+            Timber.tag(TAG).i("Waiting for other device to send their public key")
+            val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
+
+            if (res.key == null) {
+                throw RendezvousError(
+                        "Unsupported algorithm: ${res.algorithm}",
+                        RendezvousFailureReason.UnsupportedAlgorithm,
+                )
+            }
+            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
+        } else {
+            // send our public key unencrypted
+            Timber.tag(TAG).i("Sending public key")
+            send(ECDHPayload(
+                    algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
+                    key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+            ))
+        }
+
+        olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+
+        val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
+        val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
+        val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
+
+        aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
+
+        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
+        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
+        Timber.tag(TAG).i("AES info: $aesInfo")
+        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
+
+        val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
+        return getDecimalCodeRepresentation(rawChecksum)
+    }
+
+    private suspend fun send(payload: ECDHPayload) {
+        transport.send("application/json".toMediaType(), ecdhAdapter.toJson(payload).toByteArray(Charsets.UTF_8))
+    }
+
+    override suspend fun send(data: ByteArray) {
+        if (aesKey == null) {
+            throw RuntimeException("Shared secret not established")
+        }
+        send(encrypt(data))
+    }
+
+    private suspend fun receiveAsPayload(): ECDHPayload? {
+        transport.receive()?.toString(Charsets.UTF_8) ?.let {
+            return ecdhAdapter.fromJson(it)
+        } ?: return null
+    }
+
+    override suspend fun receive(): ByteArray? {
+        if (aesKey == null) {
+            throw RuntimeException("Shared secret not established")
+        }
+        val payload = receiveAsPayload() ?: return null
+        return decrypt(payload)
+    }
+
+    override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode {
+        return ECDHRendezvousCode(
+                intent,
+                rendezvous = ECDHRendezvous(
+                        transport.details() as SimpleHttpRendezvousTransportDetails,
+                        SecureRendezvousChannelAlgorithm.ECDH_V1,
+                        key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+                )
+        )
+    }
+
+    override suspend fun cancel(reason: RendezvousFailureReason) {
+        try {
+            transport.cancel(reason)
+        } finally {
+            close()
+        }
+    }
+
+    override suspend fun close() {
+        olmSAS?.releaseSas()
+        olmSAS = null
+    }
+
+    private fun encrypt(plainText: ByteArray): ECDHPayload {
+        Timber.tag(TAG).i("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
+        val iv = ByteArray(16)
+        SecureRandom().nextBytes(iv)
+
+        val cipherText = LinkedList<Byte>()
+
+        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
+        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
+        val ivParameterSpec = IvParameterSpec(iv)
+        encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
+        cipherText.addAll(encryptCipher.update(plainText).toList())
+        cipherText.addAll(encryptCipher.doFinal().toList())
+
+        return ECDHPayload(
+                ciphertext = Base64.encodeToString(cipherText.toByteArray(), Base64.NO_WRAP),
+                iv = Base64.encodeToString(iv, Base64.NO_WRAP)
+        )
+    }
+
+    private fun decrypt(payload: ECDHPayload): ByteArray {
+        val iv = Base64.decode(payload.iv, Base64.NO_WRAP)
+        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
+        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
+        val ivParameterSpec = IvParameterSpec(iv)
+        encryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
+
+        val plainText = LinkedList<Byte>()
+        plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList())
+        plainText.addAll(encryptCipher.doFinal().toList())
+
+        val plainTextBytes = plainText.toByteArray()
+
+        Timber.tag(TAG).i("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
+        return plainTextBytes
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
new file mode 100644
index 0000000000..e296dce09d
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
@@ -0,0 +1,34 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvous(
+        @Json val transport: SimpleHttpRendezvousTransportDetails,
+        @Json val algorithm: SecureRendezvousChannelAlgorithm,
+        @Json val key: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvousCode(
+        @Json val intent: RendezvousIntent,
+        @Json val rendezvous: ECDHRendezvous
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
new file mode 100644
index 0000000000..d490de0133
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
@@ -0,0 +1,26 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+open class EmbeddedRendezvous(
+    @Json(name = "transport") val transport: RendezvousTransportDetails,
+    @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
new file mode 100644
index 0000000000..ead273e8ce
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+
+class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
new file mode 100644
index 0000000000..6285c1e57a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class RendezvousIntent {
+    @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
+    @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
new file mode 100644
index 0000000000..1b1826194f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
@@ -0,0 +1,25 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+open class RendezvousTransportDetails(
+    @Json val type: RendezvousTransportType
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
new file mode 100644
index 0000000000..c3b6ba7ac8
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
@@ -0,0 +1,23 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class RendezvousTransportType(val value: String) {
+    @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
new file mode 100644
index 0000000000..ddc0ae20e7
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -0,0 +1,23 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class SecureRendezvousChannelAlgorithm(val value: String) {
+    @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
new file mode 100644
index 0000000000..3e5e1121ea
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -0,0 +1,185 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.transports
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import kotlinx.coroutines.delay
+import okhttp3.MediaType
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType
+import timber.log.Timber
+import java.text.SimpleDateFormat
+import java.util.Date
+
+private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+@JsonClass(generateAdapter = true)
+data class SimpleHttpRendezvousTransportDetails(
+        @Json val uri: String
+): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
+
+/**
+ * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
+ */
+class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
+    override var ready = false
+    private var cancelled = false
+    private var uri: String?
+    private var etag: String? = null
+    private var expiresAt: Date? = null
+
+    init {
+        uri = rendezvousUri
+    }
+
+    override suspend fun details(): RendezvousTransportDetails {
+        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
+
+        return SimpleHttpRendezvousTransportDetails(uri)
+    }
+
+    override suspend fun send(contentType: MediaType, data: ByteArray) {
+        if (cancelled) {
+            return
+        }
+
+        val method = if (uri != null) "PUT" else "POST"
+        // TODO: properly determine endpoint
+        val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
+
+        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
+
+        val httpClient = okhttp3.OkHttpClient.Builder().build()
+
+        val request = Request.Builder()
+                .url(uri)
+                .method(method, data.toRequestBody())
+                .header("content-type", contentType.toString())
+
+        etag ?.let {
+            request.header("if-match", it)
+        }
+
+        val response = httpClient.newCall(request.build()).execute()
+
+        if (response.code == 404) {
+            cancel(RendezvousFailureReason.Unknown)
+        }
+        etag = response.header("etag")
+
+        Timber.tag(TAG).i("Sent data to $uri new etag $etag")
+
+        if (method == "POST") {
+            val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
+
+            response.header("expires") ?.let {
+                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
+                expiresAt = format.parse(it)
+            }
+
+            // resolve location header which could be relative or absolute
+            this.uri = response.request.url.toUri().resolve(location).toString()
+            ready = true
+        }
+    }
+
+    override suspend fun receive(): ByteArray? {
+        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
+        var done = false
+        val httpClient = okhttp3.OkHttpClient.Builder().build()
+        while (!done) {
+            if (cancelled) {
+                return null
+            }
+            Timber.tag(TAG).i("Polling: $uri after etag $etag")
+            val request = Request.Builder()
+                    .url(uri)
+                    .get()
+
+            etag ?.let {
+                request.header("if-none-match", it)
+            }
+
+            val response = httpClient.newCall(request.build()).execute()
+
+            try {
+
+                Timber.tag(TAG).i("Received polling response: ${response.code} from $uri")
+
+                if (response.code == 404) {
+                    cancel(RendezvousFailureReason.Unknown)
+                    return null
+                }
+
+                // rely on server expiring the channel rather than checking ourselves
+
+                if (response.header("content-type") != "application/json") {
+                    response.header("etag")?.let {
+                        etag = it
+                    }
+                } else if (response.code == 200) {
+                    response.header("etag")?.let {
+                        etag = it
+                    }
+                    val data = response.body?.bytes()
+                    Timber.tag(TAG).i("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
+                    return data
+                }
+
+                done = false
+                delay(1000)
+            } finally {
+                response.close()
+            }
+        }
+
+        return null
+    }
+
+    override suspend fun cancel(reason: RendezvousFailureReason) {
+        var mappedReason = reason
+        if (mappedReason == RendezvousFailureReason.Unknown &&
+                expiresAt != null && Date() > expiresAt) {
+            mappedReason = RendezvousFailureReason.Expired
+        }
+
+        cancelled = true
+        ready = false
+        onCancelled ?.let { it(mappedReason) }
+
+        if (mappedReason == RendezvousFailureReason.UserDeclined) {
+            uri ?.let {
+                try {
+                    val httpClient = okhttp3.OkHttpClient.Builder().build()
+                    val request = Request.Builder()
+                            .url(it)
+                            .delete()
+                            .build()
+                    httpClient.newCall(request).execute()
+                } catch (e: Exception) {
+                    Timber.tag(TAG).w(e, "Failed to delete channel")
+                }
+            }
+        }
+    }
+}

From 4b14ee4695b452cd24353f05809f64d0b0ee4b6d Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:34:05 +0100
Subject: [PATCH 080/400] Partial implementation of QR login logic

---
 .../src/main/res/values/strings.xml           |  1 +
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  4 +-
 .../features/login/qr/QrCodeLoginErrorType.kt | 23 -----
 .../login/qr/QrCodeLoginStatusFragment.kt     | 12 +--
 .../features/login/qr/QrCodeLoginViewModel.kt | 86 +++++++++++++------
 5 files changed, 73 insertions(+), 53 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 8fb019dcd8..68e8b495f8 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3344,6 +3344,7 @@
     <string name="qr_code_login_header_failed_device_is_not_supported_description">Linking with this device is not supported.</string>
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
+    <string name="qr_code_login_header_failed_other_description">The request failed.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index 330562b874..4de191f863 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -16,9 +16,11 @@
 
 package im.vector.app.features.login.qr
 
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+
 sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
     data class Connected(val securityCode: String, val canConfirmSecurityCode: Boolean) : QrCodeLoginConnectionStatus()
     object SigningIn : QrCodeLoginConnectionStatus()
-    data class Failed(val errorType: QrCodeLoginErrorType, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
+    data class Failed(val errorType: RendezvousFailureReason, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
deleted file mode 100644
index 9a6cc13de0..0000000000
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.login.qr
-
-enum class QrCodeLoginErrorType {
-    DEVICE_IS_NOT_SUPPORTED,
-    TIMEOUT,
-    REQUEST_WAS_DENIED,
-}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index c1db1832ef..da539b8ede 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -27,6 +27,7 @@ import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
 import im.vector.app.features.themes.ThemeUtils
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
 
 @AndroidEntryPoint
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
@@ -77,11 +78,12 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         )
     }
 
-    private fun getErrorCode(errorType: QrCodeLoginErrorType): String {
-        return when (errorType) {
-            QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
-            QrCodeLoginErrorType.TIMEOUT -> getString(R.string.qr_code_login_header_failed_timeout_description)
-            QrCodeLoginErrorType.REQUEST_WAS_DENIED -> getString(R.string.qr_code_login_header_failed_denied_description)
+    private fun getErrorCode(reason: RendezvousFailureReason): String {
+        return when (reason) {
+            RendezvousFailureReason.UnsupportedAlgorithm, RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
+            RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
+            else -> getString(R.string.qr_code_login_header_failed_other_description)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index da3348653c..d0c34b83af 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -23,13 +23,19 @@ import dagger.assisted.AssistedInject
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.platform.VectorViewModel
-import kotlinx.coroutines.delay
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.rendezvous.Rendezvous
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
-        @Assisted private val initialState: QrCodeLoginViewState,
+        @Assisted private val initialState: QrCodeLoginViewState
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
 
+    val TAG: String = QrCodeLoginViewModel::class.java.simpleName
+
     @AssistedFactory
     interface Factory : MavericksAssistedViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> {
         override fun create(initialState: QrCodeLoginViewState): QrCodeLoginViewModel
@@ -59,32 +65,64 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
-        if (isValidQrCode(action.qrCode)) {
-            setState {
-                copy(
-                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
-                )
-            }
-            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+        Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
+
+        val rendezvous = Rendezvous.buildChannelFromCode(action.qrCode) { reason ->
+            Timber.tag(TAG).d("Rendezvous cancelled: $reason")
+            onFailed(reason)
         }
 
-        // TODO. UI test purpose. Fixme remove!
-        viewModelScope.launch {
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
-            delay(3000)
-            onConnectionEstablished("1234-ABCD-5678-EFGH")
-            delay(3000)
-            onSigningIn()
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
-        }
-    }
-
-    private fun onFailed(errorType: QrCodeLoginErrorType, canTryAgain: Boolean) {
         setState {
             copy(
-                    connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain)
+                    connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
+            )
+        }
+
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+
+        viewModelScope.launch(Dispatchers.IO) {
+            val confirmationCode = rendezvous.startAfterScanningCode()
+            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
+            confirmationCode ?.let {
+                onConnectionEstablished(it)
+            }
+            rendezvous.completeOnNewDevice()
+        }
+    //        if (isValidQrCode(action.qrCode)) {
+//            setState {
+//                copy(
+//                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
+//                )
+//            }
+//            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+//        }
+//
+
+//        // TODO. UI test purpose. Fixme remove!
+//        viewModelScope.launch {
+//            delay(3000)
+//            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
+//            delay(3000)
+//            onConnectionEstablished("1234-ABCD-5678-EFGH")
+//            delay(3000)
+//            onSigningIn()
+//            delay(3000)
+//            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
+//        }
+//        // TODO. UI test purpose. Fixme remove!
+//        viewModelScope.launch {
+//            delay(3000)
+//            onConnectionEstablished("1234-ABCD-5678-EFGH")
+//            delay(3000)
+//            onSigningIn()
+//        }
+    }
+
+
+    private fun onFailed(reason: RendezvousFailureReason) {
+        setState {
+            copy(
+                    connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry)
             )
         }
     }

From 6e58f2fa51f0cc3aa474f9c5adf7b497229e52dd Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Wed, 12 Oct 2022 13:08:01 +0100
Subject: [PATCH 081/400] Only do completeOnNewDevice if we received a
 confirmation code

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index d0c34b83af..f97a59b432 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
+                rendezvous.completeOnNewDevice()
             }
-            rendezvous.completeOnNewDevice()
         }
     //        if (isValidQrCode(action.qrCode)) {
 //            setState {

From 7a905dc2dae114212c409298e77742679e6cb324 Mon Sep 17 00:00:00 2001
From: Kat Gerasimova <ekaterinag@element.io>
Date: Wed, 12 Oct 2022 22:45:22 +0100
Subject: [PATCH 082/400] Update enhancement.yml

---
 .github/ISSUE_TEMPLATE/enhancement.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml
index 2dd968951f..cf5ce68557 100644
--- a/.github/ISSUE_TEMPLATE/enhancement.yml
+++ b/.github/ISSUE_TEMPLATE/enhancement.yml
@@ -5,7 +5,7 @@ body:
   - type: markdown
     attributes:
       value: |
-        Thank you for taking the time to propose a new feature or make a suggestion.
+        Thank you for taking the time to propose an enhancement to an existing feture. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/vector-im/element-meta/discussions/new?category=ideas).
   - type: textarea
     id: usecase
     attributes:

From 4feb60145b8d6a56f7c8d7d106f568f762008fa2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 12 Oct 2022 23:06:56 +0000
Subject: [PATCH 083/400] Bump appDistribution from 16.0.0-beta04 to
 16.0.0-beta05

Bumps `appDistribution` from 16.0.0-beta04 to 16.0.0-beta05.

Updates `firebase-appdistribution-api-ktx` from 16.0.0-beta04 to 16.0.0-beta05

Updates `firebase-appdistribution` from 16.0.0-beta04 to 16.0.0-beta05

---
updated-dependencies:
- dependency-name: com.google.firebase:firebase-appdistribution-api-ktx
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: com.google.firebase:firebase-appdistribution
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index edff769070..30bd576c21 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -12,7 +12,7 @@ def gradle = "7.2.2"
 def kotlin = "1.7.20"
 def kotlinCoroutines = "1.6.4"
 def dagger = "2.44"
-def appDistribution = "16.0.0-beta04"
+def appDistribution = "16.0.0-beta05"
 def retrofit = "2.9.0"
 def markwon = "4.6.2"
 def moshi = "1.14.0"

From 9198cc7ac072c11fb5d038b0d232ade2102ca2ab Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 12 Oct 2022 23:07:54 +0000
Subject: [PATCH 084/400] Bump android-connector from 2.1.0 to 2.1.1

Bumps [android-connector](https://github.com/UnifiedPush/android-connector) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/UnifiedPush/android-connector/releases)
- [Commits](https://github.com/UnifiedPush/android-connector/compare/2.1.0...2.1.1)

---
updated-dependencies:
- dependency-name: com.github.UnifiedPush:android-connector
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 vector/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/build.gradle b/vector/build.gradle
index c59e1a3028..32579d0ccc 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -236,7 +236,7 @@ dependencies {
     implementation libs.sentry.sentryAndroid
 
     // UnifiedPush
-    implementation 'com.github.UnifiedPush:android-connector:2.1.0'
+    implementation 'com.github.UnifiedPush:android-connector:2.1.1'
 
     implementation "androidx.emoji2:emoji2:1.2.0"
 

From b274bf0760824c2b7849c2c11706c1857952e3ce Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 12 Oct 2022 23:07:58 +0000
Subject: [PATCH 085/400] Bump firebase-messaging from 23.0.8 to 23.1.0

Bumps firebase-messaging from 23.0.8 to 23.1.0.

---
updated-dependencies:
- dependency-name: com.google.firebase:firebase-messaging
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 vector-app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector-app/build.gradle b/vector-app/build.gradle
index eb19027880..47bd928f17 100644
--- a/vector-app/build.gradle
+++ b/vector-app/build.gradle
@@ -373,7 +373,7 @@ dependencies {
 
     gplayImplementation "com.google.android.gms:play-services-location:20.0.0"
     // UnifiedPush gplay flavor only
-    gplayImplementation('com.google.firebase:firebase-messaging:23.0.8') {
+    gplayImplementation('com.google.firebase:firebase-messaging:23.1.0') {
         exclude group: 'com.google.firebase', module: 'firebase-core'
         exclude group: 'com.google.firebase', module: 'firebase-analytics'
         exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'

From 1ede6b32a5c708f5b2636130ce37843fcc311b46 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 12 Oct 2022 13:41:58 +0200
Subject: [PATCH 086/400] Adding changelog entry

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

diff --git a/changelog.d/7344.feature b/changelog.d/7344.feature
new file mode 100644
index 0000000000..a6deb4a23a
--- /dev/null
+++ b/changelog.d/7344.feature
@@ -0,0 +1 @@
+[Device management] Add lab flag for matrix client info account data event

From 4fced5bd3eaafae1baa8e47c609554307d4b1e3c Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 12 Oct 2022 13:58:20 +0200
Subject: [PATCH 087/400] Adding nex entry in labs settings

---
 library/ui-strings/src/main/res/values/strings.xml        | 2 ++
 vector-config/src/main/res/values/config-settings.xml     | 1 +
 .../im/vector/app/features/settings/VectorPreferences.kt  | 8 ++++++++
 vector/src/main/res/xml/vector_settings_labs.xml          | 6 ++++++
 4 files changed, 17 insertions(+)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index c997fb5639..f21d7c68d3 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3336,6 +3336,8 @@
     <string name="device_manager_learn_more_session_rename">Other users in direct messages and rooms that you join are able to view a full list of your sessions.\n\nThis provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here.</string>
     <string name="labs_enable_session_manager_title">Enable new session manager</string>
     <string name="labs_enable_session_manager_summary">Have greater visibility and control over all your sessions.</string>
+    <string name="labs_enable_client_info_recording_title">Enable client info recording</string>
+    <string name="labs_enable_client_info_recording_summary">Record the client name, version, and url to recognise sessions more easily in session manager.</string>
 
     <!-- Note to translators: %s will be replaces with selected space name -->
     <string name="home_empty_space_no_rooms_title">%s\nis looking a little empty.</string>
diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml
index 9c7eee5c0c..ced678fb98 100755
--- a/vector-config/src/main/res/values/config-settings.xml
+++ b/vector-config/src/main/res/values/config-settings.xml
@@ -42,6 +42,7 @@
     <bool name="settings_labs_thread_messages_default">false</bool>
     <bool name="settings_labs_new_app_layout_default">true</bool>
     <bool name="settings_labs_new_session_manager_default">false</bool>
+    <bool name="settings_labs_client_info_recording_default">false</bool>
     <bool name="settings_timeline_show_live_sender_info_visible">true</bool>
     <bool name="settings_timeline_show_live_sender_info_default">false</bool>
     <bool name="settings_labs_rich_text_editor_visible">true</bool>
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
index aaf328e9c7..89fcda142a 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
@@ -73,6 +73,7 @@ class VectorPreferences @Inject constructor(
         const val SETTINGS_LABS_DEFERRED_DM_KEY = "SETTINGS_LABS_DEFERRED_DM_KEY"
         const val SETTINGS_LABS_RICH_TEXT_EDITOR_KEY = "SETTINGS_LABS_RICH_TEXT_EDITOR_KEY"
         const val SETTINGS_LABS_NEW_SESSION_MANAGER_KEY = "SETTINGS_LABS_NEW_SESSION_MANAGER_KEY"
+        const val SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY = "SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY"
         const val SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY"
         const val SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY"
         const val SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY"
@@ -1188,6 +1189,13 @@ class VectorPreferences @Inject constructor(
         return defaultPrefs.getBoolean(SETTINGS_LABS_NEW_SESSION_MANAGER_KEY, getDefault(R.bool.settings_labs_new_session_manager_default))
     }
 
+    /**
+     * Indicates whether or not client info recording is enabled.
+     */
+    fun isClientInfoRecordingEnabled(): Boolean {
+        return defaultPrefs.getBoolean(SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY, getDefault(R.bool.settings_labs_client_info_recording_default))
+    }
+
     fun showLiveSenderInfo(): Boolean {
         return defaultPrefs.getBoolean(SETTINGS_TIMELINE_SHOW_LIVE_SENDER_INFO, getDefault(R.bool.settings_timeline_show_live_sender_info_default))
     }
diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml
index 51c78d278a..ea4d39aa47 100644
--- a/vector/src/main/res/xml/vector_settings_labs.xml
+++ b/vector/src/main/res/xml/vector_settings_labs.xml
@@ -109,4 +109,10 @@
         android:summary="@string/labs_enable_session_manager_summary"
         android:title="@string/labs_enable_session_manager_title" />
 
+    <im.vector.app.core.preference.VectorSwitchPreference
+        android:defaultValue="@bool/settings_labs_client_info_recording_default"
+        android:key="SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY"
+        android:summary="@string/labs_enable_client_info_recording_summary"
+        android:title="@string/labs_enable_client_info_recording_title" />
+
 </androidx.preference.PreferenceScreen>

From 89e14c915d30b4aec58e7019860b6f83a334db1e Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 12 Oct 2022 14:23:23 +0200
Subject: [PATCH 088/400] Moving labs fragment into dedicated package

---
 .../settings/{ => labs}/VectorSettingsLabsFragment.kt       | 6 ++++--
 vector/src/main/res/xml/vector_settings_root.xml            | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)
 rename vector/src/main/java/im/vector/app/features/settings/{ => labs}/VectorSettingsLabsFragment.kt (96%)

diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
similarity index 96%
rename from vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt
rename to vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
index 18bc35f72a..541811a650 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package im.vector.app.features.settings
+package im.vector.app.features.settings.labs
 
 import android.os.Bundle
 import android.text.method.LinkMovementMethod
@@ -30,6 +30,8 @@ import im.vector.app.features.MainActivityArgs
 import im.vector.app.features.VectorFeatures
 import im.vector.app.features.analytics.plan.MobileScreen
 import im.vector.app.features.home.room.threads.ThreadsManager
+import im.vector.app.features.settings.VectorPreferences
+import im.vector.app.features.settings.VectorSettingsBaseFragment
 import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
 import javax.inject.Inject
 
diff --git a/vector/src/main/res/xml/vector_settings_root.xml b/vector/src/main/res/xml/vector_settings_root.xml
index 02dc36bf39..ffa8688cbd 100644
--- a/vector/src/main/res/xml/vector_settings_root.xml
+++ b/vector/src/main/res/xml/vector_settings_root.xml
@@ -35,7 +35,7 @@
     <im.vector.app.core.preference.VectorPreference
         android:icon="@drawable/ic_settings_root_labs"
         android:title="@string/room_settings_labs_pref_title"
-        app:fragment="im.vector.app.features.settings.VectorSettingsLabsFragment"
+        app:fragment="im.vector.app.features.settings.labs.VectorSettingsLabsFragment"
         app:isPreferenceVisible="@bool/settings_root_labs_visible" />
 
     <im.vector.app.core.preference.VectorPreference

From f753e475d853154fabc1ab443053aa0956440aea Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 12 Oct 2022 15:38:36 +0200
Subject: [PATCH 089/400] Deleting/Updating the client Info when changing the
 lab flag

---
 .../app/core/di/MavericksViewModelModule.kt   |  6 ++
 .../DeleteMatrixClientInfoUseCase.kt          | 42 ++++++++++++
 .../settings/labs/VectorSettingsLabsAction.kt | 24 +++++++
 .../labs/VectorSettingsLabsFragment.kt        | 17 +++++
 .../labs/VectorSettingsLabsViewModel.kt       | 68 +++++++++++++++++++
 .../labs/VectorSettingsLabsViewState.kt       | 21 ++++++
 6 files changed, 178 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsAction.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewState.kt

diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
index 38b62e1511..d2afdb65e8 100644
--- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
@@ -100,6 +100,7 @@ import im.vector.app.features.settings.devtools.KeyRequestViewModel
 import im.vector.app.features.settings.font.FontScaleSettingViewModel
 import im.vector.app.features.settings.homeserver.HomeserverSettingsViewModel
 import im.vector.app.features.settings.ignored.IgnoredUsersViewModel
+import im.vector.app.features.settings.labs.VectorSettingsLabsViewModel
 import im.vector.app.features.settings.legals.LegalsViewModel
 import im.vector.app.features.settings.locale.LocalePickerViewModel
 import im.vector.app.features.settings.push.PushGatewaysViewModel
@@ -665,4 +666,9 @@ interface MavericksViewModelModule {
     @IntoMap
     @MavericksViewModelKey(SessionLearnMoreViewModel::class)
     fun sessionLearnMoreViewModelFactory(factory: SessionLearnMoreViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
+
+    @Binds
+    @IntoMap
+    @MavericksViewModelKey(VectorSettingsLabsViewModel::class)
+    fun vectorSettingsLabsViewModelFactory(factory: VectorSettingsLabsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
 }
diff --git a/vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt b/vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt
new file mode 100644
index 0000000000..3957c81997
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.core.session.clientinfo
+
+import im.vector.app.core.di.ActiveSessionHolder
+import timber.log.Timber
+import javax.inject.Inject
+
+/**
+ * This use case delete the account data event containing extended client info.
+ */
+class DeleteMatrixClientInfoUseCase @Inject constructor(
+        private val activeSessionHolder: ActiveSessionHolder,
+        private val setMatrixClientInfoUseCase: SetMatrixClientInfoUseCase,
+) {
+
+    // TODO add unit tests
+    suspend fun execute(): Result<Unit> = runCatching {
+        Timber.d("deleting recorded client info")
+        val session = activeSessionHolder.getActiveSession()
+        val emptyClientInfo = MatrixClientInfoContent(
+                name = "",
+                version = "",
+                url = "",
+        )
+        setMatrixClientInfoUseCase.execute(session, emptyClientInfo)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsAction.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsAction.kt
new file mode 100644
index 0000000000..db48c3400b
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsAction.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.settings.labs
+
+import im.vector.app.core.platform.VectorViewModelAction
+
+sealed class VectorSettingsLabsAction : VectorViewModelAction {
+    object UpdateClientInfo : VectorSettingsLabsAction()
+    object DeleteRecordedClientInfo : VectorSettingsLabsAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
index 541811a650..5840093bee 100644
--- a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
@@ -20,7 +20,9 @@ import android.os.Bundle
 import android.text.method.LinkMovementMethod
 import android.widget.TextView
 import androidx.preference.Preference
+import androidx.preference.Preference.OnPreferenceChangeListener
 import androidx.preference.SwitchPreference
+import com.airbnb.mvrx.fragmentViewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
@@ -39,6 +41,8 @@ import javax.inject.Inject
 class VectorSettingsLabsFragment :
         VectorSettingsBaseFragment() {
 
+    private val viewModel: VectorSettingsLabsViewModel by fragmentViewModel()
+
     @Inject lateinit var vectorPreferences: VectorPreferences
     @Inject lateinit var lightweightSettingsStorage: LightweightSettingsStorage
     @Inject lateinit var threadsManager: ThreadsManager
@@ -87,6 +91,7 @@ class VectorSettingsLabsFragment :
         }
 
         configureUnreadNotificationsAsTabPreference()
+        configureEnableClientInfoRecordingPreference()
     }
 
     private fun configureUnreadNotificationsAsTabPreference() {
@@ -142,4 +147,16 @@ class VectorSettingsLabsFragment :
     private fun onNewLayoutPreferenceClicked() {
         configureUnreadNotificationsAsTabPreference()
     }
+
+    private fun configureEnableClientInfoRecordingPreference() {
+        findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY)?.onPreferenceChangeListener =
+                OnPreferenceChangeListener { _, newValue ->
+                    when {
+                        (newValue as? Boolean) == false -> viewModel.handle(VectorSettingsLabsAction.DeleteRecordedClientInfo)
+                        (newValue as? Boolean) == true -> viewModel.handle(VectorSettingsLabsAction.UpdateClientInfo)
+                        else -> Unit
+                    }
+                    true
+                }
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt
new file mode 100644
index 0000000000..2f4cc0562b
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.settings.labs
+
+import com.airbnb.mvrx.MavericksViewModelFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import im.vector.app.core.di.ActiveSessionHolder
+import im.vector.app.core.di.MavericksAssistedViewModelFactory
+import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.platform.EmptyViewEvents
+import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.core.session.clientinfo.DeleteMatrixClientInfoUseCase
+import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
+import kotlinx.coroutines.launch
+
+class VectorSettingsLabsViewModel @AssistedInject constructor(
+        @Assisted initialState: VectorSettingsLabsViewState,
+        private val activeSessionHolder: ActiveSessionHolder,
+        private val updateMatrixClientInfoUseCase: UpdateMatrixClientInfoUseCase,
+        private val deleteMatrixClientInfoUseCase: DeleteMatrixClientInfoUseCase,
+) : VectorViewModel<VectorSettingsLabsViewState, VectorSettingsLabsAction, EmptyViewEvents>(initialState) {
+
+    @AssistedFactory
+    interface Factory : MavericksAssistedViewModelFactory<VectorSettingsLabsViewModel, VectorSettingsLabsViewState> {
+        override fun create(initialState: VectorSettingsLabsViewState): VectorSettingsLabsViewModel
+    }
+
+    companion object : MavericksViewModelFactory<VectorSettingsLabsViewModel, VectorSettingsLabsViewState> by hiltMavericksViewModelFactory()
+
+    // TODO add unit tests
+    override fun handle(action: VectorSettingsLabsAction) {
+        when (action) {
+            VectorSettingsLabsAction.UpdateClientInfo -> handleUpdateClientInfo()
+            VectorSettingsLabsAction.DeleteRecordedClientInfo -> handleDeleteRecordedClientInfo()
+        }
+    }
+
+    private fun handleUpdateClientInfo() {
+        viewModelScope.launch {
+            activeSessionHolder.getSafeActiveSession()
+                    ?.let { session ->
+                        updateMatrixClientInfoUseCase.execute(session)
+                    }
+        }
+    }
+
+    private fun handleDeleteRecordedClientInfo() {
+        viewModelScope.launch {
+            deleteMatrixClientInfoUseCase.execute()
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewState.kt
new file mode 100644
index 0000000000..c246d30994
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewState.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.settings.labs
+
+import com.airbnb.mvrx.MavericksState
+
+class VectorSettingsLabsViewState : MavericksState

From 8b30ab69c5aa4a90d14d195c19352881f773e0c6 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 12 Oct 2022 16:00:32 +0200
Subject: [PATCH 090/400] Checking lab flag before updating the client info

---
 .../ConfigureAndStartSessionUseCase.kt        |  6 ++++-
 .../ConfigureAndStartSessionUseCaseTest.kt    | 26 ++++++++++++++++++-
 .../app/test/fakes/FakeVectorPreferences.kt   |  4 +++
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt
index dfcb92af24..a5e1fe68bd 100644
--- a/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt
+++ b/vector/src/main/java/im/vector/app/core/session/ConfigureAndStartSessionUseCase.kt
@@ -21,6 +21,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
 import im.vector.app.core.extensions.startSyncing
 import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
 import im.vector.app.features.call.webrtc.WebRtcCallManager
+import im.vector.app.features.settings.VectorPreferences
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.sync.FilterService
 import timber.log.Timber
@@ -30,6 +31,7 @@ class ConfigureAndStartSessionUseCase @Inject constructor(
         @ApplicationContext private val context: Context,
         private val webRtcCallManager: WebRtcCallManager,
         private val updateMatrixClientInfoUseCase: UpdateMatrixClientInfoUseCase,
+        private val vectorPreferences: VectorPreferences,
 ) {
 
     suspend fun execute(session: Session, startSyncing: Boolean = true) {
@@ -41,6 +43,8 @@ class ConfigureAndStartSessionUseCase @Inject constructor(
         }
         session.pushersService().refreshPushers()
         webRtcCallManager.checkForProtocolsSupportIfNeeded()
-        updateMatrixClientInfoUseCase.execute(session)
+        if (vectorPreferences.isClientInfoRecordingEnabled()) {
+            updateMatrixClientInfoUseCase.execute(session)
+        }
     }
 }
diff --git a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt
index b879806930..5e1cba0b24 100644
--- a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt
@@ -20,6 +20,7 @@ import im.vector.app.core.extensions.startSyncing
 import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
 import im.vector.app.test.fakes.FakeContext
 import im.vector.app.test.fakes.FakeSession
+import im.vector.app.test.fakes.FakeVectorPreferences
 import im.vector.app.test.fakes.FakeWebRtcCallManager
 import io.mockk.coJustRun
 import io.mockk.coVerify
@@ -41,11 +42,13 @@ class ConfigureAndStartSessionUseCaseTest {
     private val fakeContext = FakeContext()
     private val fakeWebRtcCallManager = FakeWebRtcCallManager()
     private val fakeUpdateMatrixClientInfoUseCase = mockk<UpdateMatrixClientInfoUseCase>()
+    private val fakeVectorPreferences = FakeVectorPreferences()
 
     private val configureAndStartSessionUseCase = ConfigureAndStartSessionUseCase(
             context = fakeContext.instance,
             webRtcCallManager = fakeWebRtcCallManager.instance,
             updateMatrixClientInfoUseCase = fakeUpdateMatrixClientInfoUseCase,
+            vectorPreferences = fakeVectorPreferences.instance,
     )
 
     @Before
@@ -59,11 +62,12 @@ class ConfigureAndStartSessionUseCaseTest {
     }
 
     @Test
-    fun `given a session and start sync needed when configuring and starting the session then it should be configured properly`() = runTest {
+    fun `given start sync needed and client info recording enabled when configuring and starting the session then it should be configured properly`() = runTest {
         // Given
         val fakeSession = givenASession()
         fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
         coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
+        fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true)
 
         // When
         configureAndStartSessionUseCase.execute(fakeSession, startSyncing = true)
@@ -76,12 +80,32 @@ class ConfigureAndStartSessionUseCaseTest {
         coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) }
     }
 
+    @Test
+    fun `given start sync needed and client info recording disabled when configuring and starting the session then it should be configured properly`() = runTest {
+        // Given
+        val fakeSession = givenASession()
+        fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
+        coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
+        fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = false)
+
+        // When
+        configureAndStartSessionUseCase.execute(fakeSession, startSyncing = true)
+
+        // Then
+        verify { fakeSession.startSyncing(fakeContext.instance) }
+        fakeSession.fakeFilterService.verifySetFilter(FilterService.FilterPreset.ElementFilter)
+        fakeSession.fakePushersService.verifyRefreshPushers()
+        fakeWebRtcCallManager.verifyCheckForProtocolsSupportIfNeeded()
+        coVerify(inverse = true) { fakeUpdateMatrixClientInfoUseCase.execute(fakeSession) }
+    }
+
     @Test
     fun `given a session and no start sync needed when configuring and starting the session then it should be configured properly`() = runTest {
         // Given
         val fakeSession = givenASession()
         fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
         coJustRun { fakeUpdateMatrixClientInfoUseCase.execute(any()) }
+        fakeVectorPreferences.givenIsClientInfoRecordingEnabled(isEnabled = true)
 
         // When
         configureAndStartSessionUseCase.execute(fakeSession, startSyncing = false)
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt
index bc761d9016..8b0630c24f 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorPreferences.kt
@@ -36,4 +36,8 @@ class FakeVectorPreferences {
     fun verifySetSpaceBackstack(value: List<String?>, inverse: Boolean = false) {
         verify(inverse = inverse) { instance.setSpaceBackstack(value) }
     }
+
+    fun givenIsClientInfoRecordingEnabled(isEnabled: Boolean) {
+        every { instance.isClientInfoRecordingEnabled() } returns isEnabled
+    }
 }

From 5e87a6936dd83cf42ec57b154fd6ae404ed5761b Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 12 Oct 2022 16:13:04 +0200
Subject: [PATCH 091/400] Adding unit tests for new view model

---
 .../labs/VectorSettingsLabsViewModel.kt       |  1 -
 .../labs/VectorSettingsLabsViewModelTest.kt   | 86 +++++++++++++++++++
 2 files changed, 86 insertions(+), 1 deletion(-)
 create mode 100644 vector/src/test/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModelTest.kt

diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt
index 2f4cc0562b..38686b06be 100644
--- a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModel.kt
@@ -43,7 +43,6 @@ class VectorSettingsLabsViewModel @AssistedInject constructor(
 
     companion object : MavericksViewModelFactory<VectorSettingsLabsViewModel, VectorSettingsLabsViewState> by hiltMavericksViewModelFactory()
 
-    // TODO add unit tests
     override fun handle(action: VectorSettingsLabsAction) {
         when (action) {
             VectorSettingsLabsAction.UpdateClientInfo -> handleUpdateClientInfo()
diff --git a/vector/src/test/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModelTest.kt
new file mode 100644
index 0000000000..b3a4648263
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/labs/VectorSettingsLabsViewModelTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.settings.labs
+
+import com.airbnb.mvrx.test.MavericksTestRule
+import im.vector.app.core.session.clientinfo.DeleteMatrixClientInfoUseCase
+import im.vector.app.core.session.clientinfo.UpdateMatrixClientInfoUseCase
+import im.vector.app.test.fakes.FakeActiveSessionHolder
+import im.vector.app.test.test
+import im.vector.app.test.testDispatcher
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.mockk
+import org.junit.Rule
+import org.junit.Test
+
+class VectorSettingsLabsViewModelTest {
+
+    @get:Rule
+    val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
+
+    private val fakeActiveSessionHolder = FakeActiveSessionHolder()
+    private val fakeUpdateMatrixClientInfoUseCase = mockk<UpdateMatrixClientInfoUseCase>()
+    private val fakeDeleteMatrixClientInfoUseCase = mockk<DeleteMatrixClientInfoUseCase>()
+
+    private fun createViewModel(): VectorSettingsLabsViewModel {
+        return VectorSettingsLabsViewModel(
+                initialState = VectorSettingsLabsViewState(),
+                activeSessionHolder = fakeActiveSessionHolder.instance,
+                updateMatrixClientInfoUseCase = fakeUpdateMatrixClientInfoUseCase,
+                deleteMatrixClientInfoUseCase = fakeDeleteMatrixClientInfoUseCase,
+        )
+    }
+
+    @Test
+    fun `given update client info action when handling this action then update client info use case is called`() {
+        // Given
+        givenUpdateClientInfoSucceeds()
+
+        // When
+        val viewModel = createViewModel()
+        val viewModelTest = viewModel.test()
+        viewModel.handle(VectorSettingsLabsAction.UpdateClientInfo)
+
+        // Then
+        viewModelTest.finish()
+        coVerify { fakeUpdateMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession) }
+    }
+
+    @Test
+    fun `given delete client info action when handling this action then delete client info use case is called`() {
+        // Given
+        givenDeleteClientInfoSucceeds()
+
+        // When
+        val viewModel = createViewModel()
+        val viewModelTest = viewModel.test()
+        viewModel.handle(VectorSettingsLabsAction.DeleteRecordedClientInfo)
+
+        // Then
+        viewModelTest.finish()
+        coVerify { fakeDeleteMatrixClientInfoUseCase.execute() }
+    }
+
+    private fun givenUpdateClientInfoSucceeds() {
+        coEvery { fakeUpdateMatrixClientInfoUseCase.execute(any()) } returns Result.success(Unit)
+    }
+
+    private fun givenDeleteClientInfoSucceeds() {
+        coEvery { fakeDeleteMatrixClientInfoUseCase.execute() } returns Result.success(Unit)
+    }
+}

From 0c7f04c4a1cf971b1abe6c53caeea3920ab2409c Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 12 Oct 2022 17:23:30 +0200
Subject: [PATCH 092/400] Adding unit tests for DeleteMatrixClientInfoUseCase

---
 .../DeleteMatrixClientInfoUseCase.kt          |  3 +-
 .../DeleteMatrixClientInfoUseCaseTest.kt      | 82 +++++++++++++++++++
 2 files changed, 83 insertions(+), 2 deletions(-)
 create mode 100644 vector/src/test/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCaseTest.kt

diff --git a/vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt b/vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt
index 3957c81997..bab3af9af5 100644
--- a/vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt
+++ b/vector/src/main/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCase.kt
@@ -28,7 +28,6 @@ class DeleteMatrixClientInfoUseCase @Inject constructor(
         private val setMatrixClientInfoUseCase: SetMatrixClientInfoUseCase,
 ) {
 
-    // TODO add unit tests
     suspend fun execute(): Result<Unit> = runCatching {
         Timber.d("deleting recorded client info")
         val session = activeSessionHolder.getActiveSession()
@@ -37,6 +36,6 @@ class DeleteMatrixClientInfoUseCase @Inject constructor(
                 version = "",
                 url = "",
         )
-        setMatrixClientInfoUseCase.execute(session, emptyClientInfo)
+        return setMatrixClientInfoUseCase.execute(session, emptyClientInfo)
     }
 }
diff --git a/vector/src/test/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCaseTest.kt
new file mode 100644
index 0000000000..c89bd73979
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/core/session/clientinfo/DeleteMatrixClientInfoUseCaseTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.core.session.clientinfo
+
+import im.vector.app.test.fakes.FakeActiveSessionHolder
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.mockk
+import kotlinx.coroutines.test.runTest
+import org.amshove.kluent.shouldBe
+import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Test
+
+class DeleteMatrixClientInfoUseCaseTest {
+
+    private val fakeActiveSessionHolder = FakeActiveSessionHolder()
+    private val fakeSetMatrixClientInfoUseCase = mockk<SetMatrixClientInfoUseCase>()
+
+    private val deleteMatrixClientInfoUseCase = DeleteMatrixClientInfoUseCase(
+            activeSessionHolder = fakeActiveSessionHolder.instance,
+            setMatrixClientInfoUseCase = fakeSetMatrixClientInfoUseCase
+    )
+
+    @Test
+    fun `given current session when calling use case then empty client info is set and result is success`() = runTest {
+        // Given
+        givenSetMatrixClientInfoSucceeds()
+        val expectedClientInfoToBeSet = MatrixClientInfoContent(
+                name = "",
+                version = "",
+                url = "",
+        )
+
+        // When
+        val result = deleteMatrixClientInfoUseCase.execute()
+
+        // Then
+        result.isSuccess shouldBe true
+        coVerify {
+            fakeSetMatrixClientInfoUseCase.execute(
+                    fakeActiveSessionHolder.fakeSession,
+                    expectedClientInfoToBeSet
+            )
+        }
+    }
+
+    @Test
+    fun `given current session and error during the process when calling use case then result is failure`() = runTest {
+        // Given
+        val error = Exception()
+        givenSetMatrixClientInfoFails(error)
+
+        // When
+        val result = deleteMatrixClientInfoUseCase.execute()
+
+        // Then
+        result.isFailure shouldBe true
+        result.exceptionOrNull() shouldBeEqualTo error
+    }
+
+    private fun givenSetMatrixClientInfoSucceeds() {
+        coEvery { fakeSetMatrixClientInfoUseCase.execute(any(), any()) } returns Result.success(Unit)
+    }
+
+    private fun givenSetMatrixClientInfoFails(error: Exception) {
+        coEvery { fakeSetMatrixClientInfoUseCase.execute(any(), any()) } returns Result.failure(error)
+    }
+}

From b201ef8fbb9d2523aa10080b675c1c3bc0e7f4d8 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 09:46:35 +0200
Subject: [PATCH 093/400] Fix code styling issues

---
 .../app/core/session/ConfigureAndStartSessionUseCaseTest.kt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt
index 5e1cba0b24..8d4507e85d 100644
--- a/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/core/session/ConfigureAndStartSessionUseCaseTest.kt
@@ -62,7 +62,7 @@ class ConfigureAndStartSessionUseCaseTest {
     }
 
     @Test
-    fun `given start sync needed and client info recording enabled when configuring and starting the session then it should be configured properly`() = runTest {
+    fun `given start sync needed and client info recording enabled when execute then it should be configured properly`() = runTest {
         // Given
         val fakeSession = givenASession()
         fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
@@ -81,7 +81,7 @@ class ConfigureAndStartSessionUseCaseTest {
     }
 
     @Test
-    fun `given start sync needed and client info recording disabled when configuring and starting the session then it should be configured properly`() = runTest {
+    fun `given start sync needed and client info recording disabled when execute then it should be configured properly`() = runTest {
         // Given
         val fakeSession = givenASession()
         fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()
@@ -100,7 +100,7 @@ class ConfigureAndStartSessionUseCaseTest {
     }
 
     @Test
-    fun `given a session and no start sync needed when configuring and starting the session then it should be configured properly`() = runTest {
+    fun `given a session and no start sync needed when execute then it should be configured properly`() = runTest {
         // Given
         val fakeSession = givenASession()
         fakeWebRtcCallManager.givenCheckForProtocolsSupportIfNeededSucceeds()

From 1b41b8b538eed2a54a45cc31e6d4337a98168952 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 09:56:37 +0200
Subject: [PATCH 094/400] Revert change of year for the Labs fragment

---
 .../app/features/settings/labs/VectorSettingsLabsFragment.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
index 5840093bee..37998fa2a0 100644
--- a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright (c) 2019 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.

From 893008a93d5ad76d00c5f4877f3921a188567a4f Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 10 Oct 2022 17:09:46 +0200
Subject: [PATCH 095/400] Adding changelog entry

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

diff --git a/changelog.d/7327.wip b/changelog.d/7327.wip
new file mode 100644
index 0000000000..8f0191f948
--- /dev/null
+++ b/changelog.d/7327.wip
@@ -0,0 +1 @@
+[Device management] Update the unknown verification status icon

From b18b95d1a7b40fd626f0ab64768f25fc3500a131 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 10 Oct 2022 17:32:51 +0200
Subject: [PATCH 096/400] Adding new unknown shield icon

---
 .../app/core/ui/views/ShieldImageView.kt      |  6 +++---
 .../main/res/drawable/ic_shield_unknown.xml   | 20 +++++++++++++++++++
 .../drawable/ic_shield_unknown_no_border.xml  | 18 +++++++++++++++++
 3 files changed, 41 insertions(+), 3 deletions(-)
 create mode 100644 vector/src/main/res/drawable/ic_shield_unknown.xml
 create mode 100644 vector/src/main/res/drawable/ic_shield_unknown_no_border.xml

diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt
index 1990859668..6327daec86 100644
--- a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt
+++ b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt
@@ -45,8 +45,8 @@ class ShieldImageView @JvmOverloads constructor(
             RoomEncryptionTrustLevel.Default -> {
                 contentDescription = context.getString(R.string.a11y_trust_level_default)
                 setImageResource(
-                        if (borderLess) R.drawable.ic_shield_black_no_border
-                        else R.drawable.ic_shield_black
+                        if (borderLess) R.drawable.ic_shield_unknown_no_border
+                        else R.drawable.ic_shield_unknown
                 )
             }
             RoomEncryptionTrustLevel.Warning -> {
@@ -137,7 +137,7 @@ class ShieldImageView @JvmOverloads constructor(
 @DrawableRes
 fun RoomEncryptionTrustLevel.toDrawableRes(): Int {
     return when (this) {
-        RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black
+        RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_unknown
         RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning
         RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted
         RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> R.drawable.ic_warning_badge
diff --git a/vector/src/main/res/drawable/ic_shield_unknown.xml b/vector/src/main/res/drawable/ic_shield_unknown.xml
new file mode 100644
index 0000000000..7f5fbc730b
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_shield_unknown.xml
@@ -0,0 +1,20 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:strokeWidth="1"
+      android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
+      android:fillColor="#737D8C"
+      android:strokeColor="#ffffff"/>
+  <path
+      android:strokeWidth="1"
+      android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
+      android:fillColor="#737D8C"
+      android:strokeColor="#ffffff"/>
+  <path
+      android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z"
+      android:fillColor="#ffffff"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml
new file mode 100644
index 0000000000..89331edf9e
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml
@@ -0,0 +1,18 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:strokeWidth="1"
+      android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
+      android:fillColor="#737D8C"
+      android:strokeColor="#ffffff"/>
+  <path
+      android:pathData="M1.5,12.405V3.075L12,0L22.5,3.075V12.405C22.5,21.945 12,24 12,24C12,24 1.5,21.945 1.5,12.405Z"
+      android:fillColor="#737D8C"/>
+  <path
+      android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z"
+      android:fillColor="#ffffff"
+      android:fillType="evenOdd"/>
+</vector>

From ea0c99011f5603f26e866b112a0ed5116efa5443 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 10:41:38 +0200
Subject: [PATCH 097/400] Updating descriptions when unknown verification
 status

---
 .../ui-strings/src/main/res/values/strings.xml    |  2 ++
 .../devices/v2/list/OtherSessionsController.kt    |  7 +++++--
 .../settings/devices/v2/list/SessionInfoView.kt   | 15 +++++++++++----
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index c997fb5639..97db6ea627 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3250,10 +3250,12 @@
     <string name="a11y_device_manager_device_type_unknown">Unknown device type</string>
     <string name="device_manager_verification_status_verified">Verified session</string>
     <string name="device_manager_verification_status_unverified">Unverified session</string>
+    <string name="device_manager_verification_status_unknown">Unknown verification status</string>
     <string name="device_manager_verification_status_detail_current_session_verified">Your current session is ready for secure messaging.</string>
     <string name="device_manager_verification_status_detail_other_session_verified">This session is ready for secure messaging.</string>
     <string name="device_manager_verification_status_detail_current_session_unverified">Verify your current session for enhanced secure messaging.</string>
     <string name="device_manager_verification_status_detail_other_session_unverified">Verify or sign out from this session for best security and reliability.</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Verify your current session to reveal this session\'s verification status.</string>
     <string name="device_manager_verify_session">Verify Session</string>
     <string name="device_manager_view_details">View Details</string>
     <string name="device_manager_other_sessions_view_all">View All (%1$d)</string>
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt
index b0ba8baa1a..59e7e1888e 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt
@@ -53,7 +53,7 @@ class OtherSessionsController @Inject constructor(
             data.forEach { device ->
                 val dateFormatKind = if (device.isInactive) DateFormatKind.TIMELINE_DAY_DIVIDER else DateFormatKind.DEFAULT_DATE_AND_TIME
                 val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, dateFormatKind)
-                val description = calculateDescription(device, formattedLastActivityDate)
+                val description = buildDescription(device, formattedLastActivityDate)
                 val descriptionColor = if (device.isCurrentDevice) {
                     host.colorProvider.getColorFromAttribute(R.attr.colorError)
                 } else {
@@ -77,7 +77,7 @@ class OtherSessionsController @Inject constructor(
         }
     }
 
-    private fun calculateDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String {
+    private fun buildDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String {
         return when {
             device.isInactive -> {
                 stringProvider.getQuantityString(
@@ -93,6 +93,9 @@ class OtherSessionsController @Inject constructor(
             device.isCurrentDevice -> {
                 stringProvider.getString(R.string.device_manager_other_sessions_description_unverified_current_session)
             }
+            device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Default -> {
+                stringProvider.getString(R.string.device_manager_session_last_activity, formattedLastActivityDate)
+            }
             else -> {
                 stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate)
             }
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
index 6f6c5b24e2..bef3f5c9e4 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
@@ -90,10 +90,11 @@ class SessionInfoView @JvmOverloads constructor(
             isVerifyButtonVisible: Boolean,
     ) {
         views.sessionInfoVerificationStatusImageView.render(encryptionTrustLevel)
-        if (encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) {
-            renderCrossSigningVerified(isCurrentSession)
-        } else {
-            renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible)
+        when {
+            encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> renderCrossSigningVerified(isCurrentSession)
+            encryptionTrustLevel == RoomEncryptionTrustLevel.Default && !isCurrentSession -> renderCrossSigningUnknown()
+            else -> renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible)
+
         }
         if (hasLearnMoreLink) {
             appendLearnMoreToVerificationStatus()
@@ -142,6 +143,12 @@ class SessionInfoView @JvmOverloads constructor(
         views.sessionInfoVerifySessionButton.isVisible = isVerifyButtonVisible
     }
 
+    private fun renderCrossSigningUnknown() {
+        views.sessionInfoVerificationStatusTextView.text = context.getString(R.string.device_manager_verification_status_unknown)
+        views.sessionInfoVerificationStatusDetailTextView.text = context.getString(R.string.device_manager_verification_status_detail_other_session_unknown)
+        views.sessionInfoVerifySessionButton.isVisible = false
+    }
+
     private fun renderDeviceInfo(sessionName: String, deviceType: DeviceType, stringProvider: StringProvider) {
         setDeviceTypeIconUseCase.execute(deviceType, views.sessionInfoDeviceTypeImageView, stringProvider)
         views.sessionInfoNameTextView.text = sessionName

From a48131baad8d4c5e289b06990f758ce91f8c1ad7 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 11:05:02 +0200
Subject: [PATCH 098/400] Removing duplication of rendering session info

---
 .../v2/overview/SessionOverviewFragment.kt    | 23 -------------------
 1 file changed, 23 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
index 7c133bc229..606f647b91 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
@@ -24,7 +24,6 @@ import android.view.View
 import android.view.ViewGroup
 import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
-import androidx.core.view.isGone
 import androidx.core.view.isVisible
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.fragmentViewModel
@@ -42,7 +41,6 @@ import im.vector.app.core.resources.StringProvider
 import im.vector.app.databinding.FragmentSessionOverviewBinding
 import im.vector.app.features.auth.ReAuthActivity
 import im.vector.app.features.crypto.recover.SetupMode
-import im.vector.app.features.settings.devices.v2.DeviceFullInfo
 import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
 import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet
 import im.vector.app.features.workers.signout.SignOutUiWorker
@@ -181,11 +179,6 @@ class SessionOverviewFragment :
         updateSessionInfo(state)
         updateLoading(state.isLoading)
         updatePushNotificationToggle(state.deviceId, state.pushers.invoke().orEmpty())
-        if (state.deviceInfo is Success) {
-            renderSessionInfo(state.isCurrentSessionTrusted, state.deviceInfo.invoke())
-        } else {
-            hideSessionInfo()
-        }
     }
 
     private fun updateToolbar(viewState: SessionOverviewViewState) {
@@ -243,18 +236,6 @@ class SessionOverviewFragment :
         }
     }
 
-    private fun renderSessionInfo(isCurrentSession: Boolean, deviceFullInfo: DeviceFullInfo) {
-        views.sessionOverviewInfo.isVisible = true
-        val viewState = SessionInfoViewState(
-                isCurrentSession = isCurrentSession,
-                deviceFullInfo = deviceFullInfo,
-                isDetailsButtonVisible = false,
-                isLearnMoreLinkVisible = true,
-                isLastSeenDetailsVisible = true,
-        )
-        views.sessionOverviewInfo.render(viewState, dateFormatter, drawableProvider, colorProvider, stringProvider)
-    }
-
     private fun updateLoading(isLoading: Boolean) {
         if (isLoading) {
             showLoading(null)
@@ -313,8 +294,4 @@ class SessionOverviewFragment :
         )
         SessionLearnMoreBottomSheet.show(childFragmentManager, args)
     }
-
-    private fun hideSessionInfo() {
-        views.sessionOverviewInfo.isGone = true
-    }
 }

From 7be958e6a5ca84921e42c1d0fa810161b8cc4df7 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 11:06:34 +0200
Subject: [PATCH 099/400] Removing learn more in case of unknown verification
 status

---
 .../settings/devices/v2/overview/SessionOverviewFragment.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
index 606f647b91..463d5bb495 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
@@ -207,7 +207,7 @@ class SessionOverviewFragment :
                     deviceFullInfo = deviceInfo,
                     isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted,
                     isDetailsButtonVisible = false,
-                    isLearnMoreLinkVisible = true,
+                    isLearnMoreLinkVisible = deviceInfo.roomEncryptionTrustLevel != RoomEncryptionTrustLevel.Default,
                     isLastSeenDetailsVisible = !isCurrentSession,
             )
             views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider)

From 7ce56ced4d31a5d5c6029700f422377d6c72559b Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 11:08:57 +0200
Subject: [PATCH 100/400] Replacing error prone takeIf structure

---
 .../devices/v2/list/SessionInfoView.kt        | 53 +++++++++----------
 1 file changed, 25 insertions(+), 28 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
index bef3f5c9e4..9603085a57 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
@@ -162,34 +162,31 @@ class SessionInfoView @JvmOverloads constructor(
             drawableProvider: DrawableProvider,
             colorProvider: ColorProvider,
     ) {
-        deviceInfo.lastSeenTs
-                ?.takeIf { isLastSeenDetailsVisible }
-                ?.let { timestamp ->
-                    views.sessionInfoLastActivityTextView.isVisible = true
-                    views.sessionInfoLastActivityTextView.text = if (isInactive) {
-                        val formattedTs = dateFormatter.format(timestamp, DateFormatKind.TIMELINE_DAY_DIVIDER)
-                        context.resources.getQuantityString(
-                                R.plurals.device_manager_other_sessions_description_inactive,
-                                SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
-                                SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
-                                formattedTs
-                        )
-                    } else {
-                        val formattedTs = dateFormatter.format(timestamp, DateFormatKind.DEFAULT_DATE_AND_TIME)
-                        context.getString(R.string.device_manager_session_last_activity, formattedTs)
-                    }
-                    val drawable = if (isInactive) {
-                        val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
-                        drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor)
-                    } else {
-                        null
-                    }
-                    views.sessionInfoLastActivityTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
-                }
-                ?: run {
-                    views.sessionInfoLastActivityTextView.isGone = true
-                }
-
+        if (deviceInfo.lastSeenTs != null && isLastSeenDetailsVisible) {
+            val timestamp = deviceInfo.lastSeenTs
+            views.sessionInfoLastActivityTextView.isVisible = true
+            views.sessionInfoLastActivityTextView.text = if (isInactive) {
+                val formattedTs = dateFormatter.format(timestamp, DateFormatKind.TIMELINE_DAY_DIVIDER)
+                context.resources.getQuantityString(
+                        R.plurals.device_manager_other_sessions_description_inactive,
+                        SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
+                        SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS,
+                        formattedTs
+                )
+            } else {
+                val formattedTs = dateFormatter.format(timestamp, DateFormatKind.DEFAULT_DATE_AND_TIME)
+                context.getString(R.string.device_manager_session_last_activity, formattedTs)
+            }
+            val drawable = if (isInactive) {
+                val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
+                drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor)
+            } else {
+                null
+            }
+            views.sessionInfoLastActivityTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
+        } else {
+            views.sessionInfoLastActivityTextView.isGone = true
+        }
         views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible })
     }
 

From c17b55b874daa656a2ee295b372375adf841ad73 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Thu, 13 Oct 2022 11:11:23 +0200
Subject: [PATCH 101/400] Remove unused Animator lambda parameter + small
 cleanup

---
 .../attachmentviewer/SwipeToDismissHandler.kt | 24 ++++++++-----------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt b/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt
index 54cd34ecba..2f840cebee 100644
--- a/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt
+++ b/library/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/SwipeToDismissHandler.kt
@@ -96,31 +96,27 @@ class SwipeToDismissHandler(
                 .setDuration(ANIMATION_DURATION)
                 .setInterpolator(AccelerateInterpolator())
                 .setUpdateListener { onSwipeViewMove(swipeView.translationY, translationLimit) }
-                .setAnimatorListener(onAnimationEnd = {
+                .setAnimatorEndListener {
                     if (translationTo != 0f) {
                         onDismiss()
                     }
 
                     // remove the update listener, otherwise it will be saved on the next animation execution:
                     swipeView.animate().setUpdateListener(null)
-                })
+                }
                 .start()
     }
 }
 
-internal fun ViewPropertyAnimator.setAnimatorListener(
-        onAnimationEnd: ((Animator?) -> Unit)? = null,
-        onAnimationStart: ((Animator?) -> Unit)? = null
-) = this.setListener(
+private fun ViewPropertyAnimator.setAnimatorEndListener(
+        onAnimationEnd: () -> Unit,
+) = setListener(
         object : AnimatorListenerAdapter() {
             override fun onAnimationEnd(animation: Animator) {
-                onAnimationEnd?.invoke(animation)
+                onAnimationEnd()
             }
+        }
+)
 
-            override fun onAnimationStart(animation: Animator) {
-                onAnimationStart?.invoke(animation)
-            }
-        })
-
-internal val View?.hitRect: Rect
-    get() = Rect().also { this?.getHitRect(it) }
+private val View.hitRect: Rect
+    get() = Rect().also { getHitRect(it) }

From f77bceb918a81fd7655022ce08b0607fa4b73b5c Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 11:33:32 +0200
Subject: [PATCH 102/400] Hide unverified sessions section when current session
 is not verified

---
 .../v2/VectorSettingsDevicesFragment.kt       | 29 ++++++++++++++-----
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index 47ea96c09d..3cf3b33942 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -44,6 +44,7 @@ import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INAC
 import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationView
 import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState
 import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
+import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
 import javax.inject.Inject
 
 /**
@@ -164,12 +165,11 @@ class VectorSettingsDevicesFragment :
         if (state.devices is Success) {
             val devices = state.devices()
             val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId
-            val currentDeviceInfo = devices?.firstOrNull {
-                it.deviceInfo.deviceId == currentDeviceId
-            }
+            val currentDeviceInfo = devices?.firstOrNull { it.deviceInfo.deviceId == currentDeviceId }
+            val isCurrentSessionVerified = currentDeviceInfo?.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted
             val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId }
 
-            renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount)
+            renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount, isCurrentSessionVerified)
             renderCurrentDevice(currentDeviceInfo)
             renderOtherSessionsView(otherDevices)
         } else {
@@ -181,13 +181,18 @@ class VectorSettingsDevicesFragment :
         handleLoadingStatus(state.isLoading)
     }
 
-    private fun renderSecurityRecommendations(inactiveSessionsCount: Int, unverifiedSessionsCount: Int) {
+    private fun renderSecurityRecommendations(
+            inactiveSessionsCount: Int,
+            unverifiedSessionsCount: Int,
+            isCurrentSessionVerified: Boolean,
+    ) {
         if (unverifiedSessionsCount == 0 && inactiveSessionsCount == 0) {
             hideSecurityRecommendations()
         } else {
             views.deviceListHeaderSectionSecurityRecommendations.isVisible = true
             views.deviceListSecurityRecommendationsDivider.isVisible = true
-            views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0
+
+            views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0 && isCurrentSessionVerified
             views.deviceListInactiveSessionsRecommendation.isVisible = inactiveSessionsCount > 0
             val unverifiedSessionsViewState = SecurityRecommendationViewState(
                     description = getString(R.string.device_manager_unverified_sessions_description),
@@ -206,11 +211,19 @@ class VectorSettingsDevicesFragment :
         }
     }
 
+    private fun hideUnverifiedSessionsRecommendation() {
+        views.deviceListUnverifiedSessionsRecommendation.isVisible = false
+    }
+
+    private fun hideInactiveSessionsRecommendation() {
+        views.deviceListInactiveSessionsRecommendation.isVisible = false
+    }
+
     private fun hideSecurityRecommendations() {
         views.deviceListHeaderSectionSecurityRecommendations.isVisible = false
-        views.deviceListUnverifiedSessionsRecommendation.isVisible = false
-        views.deviceListInactiveSessionsRecommendation.isVisible = false
         views.deviceListSecurityRecommendationsDivider.isVisible = false
+        hideUnverifiedSessionsRecommendation()
+        hideInactiveSessionsRecommendation()
     }
 
     private fun renderOtherSessionsView(otherDevices: List<DeviceFullInfo>?) {

From 1a9b3941aec268fe776444d1f358abc0ea877532 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Thu, 13 Oct 2022 12:40:15 +0200
Subject: [PATCH 103/400] Fix the check script, some folders have changed.

---
 tools/check/check_code_quality.sh | 47 +++++++++++++++++--------------
 1 file changed, 26 insertions(+), 21 deletions(-)

diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh
index fc46fca758..b9cbfa4cac 100755
--- a/tools/check/check_code_quality.sh
+++ b/tools/check/check_code_quality.sh
@@ -68,15 +68,14 @@ ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt \
     ./matrix-sdk-android/src/main/java \
     ./matrix-sdk-android-flow/src/main/java \
     ./library/core-utils/src/main/java \
-    ./library/jsonviewer/src/main/java \
+    ./library/external/jsonviewer/src/main/java \
     ./library/ui-styles/src/main/java \
     ./vector/src/main/java \
-    ./vector/src/debug/java \
-    ./vector/src/release/java \
-    ./vector/src/fdroid/java \
-    ./vector/src/gplay/java \
+    ./vector-app/src/debug/java \
+    ./vector-app/src/fdroid/java \
     ./vector-app/src/gplay/java \
-    ./vector-app/src/main/java
+    ./vector-app/src/main/java \
+    ./vector-app/src/release/java
 
 resultForbiddenStringInCode=$?
 
@@ -93,13 +92,15 @@ echo
 echo "Search for forbidden patterns specific for App code..."
 
 ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code_app.txt \
+    ./library/core-utils/src/main/java \
+    ./library/external/jsonviewer/src/main/java \
+    ./library/ui-styles/src/main/java \
     ./vector/src/main/java \
-    ./vector/src/debug/java \
-    ./vector/src/release/java \
-    ./vector/src/fdroid/java \
-    ./vector/src/gplay/java \
+    ./vector-app/src/debug/java \
+    ./vector-app/src/fdroid/java \
     ./vector-app/src/gplay/java \
-    ./vector-app/src/main/java
+    ./vector-app/src/main/java \
+    ./vector-app/src/release/java
 
 resultForbiddenStringInCodeApp=$?
 
@@ -120,8 +121,7 @@ echo
 echo "Search for forbidden patterns in layouts..."
 
 ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_layout.txt \
-    ./vector/src/main/res/layout \
-    ./vector-app/src/main/res/layout
+    ./vector/src/main/res/layout
 
 resultForbiddenStringInLayout=$?
 
@@ -154,17 +154,19 @@ echo "Search for kotlin files with more than ${maxLines} lines..."
 ${checkLongFilesScript} ${maxLines} \
     ./matrix-sdk-android/src/main/java \
     ./matrix-sdk-android-flow/src/main/java \
+    ./library/core-utils/src/main/java \
+    ./library/external/jsonviewer/src/main/java \
+    ./library/ui-styles/src/main/java \
     ./vector/src/androidTest/java \
-    ./vector/src/debug/java \
-    ./vector/src/fdroid/java \
-    ./vector/src/gplay/java \
     ./vector/src/main/java \
-    ./vector/src/release/java \
     ./vector/src/sharedTest/java \
     ./vector/src/test/java \
-    ./vector/src/androidTest/java \
-    ./vector/src/gplay/java \
-    ./vector/src/main/java
+    ./vector-app/src/androidTest/java \
+    ./vector-app/src/debug/java \
+    ./vector-app/src/fdroid/java \
+    ./vector-app/src/gplay/java \
+    ./vector-app/src/main/java \
+    ./vector-app/src/release/java
 
 
 resultLongFiles=$?
@@ -179,8 +181,11 @@ echo "Search for png files in /drawable..."
 ls -1U ./vector/src/main/res/drawable/*.png
 resultTmp=$?
 
+ls -1U ./vector-app/src/main/res/drawable/*.png
+resultTmp2=$?
+
 # Inverse the result, cause no file found is an error for ls but this is what we want!
-if [[ ${resultTmp} -eq 0 ]]; then
+if [[ ${resultTmp} -eq 0 ]] || [[ ${resultTmp2} -eq 0 ]]; then
    echo "ERROR, png files detected in /drawable"
    resultPngInDrawable=1
 else

From 53655eb37dc12612b16a5d057e99a5653e15d15c Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Thu, 13 Oct 2022 14:00:34 +0200
Subject: [PATCH 104/400] Fix typo

---
 .github/ISSUE_TEMPLATE/enhancement.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml
index cf5ce68557..0e51d5155e 100644
--- a/.github/ISSUE_TEMPLATE/enhancement.yml
+++ b/.github/ISSUE_TEMPLATE/enhancement.yml
@@ -5,7 +5,7 @@ body:
   - type: markdown
     attributes:
       value: |
-        Thank you for taking the time to propose an enhancement to an existing feture. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/vector-im/element-meta/discussions/new?category=ideas).
+        Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/vector-im/element-meta/discussions/new?category=ideas).
   - type: textarea
     id: usecase
     attributes:

From 28ce915f186119bb85b82d38e4668c40922ffc05 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 15:01:31 +0200
Subject: [PATCH 105/400] Checking current session status in the filter use
 case

---
 .../settings/devices/v2/GetDeviceFullInfoListUseCase.kt  | 2 +-
 .../settings/devices/v2/filter/FilterDevicesUseCase.kt   | 9 +++++++--
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt
index 42e4cebe4c..6adb33d5ab 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt
@@ -57,7 +57,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor(
                 } else {
                     emptyList()
                 }
-                filterDevicesUseCase.execute(deviceFullInfoList, filterType, excludedDeviceIds)
+                filterDevicesUseCase.execute(currentSessionCrossSigningInfo, deviceFullInfoList, filterType, excludedDeviceIds)
             }
 
             deviceFullInfoFlow.distinctUntilChanged()
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt
index a23a7a7108..8f23fd06cc 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt
@@ -17,22 +17,27 @@
 package im.vector.app.features.settings.devices.v2.filter
 
 import im.vector.app.features.settings.devices.v2.DeviceFullInfo
+import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
 import org.matrix.android.sdk.api.extensions.orFalse
 import javax.inject.Inject
 
 class FilterDevicesUseCase @Inject constructor() {
 
     fun execute(
+            currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo,
             devices: List<DeviceFullInfo>,
             filterType: DeviceManagerFilterType,
             excludedDeviceIds: List<String> = emptyList(),
     ): List<DeviceFullInfo> {
+        val isCurrentSessionVerified = currentSessionCrossSigningInfo.isCrossSigningVerified.orFalse()
         return devices
                 .filter {
                     when (filterType) {
                         DeviceManagerFilterType.ALL_SESSIONS -> true
-                        DeviceManagerFilterType.VERIFIED -> it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
-                        DeviceManagerFilterType.UNVERIFIED -> !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
+                        // when current session is not verified, other session status cannot be trusted
+                        DeviceManagerFilterType.VERIFIED -> isCurrentSessionVerified && it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
+                        // when current session is not verified, other session status cannot be trusted
+                        DeviceManagerFilterType.UNVERIFIED -> isCurrentSessionVerified && !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse()
                         DeviceManagerFilterType.INACTIVE -> it.isInactive
                     }
                 }

From 316f1efe324c97fe8f8fc86066b65c15792738f5 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 15:21:25 +0200
Subject: [PATCH 106/400] Updating unit tests

---
 .../v2/GetDeviceFullInfoListUseCaseTest.kt    |  6 ++-
 .../v2/filter/FilterDevicesUseCaseTest.kt     | 39 ++++++++++++++++---
 2 files changed, 37 insertions(+), 8 deletions(-)

diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt
index ebdb74b74d..17ad58668a 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt
@@ -144,10 +144,11 @@ class GetDeviceFullInfoListUseCaseTest {
                 matrixClientInfo = matrixClientInfo3,
         )
         val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1)
-        every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult
+        every { filterDevicesUseCase.execute(any(), any(), any()) } returns expectedResult
+        val filterType = DeviceManagerFilterType.ALL_SESSIONS
 
         // When
-        val result = getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.ALL_SESSIONS, excludeCurrentDevice = false)
+        val result = getDeviceFullInfoListUseCase.execute(filterType, excludeCurrentDevice = false)
                 .test(this)
 
         // Then
@@ -166,6 +167,7 @@ class GetDeviceFullInfoListUseCaseTest {
             getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_1)
             getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_2)
             getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_3)
+            filterDevicesUseCase.execute(currentSessionCrossSigningInfo, expectedResult, filterType, emptyList())
         }
     }
 
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt
index 3a418cf2c0..79dff5bc16 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt
@@ -20,6 +20,7 @@ import im.vector.app.core.session.clientinfo.MatrixClientInfoContent
 import im.vector.app.features.settings.devices.v2.DeviceFullInfo
 import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo
 import im.vector.app.features.settings.devices.v2.list.DeviceType
+import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
 import org.amshove.kluent.shouldBeEqualTo
 import org.amshove.kluent.shouldContainAll
 import org.junit.Test
@@ -94,32 +95,58 @@ class FilterDevicesUseCaseTest {
 
     @Test
     fun `given a device list when filter type is ALL_SESSIONS then returns the same list`() {
-        val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.ALL_SESSIONS, emptyList())
+        val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
+        val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.ALL_SESSIONS, emptyList())
 
         filteredDeviceList.size shouldBeEqualTo devices.size
     }
 
     @Test
-    fun `given a device list when filter type is VERIFIED then returns only verified devices`() {
-        val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.VERIFIED, emptyList())
+    fun `given a device list and current session is verified when filter type is VERIFIED then returns only verified devices`() {
+        val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
+        val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.VERIFIED, emptyList())
 
         filteredDeviceList.size shouldBeEqualTo 2
         filteredDeviceList shouldContainAll listOf(activeVerifiedDevice, inactiveVerifiedDevice)
     }
 
     @Test
-    fun `given a device list when filter type is UNVERIFIED then returns only unverified devices`() {
-        val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
+    fun `given a device list and current session is unverified when filter type is VERIFIED then returns empty list of devices`() {
+        val currentSessionCrossSigningInfo = givenCurrentSessionVerified(false)
+        val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.VERIFIED, emptyList())
+
+        filteredDeviceList.size shouldBeEqualTo 0
+    }
+
+    @Test
+    fun `given a device list and current session is verified when filter type is UNVERIFIED then returns only unverified devices`() {
+        val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
+        val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
 
         filteredDeviceList.size shouldBeEqualTo 2
         filteredDeviceList shouldContainAll listOf(activeUnverifiedDevice, inactiveUnverifiedDevice)
     }
 
+    @Test
+    fun `given a device list and current session is unverified when filter type is UNVERIFIED then returns empty list of devices`() {
+        val currentSessionCrossSigningInfo = givenCurrentSessionVerified(false)
+        val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList())
+
+        filteredDeviceList.size shouldBeEqualTo 0
+    }
+
     @Test
     fun `given a device list when filter type is INACTIVE then returns only inactive devices`() {
-        val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.INACTIVE, emptyList())
+        val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true)
+        val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.INACTIVE, emptyList())
 
         filteredDeviceList.size shouldBeEqualTo 2
         filteredDeviceList shouldContainAll listOf(inactiveVerifiedDevice, inactiveUnverifiedDevice)
     }
+
+    private fun givenCurrentSessionVerified(isVerified: Boolean): CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(
+            isCrossSigningVerified = isVerified,
+            isCrossSigningInitialized = true,
+            deviceId = ""
+    )
 }

From f72ab9b51e8da1f21ad17b52558dc7eb172c3933 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Thu, 13 Oct 2022 15:22:50 +0200
Subject: [PATCH 107/400] Fixing code style issue

---
 .../app/features/settings/devices/v2/list/SessionInfoView.kt     | 1 -
 1 file changed, 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
index 9603085a57..3d9c3a8f37 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt
@@ -94,7 +94,6 @@ class SessionInfoView @JvmOverloads constructor(
             encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> renderCrossSigningVerified(isCurrentSession)
             encryptionTrustLevel == RoomEncryptionTrustLevel.Default && !isCurrentSession -> renderCrossSigningUnknown()
             else -> renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible)
-
         }
         if (hasLearnMoreLink) {
             appendLearnMoreToVerificationStatus()

From 8645f74c774fc5ef5a57070a05fc021fb6ba8628 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:11:15 +0100
Subject: [PATCH 108/400] Support for login by m.login.token during QR code
 sign in

---
 .../sdk/api/auth/AuthenticationService.kt     | 14 +++
 .../matrix/android/sdk/api/auth/LoginType.kt  |  4 +-
 .../auth/DefaultAuthenticationService.kt      | 15 +++-
 .../sdk/internal/auth/data/LoginParams.kt     |  2 +
 .../internal/auth/data/PasswordLoginParams.kt |  4 +-
 .../internal/auth/data/TokenLoginParams.kt    |  4 +-
 .../internal/auth/login/QrLoginTokenTask.kt   | 88 +++++++++++++++++++
 7 files changed, 126 insertions(+), 5 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 5ae70e1978..8f2a784d49 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -124,4 +124,18 @@ interface AuthenticationService {
             initialDeviceName: String,
             deviceId: String? = null
     ): Session
+
+    /**
+     * Authenticate using m.login.token method during sign in with QR code.
+     * @param homeServerConnectionConfig the information about the homeserver and other configuration
+     * @param loginToken the m.login.token
+     * @param initialDeviceName the initial device name
+     * @param deviceId the device id, optional. If not provided or null, the server will generate one.
+     */
+    suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String? = null
+    ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
index 627a825679..991b7b654d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
@@ -22,7 +22,8 @@ enum class LoginType {
     UNSUPPORTED,
     CUSTOM,
     DIRECT,
-    UNKNOWN;
+    UNKNOWN,
+    QR;
 
     companion object {
 
@@ -32,6 +33,7 @@ enum class LoginType {
             UNSUPPORTED.name -> UNSUPPORTED
             CUSTOM.name -> CUSTOM
             DIRECT.name -> DIRECT
+            QR.name -> QR
             else -> UNKNOWN
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 446f931847..90dc57b4f0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig
 import org.matrix.android.sdk.internal.auth.db.PendingSessionData
 import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
@@ -62,7 +63,8 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val sessionCreator: SessionCreator,
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
-        private val directLoginTask: DirectLoginTask
+        private val directLoginTask: DirectLoginTask,
+        private val loginTokenAuthTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -404,6 +406,17 @@ internal class DefaultAuthenticationService @Inject constructor(
         )
     }
 
+    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+        return loginTokenAuthTask.execute(
+                QrLoginTokenTask.Params(
+                        homeServerConnectionConfig = homeServerConnectionConfig,
+                        loginToken = loginToken,
+                        deviceName = initialDeviceName,
+                        deviceId = deviceId
+                )
+        )
+    }
+
     private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
         val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
         return retrofit.create(AuthAPI::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
index ea8578ed8c..8646752083 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
@@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data
 
 internal interface LoginParams {
     val type: String
+    val deviceDisplayName: String?
+    val deviceId: String?
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
index 5f0a2298cb..062b2466e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
@@ -30,8 +30,8 @@ internal data class PasswordLoginParams(
         @Json(name = "identifier") val identifier: Map<String, String>,
         @Json(name = "password") val password: String,
         @Json(name = "type") override val type: String,
-        @Json(name = "initial_device_display_name") val deviceDisplayName: String?,
-        @Json(name = "device_id") val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams {
 
     companion object {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 0c6fb45e78..22cc185fa7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 @JsonClass(generateAdapter = true)
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
-        @Json(name = "token") val token: String
+        @Json(name = "token") val token: String,
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
new file mode 100644
index 0000000000..477719f607
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.auth.login
+
+import dagger.Lazy
+import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.auth.LoginType
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.auth.AuthAPI
+import org.matrix.android.sdk.internal.auth.SessionCreator
+import org.matrix.android.sdk.internal.auth.data.TokenLoginParams
+import org.matrix.android.sdk.internal.di.Unauthenticated
+import org.matrix.android.sdk.internal.network.RetrofitFactory
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory
+import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface QrLoginTokenTask : Task<QrLoginTokenTask.Params, Session> {
+    data class Params(
+            val homeServerConnectionConfig: HomeServerConnectionConfig,
+            val loginToken: String,
+            val deviceName: String?,
+            val deviceId: String?
+    )
+}
+
+internal class DefaultQrLoginTokenTask @Inject constructor(
+        @Unauthenticated
+        private val okHttpClient: Lazy<OkHttpClient>,
+        private val retrofitFactory: RetrofitFactory,
+        private val sessionCreator: SessionCreator,
+) : QrLoginTokenTask {
+
+    override suspend fun execute(params: QrLoginTokenTask.Params): Session {
+        val client = buildClient(params.homeServerConnectionConfig)
+        val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
+
+        val authAPI = retrofitFactory.create(client, homeServerUrl)
+                .create(AuthAPI::class.java)
+
+        val loginParams = TokenLoginParams(
+                token = params.loginToken,
+                deviceDisplayName = params.deviceName,
+                deviceId = params.deviceId
+        )
+
+        val credentials = try {
+            executeRequest(null) {
+                authAPI.login(loginParams)
+            }
+        } catch (throwable: Throwable) {
+            throw when (throwable) {
+                is UnrecognizedCertificateException -> Failure.UnrecognizedCertificateFailure(
+                        homeServerUrl,
+                        throwable.fingerprint
+                )
+                else -> throwable
+            }
+        }
+
+        return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.QR)
+    }
+
+    private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient {
+        return okHttpClient.get()
+                .newBuilder()
+                .addSocketFactory(homeServerConnectionConfig)
+                .build()
+    }
+}

From f13419d696c401ee6d00045ec6bb173783fa6d4b Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:18:31 +0100
Subject: [PATCH 109/400] Changelog

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

diff --git a/changelog.d/7358.sdk b/changelog.d/7358.sdk
new file mode 100644
index 0000000000..3d17076a44
--- /dev/null
+++ b/changelog.d/7358.sdk
@@ -0,0 +1 @@
+Add support for `m.login.token` auth during QR code based sign in

From 09da74b029bfd96b73a173f340dc57c55bdbc950 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:19:39 +0100
Subject: [PATCH 110/400] Linting

---
 .../sdk/internal/auth/DefaultAuthenticationService.kt      | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 90dc57b4f0..7fd730bece 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -406,7 +406,12 @@ internal class DefaultAuthenticationService @Inject constructor(
         )
     }
 
-    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+    override suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String?,
+    ): Session {
         return loginTokenAuthTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,

From 21ae4c6ddbf29bd33f3888788717f0cbd06d74b5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:11:15 +0100
Subject: [PATCH 111/400] Support for login by m.login.token during QR code
 sign in

---
 .../sdk/api/auth/AuthenticationService.kt     | 14 +++
 .../matrix/android/sdk/api/auth/LoginType.kt  |  4 +-
 .../auth/DefaultAuthenticationService.kt      | 15 +++-
 .../sdk/internal/auth/data/LoginParams.kt     |  2 +
 .../internal/auth/data/PasswordLoginParams.kt |  4 +-
 .../internal/auth/data/TokenLoginParams.kt    |  4 +-
 .../internal/auth/login/QrLoginTokenTask.kt   | 88 +++++++++++++++++++
 7 files changed, 126 insertions(+), 5 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 5ae70e1978..8f2a784d49 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -124,4 +124,18 @@ interface AuthenticationService {
             initialDeviceName: String,
             deviceId: String? = null
     ): Session
+
+    /**
+     * Authenticate using m.login.token method during sign in with QR code.
+     * @param homeServerConnectionConfig the information about the homeserver and other configuration
+     * @param loginToken the m.login.token
+     * @param initialDeviceName the initial device name
+     * @param deviceId the device id, optional. If not provided or null, the server will generate one.
+     */
+    suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String? = null
+    ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
index 627a825679..991b7b654d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
@@ -22,7 +22,8 @@ enum class LoginType {
     UNSUPPORTED,
     CUSTOM,
     DIRECT,
-    UNKNOWN;
+    UNKNOWN,
+    QR;
 
     companion object {
 
@@ -32,6 +33,7 @@ enum class LoginType {
             UNSUPPORTED.name -> UNSUPPORTED
             CUSTOM.name -> CUSTOM
             DIRECT.name -> DIRECT
+            QR.name -> QR
             else -> UNKNOWN
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 446f931847..90dc57b4f0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig
 import org.matrix.android.sdk.internal.auth.db.PendingSessionData
 import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
@@ -62,7 +63,8 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val sessionCreator: SessionCreator,
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
-        private val directLoginTask: DirectLoginTask
+        private val directLoginTask: DirectLoginTask,
+        private val loginTokenAuthTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -404,6 +406,17 @@ internal class DefaultAuthenticationService @Inject constructor(
         )
     }
 
+    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+        return loginTokenAuthTask.execute(
+                QrLoginTokenTask.Params(
+                        homeServerConnectionConfig = homeServerConnectionConfig,
+                        loginToken = loginToken,
+                        deviceName = initialDeviceName,
+                        deviceId = deviceId
+                )
+        )
+    }
+
     private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
         val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
         return retrofit.create(AuthAPI::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
index ea8578ed8c..8646752083 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
@@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data
 
 internal interface LoginParams {
     val type: String
+    val deviceDisplayName: String?
+    val deviceId: String?
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
index 5f0a2298cb..062b2466e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
@@ -30,8 +30,8 @@ internal data class PasswordLoginParams(
         @Json(name = "identifier") val identifier: Map<String, String>,
         @Json(name = "password") val password: String,
         @Json(name = "type") override val type: String,
-        @Json(name = "initial_device_display_name") val deviceDisplayName: String?,
-        @Json(name = "device_id") val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams {
 
     companion object {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 0c6fb45e78..22cc185fa7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 @JsonClass(generateAdapter = true)
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
-        @Json(name = "token") val token: String
+        @Json(name = "token") val token: String,
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
new file mode 100644
index 0000000000..477719f607
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.auth.login
+
+import dagger.Lazy
+import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.auth.LoginType
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.auth.AuthAPI
+import org.matrix.android.sdk.internal.auth.SessionCreator
+import org.matrix.android.sdk.internal.auth.data.TokenLoginParams
+import org.matrix.android.sdk.internal.di.Unauthenticated
+import org.matrix.android.sdk.internal.network.RetrofitFactory
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory
+import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface QrLoginTokenTask : Task<QrLoginTokenTask.Params, Session> {
+    data class Params(
+            val homeServerConnectionConfig: HomeServerConnectionConfig,
+            val loginToken: String,
+            val deviceName: String?,
+            val deviceId: String?
+    )
+}
+
+internal class DefaultQrLoginTokenTask @Inject constructor(
+        @Unauthenticated
+        private val okHttpClient: Lazy<OkHttpClient>,
+        private val retrofitFactory: RetrofitFactory,
+        private val sessionCreator: SessionCreator,
+) : QrLoginTokenTask {
+
+    override suspend fun execute(params: QrLoginTokenTask.Params): Session {
+        val client = buildClient(params.homeServerConnectionConfig)
+        val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
+
+        val authAPI = retrofitFactory.create(client, homeServerUrl)
+                .create(AuthAPI::class.java)
+
+        val loginParams = TokenLoginParams(
+                token = params.loginToken,
+                deviceDisplayName = params.deviceName,
+                deviceId = params.deviceId
+        )
+
+        val credentials = try {
+            executeRequest(null) {
+                authAPI.login(loginParams)
+            }
+        } catch (throwable: Throwable) {
+            throw when (throwable) {
+                is UnrecognizedCertificateException -> Failure.UnrecognizedCertificateFailure(
+                        homeServerUrl,
+                        throwable.fingerprint
+                )
+                else -> throwable
+            }
+        }
+
+        return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.QR)
+    }
+
+    private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient {
+        return okHttpClient.get()
+                .newBuilder()
+                .addSocketFactory(homeServerConnectionConfig)
+                .build()
+    }
+}

From 098c268af3c348ce8ce7ef088047e907cd1efe83 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:18:31 +0100
Subject: [PATCH 112/400] Changelog

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

diff --git a/changelog.d/7358.sdk b/changelog.d/7358.sdk
new file mode 100644
index 0000000000..3d17076a44
--- /dev/null
+++ b/changelog.d/7358.sdk
@@ -0,0 +1 @@
+Add support for `m.login.token` auth during QR code based sign in

From a71ecee44a5b39d1ac13faa0078e0349d0bbf901 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:19:39 +0100
Subject: [PATCH 113/400] Linting

---
 .../sdk/internal/auth/DefaultAuthenticationService.kt      | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 90dc57b4f0..7fd730bece 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -406,7 +406,12 @@ internal class DefaultAuthenticationService @Inject constructor(
         )
     }
 
-    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+    override suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String?,
+    ): Session {
         return loginTokenAuthTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,

From 0d245657e1fd50596397e4dd48d70c24c8661fba Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:32:49 +0100
Subject: [PATCH 114/400] Retry scanning if not a QR code


From 1c70d455fbfda7a301ed01f1f18eef1ddf2aa902 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:33:30 +0100
Subject: [PATCH 115/400] Implementations of MSC3886 and MSC3903


From b192fdb0a89b1aa922440ae3dda3bf288a8b7611 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:34:05 +0100
Subject: [PATCH 116/400] Partial implementation of QR login logic

---
 .../features/login/qr/QrCodeLoginViewModel.kt |  2 +-
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 -------------------
 2 files changed, 1 insertion(+), 21 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index f97a59b432..d0c34b83af 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
-                rendezvous.completeOnNewDevice()
             }
+            rendezvous.completeOnNewDevice()
         }
     //        if (isValidQrCode(action.qrCode)) {
 //            setState {
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
deleted file mode 100644
index d7d74b08e9..0000000000
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * 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.voicebroadcast
-
-/** Voice Broadcast State Event. */
-const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From 6e09d900070319b356a2b10a1769af8da97a2aef Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 12 Oct 2022 14:32:09 +0300
Subject: [PATCH 117/400] Merge branch 'develop' into
 feature/ons/qr_code_login_ui

# Conflicts:
#	library/ui-strings/src/main/res/values/strings.xml
#	library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
#	vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
#	vector/src/main/java/im/vector/app/features/VectorFeatures.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
#	vector/src/main/res/layout/fragment_other_sessions.xml
#	vector/src/main/res/layout/fragment_settings_devices.xml
---
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
new file mode 100644
index 0000000000..d7d74b08e9
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.voicebroadcast
+
+/** Voice Broadcast State Event. */
+const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From 86090086b1344a8e80f8bd3accb1cbbf26a2eab2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Wed, 12 Oct 2022 13:08:01 +0100
Subject: [PATCH 118/400] Only do completeOnNewDevice if we received a
 confirmation code

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index d0c34b83af..f97a59b432 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
+                rendezvous.completeOnNewDevice()
             }
-            rendezvous.completeOnNewDevice()
         }
     //        if (isValidQrCode(action.qrCode)) {
 //            setState {

From 5bff9ceec46d32b983d75230b170e89a34d1eef7 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:35:45 +0100
Subject: [PATCH 119/400] Make initialDeviceName optional

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 8f2a784d49..c8065e4524 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -135,7 +135,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String? = null
     ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 7fd730bece..6c3622ed5d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -409,7 +409,7 @@ internal class DefaultAuthenticationService @Inject constructor(
     override suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String?,
     ): Session {
         return loginTokenAuthTask.execute(

From ebb3d201c18efba47ec546d778c2f311f266ff9e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:35:45 +0100
Subject: [PATCH 120/400] Make initialDeviceName optional

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 8f2a784d49..c8065e4524 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -135,7 +135,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String? = null
     ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 7fd730bece..6c3622ed5d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -409,7 +409,7 @@ internal class DefaultAuthenticationService @Inject constructor(
     override suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String?,
     ): Session {
         return loginTokenAuthTask.execute(

From b5b32b7fdaafd03b4cc60d34903c5d17d6069a5f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:58:19 +0100
Subject: [PATCH 121/400] Use correct var name

---
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 6c3622ed5d..5b12e3bdc3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -64,7 +64,7 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
         private val directLoginTask: DirectLoginTask,
-        private val loginTokenAuthTask: QrLoginTokenTask
+        private val qrLoginTokenTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -412,7 +412,7 @@ internal class DefaultAuthenticationService @Inject constructor(
             initialDeviceName: String?,
             deviceId: String?,
     ): Session {
-        return loginTokenAuthTask.execute(
+        return qrLoginTokenTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,
                         loginToken = loginToken,

From 5843c3832b6e654a813dde273c75cf9e68497e63 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:58:19 +0100
Subject: [PATCH 122/400] Use correct var name

---
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 6c3622ed5d..5b12e3bdc3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -64,7 +64,7 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
         private val directLoginTask: DirectLoginTask,
-        private val loginTokenAuthTask: QrLoginTokenTask
+        private val qrLoginTokenTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -412,7 +412,7 @@ internal class DefaultAuthenticationService @Inject constructor(
             initialDeviceName: String?,
             deviceId: String?,
     ): Session {
-        return loginTokenAuthTask.execute(
+        return qrLoginTokenTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,
                         loginToken = loginToken,

From 8c86b3d297b38533b7f38da69e3407fe800a673e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:02:57 +0100
Subject: [PATCH 123/400] Add missing binding

---
 .../java/org/matrix/android/sdk/internal/auth/AuthModule.kt  | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
index 463692e574..b1f65194f1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
@@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule
 import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore
 import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore
 import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.database.RealmKeysUtils
 import org.matrix.android.sdk.internal.di.AuthDatabase
 import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter
@@ -94,4 +96,7 @@ internal abstract class AuthModule {
 
     @Binds
     abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService
+
+    @Binds
+    abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask
 }

From 579df742579f19fda49913d031759f26ea61c841 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:02:57 +0100
Subject: [PATCH 124/400] Add missing binding

---
 .../java/org/matrix/android/sdk/internal/auth/AuthModule.kt  | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
index 463692e574..b1f65194f1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
@@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule
 import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore
 import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore
 import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.database.RealmKeysUtils
 import org.matrix.android.sdk.internal.di.AuthDatabase
 import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter
@@ -94,4 +96,7 @@ internal abstract class AuthModule {
 
     @Binds
     abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService
+
+    @Binds
+    abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask
 }

From c6e7185f8b7c1cdb11f5acf0f11aa17ca761d8bf Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:08:50 +0100
Subject: [PATCH 125/400] Set default value for optional params

---
 .../matrix/android/sdk/internal/auth/data/TokenLoginParams.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 22cc185fa7..52045a1d7a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
         @Json(name = "token") val token: String,
-        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
-        @Json(name = "device_id") override val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null,
+        @Json(name = "device_id") override val deviceId: String? = null
 ) : LoginParams

From b5e81d27d65bb8dc5819250c29035e004c24121f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:08:50 +0100
Subject: [PATCH 126/400] Set default value for optional params

---
 .../matrix/android/sdk/internal/auth/data/TokenLoginParams.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 22cc185fa7..52045a1d7a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
         @Json(name = "token") val token: String,
-        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
-        @Json(name = "device_id") override val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null,
+        @Json(name = "device_id") override val deviceId: String? = null
 ) : LoginParams

From 81f21971993686ae4de245b16f70b15e855ff402 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:11:41 +0100
Subject: [PATCH 127/400] Another default value fix

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index c8065e4524..e490311b91 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -135,7 +135,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String?,
+            initialDeviceName: String? = null,
             deviceId: String? = null
     ): Session
 }

From 22b344c43a224a52d45be8087a7ead5678d6b38f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:11:41 +0100
Subject: [PATCH 128/400] Another default value fix

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index c8065e4524..e490311b91 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -135,7 +135,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String?,
+            initialDeviceName: String? = null,
             deviceId: String? = null
     ): Session
 }

From 738242c8378be68392d03567e5b4714a2591cd20 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:28:47 +0100
Subject: [PATCH 129/400] Map for soft logout

---
 .../im/vector/app/features/signout/soft/SoftLogoutController.kt  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
index b1a240e942..a1ed27df1d 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
@@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor(
             LoginType.SSO -> buildLoginSSOForm()
             LoginType.DIRECT,
             LoginType.CUSTOM,
+            LoginType.QR,
             LoginType.UNSUPPORTED -> buildLoginUnsupportedForm()
             LoginType.UNKNOWN -> Unit
         }

From 8dbb1b830ef8c508190b0367501a1ef441c717c6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:28:47 +0100
Subject: [PATCH 130/400] Map for soft logout

---
 .../im/vector/app/features/signout/soft/SoftLogoutController.kt  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
index b1a240e942..a1ed27df1d 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
@@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor(
             LoginType.SSO -> buildLoginSSOForm()
             LoginType.DIRECT,
             LoginType.CUSTOM,
+            LoginType.QR,
             LoginType.UNSUPPORTED -> buildLoginUnsupportedForm()
             LoginType.UNKNOWN -> Unit
         }

From fb2776dca41ac76e800b9101b62467741324cdc3 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Thu, 13 Oct 2022 20:33:52 +0300
Subject: [PATCH 131/400] Cherry pick previous commits.

---
 .../sdk/api/auth/AuthenticationService.kt     |  6 ++++
 .../homeserver/HomeServerCapabilities.kt      |  7 +++-
 .../auth/DefaultAuthenticationService.kt      | 16 +++++++++
 .../sdk/internal/auth/version/Versions.kt     |  5 +++
 .../database/RealmSessionStoreMigration.kt    |  4 ++-
 .../mapper/HomeServerCapabilitiesMapper.kt    |  3 +-
 .../database/migration/MigrateSessionTo038.kt | 34 +++++++++++++++++++
 .../model/HomeServerCapabilitiesEntity.kt     |  3 +-
 .../GetHomeServerCapabilitiesTask.kt          |  2 ++
 .../features/login/qr/QrCodeLoginActivity.kt  |  3 +-
 .../login/qr/QrCodeLoginShowQrCodeFragment.kt |  2 +-
 .../login/qr/QrCodeLoginStatusFragment.kt     |  2 +-
 .../onboarding/OnboardingViewModel.kt         | 20 +++++++++++
 .../onboarding/OnboardingViewState.kt         |  4 ++-
 .../ftueauth/FtueAuthCombinedLoginFragment.kt | 10 +++++-
 .../res/layout/fragment_settings_devices.xml  | 13 +++++--
 16 files changed, 121 insertions(+), 13 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo038.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 5ae70e1978..d040f9c67b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -124,4 +124,10 @@ interface AuthenticationService {
             initialDeviceName: String,
             deviceId: String? = null
     ): Session
+
+    /**
+     * @param homeServerConnectionConfig the information about the homeserver and other configuration
+     * Return true if qr code login is supported by the server, false otherwise.
+     */
+    suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
index b5d6d891e4..8c14ca892a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
@@ -59,7 +59,12 @@ data class HomeServerCapabilities(
         /**
          * True if the home server supports controlling the logout of all devices when changing password.
          */
-        val canControlLogoutDevices: Boolean = false
+        val canControlLogoutDevices: Boolean = false,
+
+        /**
+         * True if the home server supports login via qr code, false otherwise.
+         */
+        val canLoginWithQrCode: Boolean = false,
 ) {
 
     enum class RoomCapabilitySupport {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 446f931847..4f45f807df 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 import org.matrix.android.sdk.api.auth.login.LoginWizard
 import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
+import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.failure.MatrixIdFailure
 import org.matrix.android.sdk.api.session.Session
@@ -42,6 +43,7 @@ import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
 import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
+import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
 import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
 import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk
 import org.matrix.android.sdk.internal.di.Unauthenticated
@@ -404,6 +406,20 @@ internal class DefaultAuthenticationService @Inject constructor(
         )
     }
 
+    override suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean {
+        val authAPI = buildAuthAPI(homeServerConnectionConfig)
+        val versions = runCatching {
+            executeRequest(null) {
+                authAPI.versions()
+            }
+        }
+        return if (versions.isSuccess) {
+            versions.getOrNull()?.doesServerSupportQrCodeLogin().orFalse()
+        } else {
+            false
+        }
+    }
+
     private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
         val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
         return retrofit.create(AuthAPI::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt
index 915b25134b..5e133fab9c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt
@@ -53,6 +53,7 @@ private const val FEATURE_ID_ACCESS_TOKEN = "m.id_access_token"
 private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
 private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
 private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
+private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
 
 /**
  * Return true if the SDK supports this homeserver version.
@@ -78,6 +79,10 @@ internal fun Versions.doesServerSupportThreads(): Boolean {
     return unstableFeatures?.get(FEATURE_THREADS_MSC3440_STABLE) ?: false
 }
 
+internal fun Versions.doesServerSupportQrCodeLogin(): Boolean {
+    return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false
+}
+
 /**
  * Return true if the server support the lazy loading of room members.
  *
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index 2693ca474c..aef482ae2e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -54,6 +54,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
+import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038
 import org.matrix.android.sdk.internal.util.Normalizer
 import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
 import javax.inject.Inject
@@ -62,7 +63,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
         private val normalizer: Normalizer
 ) : MatrixRealmMigration(
         dbName = "Session",
-        schemaVersion = 37L,
+        schemaVersion = 38L,
 ) {
     /**
      * Forces all RealmSessionStoreMigration instances to be equal.
@@ -109,5 +110,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
         if (oldVersion < 35) MigrateSessionTo035(realm).perform()
         if (oldVersion < 36) MigrateSessionTo036(realm).perform()
         if (oldVersion < 37) MigrateSessionTo037(realm).perform()
+        if (oldVersion < 38) MigrateSessionTo038(realm).perform()
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
index 184a0108b9..63fa101c45 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
@@ -43,7 +43,8 @@ internal object HomeServerCapabilitiesMapper {
                 defaultIdentityServerUrl = entity.defaultIdentityServerUrl,
                 roomVersions = mapRoomVersion(entity.roomVersionsJson),
                 canUseThreading = entity.canUseThreading,
-                canControlLogoutDevices = entity.canControlLogoutDevices
+                canControlLogoutDevices = entity.canControlLogoutDevices,
+                canLoginWithQrCode = entity.canLoginWithQrCode,
         )
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo038.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo038.kt
new file mode 100644
index 0000000000..52a1ba1e4e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo038.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 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.database.migration
+
+import io.realm.DynamicRealm
+import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
+import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities
+import org.matrix.android.sdk.internal.util.database.RealmMigrator
+
+internal class MigrateSessionTo038(realm: DynamicRealm) : RealmMigrator(realm, 38) {
+
+    override fun doMigrate(realm: DynamicRealm) {
+        realm.schema.get("HomeServerCapabilitiesEntity")
+                ?.addField(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, Boolean::class.java)
+                ?.transform { obj ->
+                    obj.set(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, false)
+                }
+                ?.forceRefreshOfHomeServerCapabilities()
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
index 9d90973f8a..cfa02b2c74 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
@@ -30,7 +30,8 @@ internal open class HomeServerCapabilitiesEntity(
         var defaultIdentityServerUrl: String? = null,
         var lastUpdatedTimestamp: Long = 0L,
         var canUseThreading: Boolean = false,
-        var canControlLogoutDevices: Boolean = false
+        var canControlLogoutDevices: Boolean = false,
+        var canLoginWithQrCode: Boolean = false,
 ) : RealmObject() {
 
     companion object
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index add69dd8c7..c9a342777b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
+import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads
 import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
 import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
@@ -134,6 +135,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 }
                 homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
                         getVersionResult?.doesServerSupportThreads().orFalse()
+                homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult?.doesServerSupportQrCodeLogin().orFalse()
             }
 
             if (getMediaConfigResult != null) {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index e0323fdc2d..042f885231 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -24,7 +24,6 @@ import com.airbnb.mvrx.Mavericks
 import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
-import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
 import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
@@ -88,7 +87,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
     }
 
     private fun handleNavigateToStatusScreen() {
-        addFragmentToBackstack(
+        addFragment(
                 views.container,
                 QrCodeLoginStatusFragment::class.java,
                 tag = FRAGMENT_QR_CODE_STATUS_TAG
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
index 9741b79f7a..1c68ebb2a5 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
@@ -44,7 +44,7 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
 
     private fun initCancelButton() {
         views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks {
-            parentFragmentManager.popBackStack()
+            activity?.onBackPressed()
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index c1db1832ef..1c0841aa11 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -45,7 +45,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
 
     private fun initCancelButton() {
         views.qrCodeLoginStatusCancelButton.debouncedClicks {
-            parentFragmentManager.popBackStack()
+            activity?.onBackPressed()
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 4f8a77f25d..03cf2f43e6 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -117,6 +117,25 @@ class OnboardingViewModel @AssistedInject constructor(
         }
     }
 
+    private fun observeQrCodeLoginCapability() = viewModelScope.launch {
+        if (!vectorFeatures.isQrCodeLoginEnabled()) {
+            setState {
+                copy(
+                        canLoginWithQrCode = false
+                )
+            }
+        } else {
+            homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
+                val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
+                setState {
+                    copy(
+                            canLoginWithQrCode = canLoginWithQrCode
+                    )
+                }
+            }
+        }
+    }
+
     private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
     private val defaultHomeserverUrl = matrixOrgUrl
 
@@ -234,6 +253,7 @@ class OnboardingViewModel @AssistedInject constructor(
     private fun handleSplashAction(action: OnboardingAction.SplashAction) {
         setState { copy(onboardingFlow = action.onboardingFlow) }
         continueToPageAfterSplash(action.onboardingFlow)
+        observeQrCodeLoginCapability()
     }
 
     private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
index 99678ea5c1..de10852238 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
@@ -58,7 +58,9 @@ data class OnboardingViewState(
         val selectedAuthenticationState: SelectedAuthenticationState = SelectedAuthenticationState(),
 
         @PersistState
-        val personalizationState: PersonalizationState = PersonalizationState()
+        val personalizationState: PersonalizationState = PersonalizationState(),
+
+        val canLoginWithQrCode: Boolean = false,
 ) : MavericksState
 
 enum class OnboardingFlow {
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
index 0a7ce22853..4d32964b7d 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
@@ -74,7 +74,15 @@ class FtueAuthCombinedLoginFragment :
             viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content()))
         }
         views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) }
-        if (vectorFeatures.isQrCodeLoginEnabled()) {
+
+        viewModel.onEach(OnboardingViewState::canLoginWithQrCode) {
+            configureQrCodeLoginButtonVisibility(it)
+        }
+    }
+
+    private fun configureQrCodeLoginButtonVisibility(canLoginWithQrCode: Boolean) {
+        if (canLoginWithQrCode) {
+            views.loginWithQrCode.isVisible = true
             views.loginWithQrCode.debouncedClicks {
                 navigator
                         .openLoginWithQrCode(
diff --git a/vector/src/main/res/layout/fragment_settings_devices.xml b/vector/src/main/res/layout/fragment_settings_devices.xml
index 7167eebb8e..3b84e80114 100644
--- a/vector/src/main/res/layout/fragment_settings_devices.xml
+++ b/vector/src/main/res/layout/fragment_settings_devices.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <ScrollView 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="match_parent">
 
@@ -112,12 +113,14 @@
             android:id="@+id/deviceListHeaderSignInWithQrCode"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:visibility="gone"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/deviceListOtherSessions"
             app:sessionListHeaderShowLearnMore="false"
             app:sessionsListHeaderDescription="@string/device_manager_sessions_sign_in_with_qr_code_description"
-            app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title" />
+            app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title"
+            tools:visibility="visible" />
 
         <Button
             android:id="@+id/deviceListHeaderScanQrCodeButton"
@@ -126,9 +129,11 @@
             android:layout_marginHorizontal="16dp"
             android:layout_marginTop="12dp"
             android:text="@string/qr_code_login_scan_qr_code_button"
+            android:visibility="gone"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode" />
+            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode"
+            tools:visibility="visible" />
 
         <Button
             android:id="@+id/deviceListHeaderShowQrCodeButton"
@@ -139,9 +144,11 @@
             android:layout_marginTop="4dp"
             android:layout_marginBottom="12dp"
             android:text="@string/qr_code_login_show_qr_code_button"
+            android:visibility="gone"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton" />
+            app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton"
+            tools:visibility="visible" />
 
         <include
             android:id="@+id/waiting_view"

From 0111b932dea08e98362182165a23d717bea68592 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:08:45 +0100
Subject: [PATCH 132/400] Support for navigation to home screen

---
 .../im/vector/app/features/login/qr/QrCodeLoginActivity.kt | 7 +++++++
 .../vector/app/features/login/qr/QrCodeLoginViewEvents.kt  | 1 +
 2 files changed, 8 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index e0323fdc2d..3c7b0a4729 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -26,6 +26,7 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.app.features.home.HomeActivity
 import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
 
@@ -75,6 +76,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
             when (it) {
                 QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
                 QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen()
+                QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen()
             }
         }
     }
@@ -95,6 +97,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         )
     }
 
+    private fun handleNavigateToHomeScreen() {
+        val intent = HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true)
+        startActivity(intent)
+    }
+
     companion object {
 
         private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG"
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index dc258408e7..0f282fee38 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewEvents
 sealed class QrCodeLoginViewEvents : VectorViewEvents {
     object NavigateToStatusScreen : QrCodeLoginViewEvents()
     object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents()
+    object NavigateToHomeScreen : QrCodeLoginViewEvents()
 }

From 1ed082d3cb16943a15664dbb862126942f5539d3 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:15:52 +0100
Subject: [PATCH 133/400] QR login + E2EE set up

---
 .../sdk/internal/rendezvous/Rendezvous.kt     | 95 +++++++++----------
 .../features/login/qr/QrCodeLoginViewModel.kt | 73 ++++++--------
 2 files changed, 74 insertions(+), 94 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
index dd7b91582b..2f85a97c55 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
@@ -16,8 +16,11 @@
 
 package org.matrix.android.sdk.internal.rendezvous
 
+import android.net.Uri
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
@@ -56,9 +59,12 @@ internal data class Payload(
 
 private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
-data class Rendezvous(
+/**
+ * Implementation of MSC3906 to sign in + E2EE set up using a QR code.
+ */
+class Rendezvous(
         val channel: RendezvousChannel,
-        val theirIntent: RendezvousIntent
+        val theirIntent: RendezvousIntent,
 ) {
     companion object {
         fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
@@ -116,7 +122,7 @@ data class Rendezvous(
         return checksum
     }
 
-    suspend fun completeOnNewDevice(): Session? {
+    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
         Timber.tag(TAG).i("Waiting for login_token");
 
         val loginToken = receive()
@@ -143,59 +149,46 @@ data class Rendezvous(
 
         Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
 
-        // TODO: set view to be state logging in?
+        val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
+        return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
+    }
 
-        // use token to login
-//        const login = await sendLoginRequest(homeserver, undefined, "m.login.token", { token: login_token });
-//
-//        await setLoggedIn(login);
-//
-//        const { deviceId, userId } = login;
-//
-//        const client = MatrixClientPeg.get();
-//
+    suspend fun completeVerificationOnNewDevice(session: Session) {
+        val userId = session.myUserId
+        val crypto = session.cryptoService()
+        val deviceId = crypto.getMyDevice().deviceId
+        val deviceKey = crypto.getMyDevice().fingerprint()
+        send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
 
-        val newSession: Session? = null
+        // await confirmation of verification
 
-        newSession ?.let {
-            session ->
-            val userId = session.myUserId
-            val crypto = session.cryptoService()
-            val deviceId = crypto.getMyDevice().deviceId
-            val deviceKey = crypto.getMyDevice().fingerprint()
-            send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
-
-            // await confirmation of verification
-
-            val verificationResponse = receive()
-            val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
-            val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
-            if (verifyingDeviceFromServer?.fingerprint() == verificationResponse.verifying_device_key) {
-                // set other device as verified
-                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
-                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
-
-                verificationResponse.master_key ?.let {
-                    // set master key as trusted
-                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
-
-                }
-
-                // request secrets from the verifying device
-                Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
-
-                session.sharedSecretStorageService() .let {
-                    it.requestSecret(verifyingDeviceId, MASTER_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, SELF_SIGNING_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, USER_SIGNING_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, KEYBACKUP_SECRET_SSSS_NAME)
-                }
-            } else {
-                Timber.tag(TAG).i("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
-            }
+        val verificationResponse = receive()
+        val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
+        val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
+        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
+            return;
         }
 
-        return newSession
+        // set other device as verified
+        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
+        crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+
+        // TODO: what do we do with the master key?
+//        verificationResponse.master_key ?.let {
+//            // set master key as trusted
+//            crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
+//        }
+
+        // request secrets from the verifying device
+        Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+
+        session.sharedSecretStorageService() .let {
+            it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+        }
     }
 
     private suspend fun receive(): Payload? {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index f97a59b432..c729152e44 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -16,24 +16,31 @@
 
 package im.vector.app.features.login.qr
 
+import android.content.Context
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.features.home.HomeActivity
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.internal.rendezvous.Rendezvous
 import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
 import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
-        @Assisted private val initialState: QrCodeLoginViewState
+        @Assisted private val initialState: QrCodeLoginViewState,
+        private val applicationContext: Context,
+        private val authenticationService: AuthenticationService,
+        private val activeSessionHolder: ActiveSessionHolder,
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
-
     val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
     @AssistedFactory
@@ -81,44 +88,31 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
 
         viewModelScope.launch(Dispatchers.IO) {
-            val confirmationCode = rendezvous.startAfterScanningCode()
-            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
-            confirmationCode ?.let {
-                onConnectionEstablished(it)
-                rendezvous.completeOnNewDevice()
+            try {
+                val confirmationCode = rendezvous.startAfterScanningCode()
+                Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
+                confirmationCode?.let {
+                    onConnectionEstablished(it)
+                    val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
+                    onSigningIn()
+                    session?.let {
+                        activeSessionHolder.setActiveSession(session)
+                        authenticationService.reset()
+
+                        session.configureAndStart(applicationContext)
+
+                        rendezvous.completeVerificationOnNewDevice(session)
+
+                        _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
+                    }
+                }
+            } catch (failure: Throwable) {
+                Timber.tag(TAG).e(failure, "Error occurred during sign in")
+                onFailed(RendezvousFailureReason.Unknown)
             }
         }
-    //        if (isValidQrCode(action.qrCode)) {
-//            setState {
-//                copy(
-//                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
-//                )
-//            }
-//            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
-//        }
-//
-
-//        // TODO. UI test purpose. Fixme remove!
-//        viewModelScope.launch {
-//            delay(3000)
-//            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
-//            delay(3000)
-//            onConnectionEstablished("1234-ABCD-5678-EFGH")
-//            delay(3000)
-//            onSigningIn()
-//            delay(3000)
-//            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
-//        }
-//        // TODO. UI test purpose. Fixme remove!
-//        viewModelScope.launch {
-//            delay(3000)
-//            onConnectionEstablished("1234-ABCD-5678-EFGH")
-//            delay(3000)
-//            onSigningIn()
-//        }
     }
 
-
     private fun onFailed(reason: RendezvousFailureReason) {
         setState {
             copy(
@@ -144,13 +138,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         }
     }
 
-    /**
-     * TODO. UI test purpose. Fixme accordingly.
-     */
-    private fun isValidQrCode(qrCode: String): Boolean {
-        return qrCode.startsWith("http")
-    }
-
     /**
      * TODO. UI test purpose. Fixme accordingly.
      */

From 560fda51d163695d1c4878751d9f2f0ab92014b7 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:26:22 +0100
Subject: [PATCH 134/400] Reduce logging

---
 .../rendezvous/channels/ECDHRendezvousChannel.kt | 16 ++++++++--------
 .../transports/SimpleHttpRendezvousTransport.kt  |  8 ++++----
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
index 33837bc425..cced29aab4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -94,7 +94,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         val isInitiator = theirPublicKey == null
 
         if (isInitiator) {
-            Timber.tag(TAG).i("Waiting for other device to send their public key")
+//            Timber.tag(TAG).i("Waiting for other device to send their public key")
             val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
 
             if (res.key == null) {
@@ -106,7 +106,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
         } else {
             // send our public key unencrypted
-            Timber.tag(TAG).i("Sending public key")
+//            Timber.tag(TAG).i("Sending public key")
             send(ECDHPayload(
                     algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
                     key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
@@ -121,10 +121,10 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
         aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
 
-        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
-        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
-        Timber.tag(TAG).i("AES info: $aesInfo")
-        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("AES info: $aesInfo")
+//        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
 
         val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
         return getDecimalCodeRepresentation(rawChecksum)
@@ -180,7 +180,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {
-        Timber.tag(TAG).i("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
+//        Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
         val iv = ByteArray(16)
         SecureRandom().nextBytes(iv)
 
@@ -212,7 +212,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
         val plainTextBytes = plainText.toByteArray()
 
-        Timber.tag(TAG).i("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
+//        Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
         return plainTextBytes
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 3e5e1121ea..cc4346d55e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -67,7 +67,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         // TODO: properly determine endpoint
         val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
 
-        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
+//        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
 
         val httpClient = okhttp3.OkHttpClient.Builder().build()
 
@@ -123,8 +123,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             val response = httpClient.newCall(request.build()).execute()
 
             try {
-
-                Timber.tag(TAG).i("Received polling response: ${response.code} from $uri")
+//                Timber.tag(TAG).d("Received polling response: ${response.code} from $uri")
 
                 if (response.code == 404) {
                     cancel(RendezvousFailureReason.Unknown)
@@ -142,7 +141,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                         etag = it
                     }
                     val data = response.body?.bytes()
-                    Timber.tag(TAG).i("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
+//                    Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
                     return data
                 }
 
@@ -158,6 +157,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
 
     override suspend fun cancel(reason: RendezvousFailureReason) {
         var mappedReason = reason
+        Timber.tag(TAG).i("$expiresAt")
         if (mappedReason == RendezvousFailureReason.Unknown &&
                 expiresAt != null && Date() > expiresAt) {
             mappedReason = RendezvousFailureReason.Expired

From 90fa5d53454fa3b4b6584aec7f91aa571b03b539 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:48:21 +0100
Subject: [PATCH 135/400] Revert "Only do completeOnNewDevice if we received a
 confirmation code"

This reverts commit 6e58f2fa51f0cc3aa474f9c5adf7b497229e52dd.
---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index f97a59b432..d0c34b83af 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
-                rendezvous.completeOnNewDevice()
             }
+            rendezvous.completeOnNewDevice()
         }
     //        if (isValidQrCode(action.qrCode)) {
 //            setState {

From e305478ddabf39c6e2a272485a1b154b3ad12263 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:48:25 +0100
Subject: [PATCH 136/400] Revert "Partial implementation of QR login logic"

This reverts commit 4b14ee4695b452cd24353f05809f64d0b0ee4b6d.
---
 .../src/main/res/values/strings.xml           |  1 -
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  4 +-
 .../features/login/qr/QrCodeLoginErrorType.kt | 23 ++++++
 .../login/qr/QrCodeLoginStatusFragment.kt     | 12 ++-
 .../features/login/qr/QrCodeLoginViewModel.kt | 80 +++++--------------
 5 files changed, 50 insertions(+), 70 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 108fe7db7c..822e9d3865 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3368,7 +3368,6 @@
     <string name="qr_code_login_header_failed_device_is_not_supported_description">Linking with this device is not supported.</string>
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
-    <string name="qr_code_login_header_failed_other_description">The request failed.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index 4de191f863..330562b874 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -16,11 +16,9 @@
 
 package im.vector.app.features.login.qr
 
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-
 sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
     data class Connected(val securityCode: String, val canConfirmSecurityCode: Boolean) : QrCodeLoginConnectionStatus()
     object SigningIn : QrCodeLoginConnectionStatus()
-    data class Failed(val errorType: RendezvousFailureReason, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
+    data class Failed(val errorType: QrCodeLoginErrorType, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
new file mode 100644
index 0000000000..9a6cc13de0
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.login.qr
+
+enum class QrCodeLoginErrorType {
+    DEVICE_IS_NOT_SUPPORTED,
+    TIMEOUT,
+    REQUEST_WAS_DENIED,
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index fb372cbb2f..1c0841aa11 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -27,7 +27,6 @@ import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
 import im.vector.app.features.themes.ThemeUtils
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
 
 @AndroidEntryPoint
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
@@ -78,12 +77,11 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         )
     }
 
-    private fun getErrorCode(reason: RendezvousFailureReason): String {
-        return when (reason) {
-            RendezvousFailureReason.UnsupportedAlgorithm, RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
-            RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
-            RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
-            else -> getString(R.string.qr_code_login_header_failed_other_description)
+    private fun getErrorCode(errorType: QrCodeLoginErrorType): String {
+        return when (errorType) {
+            QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            QrCodeLoginErrorType.TIMEOUT -> getString(R.string.qr_code_login_header_failed_timeout_description)
+            QrCodeLoginErrorType.REQUEST_WAS_DENIED -> getString(R.string.qr_code_login_header_failed_denied_description)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index d0c34b83af..da3348653c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -23,19 +23,13 @@ import dagger.assisted.AssistedInject
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.platform.VectorViewModel
-import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.internal.rendezvous.Rendezvous
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
-        @Assisted private val initialState: QrCodeLoginViewState
+        @Assisted private val initialState: QrCodeLoginViewState,
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
 
-    val TAG: String = QrCodeLoginViewModel::class.java.simpleName
-
     @AssistedFactory
     interface Factory : MavericksAssistedViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> {
         override fun create(initialState: QrCodeLoginViewState): QrCodeLoginViewModel
@@ -65,64 +59,32 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
-        Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
-
-        val rendezvous = Rendezvous.buildChannelFromCode(action.qrCode) { reason ->
-            Timber.tag(TAG).d("Rendezvous cancelled: $reason")
-            onFailed(reason)
-        }
-
-        setState {
-            copy(
-                    connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
-            )
-        }
-
-        _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
-
-        viewModelScope.launch(Dispatchers.IO) {
-            val confirmationCode = rendezvous.startAfterScanningCode()
-            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
-            confirmationCode ?.let {
-                onConnectionEstablished(it)
+        if (isValidQrCode(action.qrCode)) {
+            setState {
+                copy(
+                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
+                )
             }
-            rendezvous.completeOnNewDevice()
+            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
         }
-    //        if (isValidQrCode(action.qrCode)) {
-//            setState {
-//                copy(
-//                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
-//                )
-//            }
-//            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
-//        }
-//
 
-//        // TODO. UI test purpose. Fixme remove!
-//        viewModelScope.launch {
-//            delay(3000)
-//            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
-//            delay(3000)
-//            onConnectionEstablished("1234-ABCD-5678-EFGH")
-//            delay(3000)
-//            onSigningIn()
-//            delay(3000)
-//            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
-//        }
-//        // TODO. UI test purpose. Fixme remove!
-//        viewModelScope.launch {
-//            delay(3000)
-//            onConnectionEstablished("1234-ABCD-5678-EFGH")
-//            delay(3000)
-//            onSigningIn()
-//        }
+        // TODO. UI test purpose. Fixme remove!
+        viewModelScope.launch {
+            delay(3000)
+            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
+            delay(3000)
+            onConnectionEstablished("1234-ABCD-5678-EFGH")
+            delay(3000)
+            onSigningIn()
+            delay(3000)
+            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
+        }
     }
 
-
-    private fun onFailed(reason: RendezvousFailureReason) {
+    private fun onFailed(errorType: QrCodeLoginErrorType, canTryAgain: Boolean) {
         setState {
             copy(
-                    connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry)
+                    connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain)
             )
         }
     }

From 489dfd73546a78246b1f18faf97e7a36bb4e0241 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:48:27 +0100
Subject: [PATCH 137/400] Revert "Implementations of MSC3886 and MSC3903"

This reverts commit 1235db7895fc92c05d220abaf30433d56dd7b2f8.
---
 .../android/sdk/api/logger/LoggerTag.kt       |   1 -
 .../sdk/internal/rendezvous/Rendezvous.kt     | 217 -----------------
 .../internal/rendezvous/RendezvousChannel.kt  |  45 ----
 .../rendezvous/RendezvousFailureReason.kt     |  31 ---
 .../rendezvous/RendezvousTransport.kt         |  29 ---
 .../channels/ECDHRendezvousChannel.kt         | 218 ------------------
 .../rendezvous/model/ECDHRendezvous.kt        |  34 ---
 .../rendezvous/model/EmbeddedRendezvous.kt    |  26 ---
 .../rendezvous/model/RendezvousError.kt       |  22 --
 .../rendezvous/model/RendezvousIntent.kt      |  24 --
 .../model/RendezvousTransportDetails.kt       |  25 --
 .../model/RendezvousTransportType.kt          |  23 --
 .../model/SecureRendezvousChannelAlgorithm.kt |  23 --
 .../SimpleHttpRendezvousTransport.kt          | 185 ---------------
 14 files changed, 903 deletions(-)
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
index 22af8cebbd..ae65963f37 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
@@ -27,7 +27,6 @@ open class LoggerTag(name: String, parentTag: LoggerTag? = null) {
     object SYNC : LoggerTag("SYNC")
     object VOIP : LoggerTag("VOIP")
     object CRYPTO : LoggerTag("CRYPTO")
-    object RENDEZVOUS : LoggerTag("RZ")
 
     val value: String = if (parentTag == null) {
         name
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
deleted file mode 100644
index dd7b91582b..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.api.logger.LoggerTag
-import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
-import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
-import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
-import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
-import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
-import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport
-import timber.log.Timber
-
-internal enum class PayloadType(val value: String) {
-    @Json(name = "m.login.start") Start("m.login.start"),
-    @Json(name = "m.login.finish") Finish("m.login.finish"),
-    @Json(name = "m.login.progress") Progress("m.login.progress")
-}
-
-@JsonClass(generateAdapter = true)
-internal data class Payload(
-        @Json val type: PayloadType,
-        @Json val intent: RendezvousIntent? = null,
-        @Json val outcome: String? = null,
-        @Json val protocols: List<String>? = null,
-        @Json val protocol: String? = null,
-        @Json val homeserver: String? = null,
-        @Json val login_token: String? = null,
-        @Json val device_id: String? = null,
-        @Json val device_key: String? = null,
-        @Json val verifying_device_id: String? = null,
-        @Json val verifying_device_key: String? = null,
-        @Json val master_key: String? = null
-)
-
-private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-data class Rendezvous(
-        val channel: RendezvousChannel,
-        val theirIntent: RendezvousIntent
-) {
-    companion object {
-        fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
-            val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
-
-            val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri)
-
-            return Rendezvous(
-                    ECDHRendezvousChannel(transport, parsed.rendezvous.key),
-                    parsed.intent
-            )
-        }
-    }
-
-    private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java)
-    // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
-    val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
-
-    private suspend fun areIntentsIncompatible(): Boolean {
-        val incompatible = theirIntent == ourIntent
-
-        Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
-
-        if (incompatible) {
-            send(Payload(PayloadType.Finish, intent = ourIntent))
-            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn
-            channel.cancel(reason)
-        }
-
-        return incompatible
-    }
-
-    suspend fun startAfterScanningCode(): String? {
-        val checksum = channel.connect();
-
-        Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
-
-        if (areIntentsIncompatible()) {
-            return null
-        }
-
-        // get protocols
-        Timber.tag(TAG).i("Waiting for protocols");
-        val protocolsResponse = receive()
-
-        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
-            send(Payload(PayloadType.Finish, outcome = "unsupported"))
-            Timber.tag(TAG).i("No supported protocol")
-            cancel(RendezvousFailureReason.Unknown)
-            return null
-        }
-
-        send(Payload(PayloadType.Progress, protocol = "login_token"))
-
-        return checksum
-    }
-
-    suspend fun completeOnNewDevice(): Session? {
-        Timber.tag(TAG).i("Waiting for login_token");
-
-        val loginToken = receive()
-
-        if (loginToken?.type == PayloadType.Finish) {
-            when (loginToken.outcome) {
-                "declined" -> {
-                    Timber.tag(TAG).i("Login declined by other device")
-                    channel.cancel(RendezvousFailureReason.UserDeclined)
-                    return null
-                }
-                "unsupported" -> {
-                    Timber.tag(TAG).i("Not supported")
-                    channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
-                    return null
-                }
-            }
-            channel.cancel(RendezvousFailureReason.Unknown)
-            return null
-        }
-
-        val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
-        val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
-
-        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
-
-        // TODO: set view to be state logging in?
-
-        // use token to login
-//        const login = await sendLoginRequest(homeserver, undefined, "m.login.token", { token: login_token });
-//
-//        await setLoggedIn(login);
-//
-//        const { deviceId, userId } = login;
-//
-//        const client = MatrixClientPeg.get();
-//
-
-        val newSession: Session? = null
-
-        newSession ?.let {
-            session ->
-            val userId = session.myUserId
-            val crypto = session.cryptoService()
-            val deviceId = crypto.getMyDevice().deviceId
-            val deviceKey = crypto.getMyDevice().fingerprint()
-            send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
-
-            // await confirmation of verification
-
-            val verificationResponse = receive()
-            val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
-            val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
-            if (verifyingDeviceFromServer?.fingerprint() == verificationResponse.verifying_device_key) {
-                // set other device as verified
-                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
-                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
-
-                verificationResponse.master_key ?.let {
-                    // set master key as trusted
-                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
-
-                }
-
-                // request secrets from the verifying device
-                Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
-
-                session.sharedSecretStorageService() .let {
-                    it.requestSecret(verifyingDeviceId, MASTER_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, SELF_SIGNING_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, USER_SIGNING_KEY_SSSS_NAME)
-                    it.requestSecret(verifyingDeviceId, KEYBACKUP_SECRET_SSSS_NAME)
-                }
-            } else {
-                Timber.tag(TAG).i("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
-            }
-        }
-
-        return newSession
-    }
-
-    private suspend fun receive(): Payload? {
-        val data = channel.receive()?: return null
-        return adapter.fromJson(data.toString(Charsets.UTF_8))
-    }
-
-    private suspend fun send(payload: Payload) {
-        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8));
-    }
-
-    suspend fun cancel(reason: RendezvousFailureReason) {
-        channel.cancel(reason)
-    }
-
-    suspend fun close() {
-        channel.close()
-    }
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
deleted file mode 100644
index 43552f46be..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous
-
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
-
-interface RendezvousChannel {
-    var transport: RendezvousTransport;
-    /**
-     * @returns the checksum/confirmation digits to be shown to the user
-     */
-    suspend fun connect(): String
-    /**
-     * Send a payload via the channel.
-     * @param data payload to send
-     */
-    suspend fun send(data: ByteArray)
-    /**
-     * Receive a payload from the channel.
-     * @returns the received payload
-     */
-    suspend fun receive(): ByteArray?
-    /**
-     * @returns a representation of the channel that can be encoded in a QR or similar
-     */
-    suspend fun close()
-    // TODO: this should be transport independent in the future
-    suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
-    suspend fun cancel(reason: RendezvousFailureReason)
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
deleted file mode 100644
index 0e2ea8c758..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous
-
-enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
-    UserDeclined("user_declined"),
-    OtherDeviceNotSignedIn("other_device_not_signed_in"),
-    OtherDeviceAlreadySignedIn("other_device_already_signed_in"),
-    Unknown("unknown"),
-    Expired("expired"),
-    UserCancelled("user_cancelled"),
-    InvalidCode("invalid_code"),
-    UnsupportedAlgorithm("unsupported_algorithm", false),
-    DataMismatch("data_mismatch"),
-    UnsupportedTransport("unsupported_transport", false),
-    HomeserverLacksSupport("homeserver_lacks_support", false)
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
deleted file mode 100644
index 753b0bc6fa..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous
-
-import okhttp3.MediaType
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
-
-interface RendezvousTransport {
-    var ready: Boolean;
-    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?;
-    suspend fun details(): RendezvousTransportDetails;
-    suspend fun send(contentType: MediaType, data: ByteArray);
-    suspend fun receive(): ByteArray?;
-    suspend fun cancel(reason: RendezvousFailureReason);
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
deleted file mode 100644
index 33837bc425..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.channels
-
-import android.util.Base64
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-import okhttp3.MediaType.Companion.toMediaType
-import org.matrix.android.sdk.api.logger.LoggerTag
-import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.extensions.toUnsignedInt
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel
-import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
-import org.matrix.olm.OlmSAS
-import timber.log.Timber
-import java.security.SecureRandom
-import java.util.LinkedList
-import javax.crypto.Cipher
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
-
-@JsonClass(generateAdapter = true)
-data class ECDHPayload(
-        @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
-        @Json val key: String? = null,
-        @Json val ciphertext: String? = null,
-        @Json val iv: String? = null
-)
-
-private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-    val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
-    val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
-    val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
-    val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
-    val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
-    // (B0 << 5 | B1 >> 3) + 1000
-    val first = (b0.shl(5) or b1.shr(3)) + 1000
-    // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
-    val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
-    // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
-    val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-    return "$first-$second-$third"
-}
-
-const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
-const val KEY_SPEC = "AES"
-
-/**
- *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
- *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
- */
-class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel {
-    private var olmSAS: OlmSAS?
-    private val ourPublicKey: ByteArray
-    private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
-    private var theirPublicKey: ByteArray? = null
-    private var aesKey: ByteArray? = null
-
-    init {
-        theirPublicKeyBase64 ?.let {
-            theirPublicKey = Base64.decode(it, Base64.NO_WRAP)
-        }
-        olmSAS = OlmSAS()
-        ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP)
-    }
-
-    override suspend fun connect(): String {
-        if (olmSAS == null) {
-            throw RuntimeException("Channel closed")
-        }
-        val isInitiator = theirPublicKey == null
-
-        if (isInitiator) {
-            Timber.tag(TAG).i("Waiting for other device to send their public key")
-            val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
-
-            if (res.key == null) {
-                throw RendezvousError(
-                        "Unsupported algorithm: ${res.algorithm}",
-                        RendezvousFailureReason.UnsupportedAlgorithm,
-                )
-            }
-            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
-        } else {
-            // send our public key unencrypted
-            Timber.tag(TAG).i("Sending public key")
-            send(ECDHPayload(
-                    algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
-                    key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-            ))
-        }
-
-        olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
-
-        val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
-        val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
-        val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
-
-        aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
-
-        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
-        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
-        Timber.tag(TAG).i("AES info: $aesInfo")
-        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
-
-        val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
-        return getDecimalCodeRepresentation(rawChecksum)
-    }
-
-    private suspend fun send(payload: ECDHPayload) {
-        transport.send("application/json".toMediaType(), ecdhAdapter.toJson(payload).toByteArray(Charsets.UTF_8))
-    }
-
-    override suspend fun send(data: ByteArray) {
-        if (aesKey == null) {
-            throw RuntimeException("Shared secret not established")
-        }
-        send(encrypt(data))
-    }
-
-    private suspend fun receiveAsPayload(): ECDHPayload? {
-        transport.receive()?.toString(Charsets.UTF_8) ?.let {
-            return ecdhAdapter.fromJson(it)
-        } ?: return null
-    }
-
-    override suspend fun receive(): ByteArray? {
-        if (aesKey == null) {
-            throw RuntimeException("Shared secret not established")
-        }
-        val payload = receiveAsPayload() ?: return null
-        return decrypt(payload)
-    }
-
-    override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode {
-        return ECDHRendezvousCode(
-                intent,
-                rendezvous = ECDHRendezvous(
-                        transport.details() as SimpleHttpRendezvousTransportDetails,
-                        SecureRendezvousChannelAlgorithm.ECDH_V1,
-                        key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-                )
-        )
-    }
-
-    override suspend fun cancel(reason: RendezvousFailureReason) {
-        try {
-            transport.cancel(reason)
-        } finally {
-            close()
-        }
-    }
-
-    override suspend fun close() {
-        olmSAS?.releaseSas()
-        olmSAS = null
-    }
-
-    private fun encrypt(plainText: ByteArray): ECDHPayload {
-        Timber.tag(TAG).i("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
-        val iv = ByteArray(16)
-        SecureRandom().nextBytes(iv)
-
-        val cipherText = LinkedList<Byte>()
-
-        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
-        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
-        val ivParameterSpec = IvParameterSpec(iv)
-        encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
-        cipherText.addAll(encryptCipher.update(plainText).toList())
-        cipherText.addAll(encryptCipher.doFinal().toList())
-
-        return ECDHPayload(
-                ciphertext = Base64.encodeToString(cipherText.toByteArray(), Base64.NO_WRAP),
-                iv = Base64.encodeToString(iv, Base64.NO_WRAP)
-        )
-    }
-
-    private fun decrypt(payload: ECDHPayload): ByteArray {
-        val iv = Base64.decode(payload.iv, Base64.NO_WRAP)
-        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
-        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
-        val ivParameterSpec = IvParameterSpec(iv)
-        encryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
-
-        val plainText = LinkedList<Byte>()
-        plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList())
-        plainText.addAll(encryptCipher.doFinal().toList())
-
-        val plainTextBytes = plainText.toByteArray()
-
-        Timber.tag(TAG).i("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
-        return plainTextBytes
-    }
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
deleted file mode 100644
index e296dce09d..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.model
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
-
-@JsonClass(generateAdapter = true)
-data class ECDHRendezvous(
-        @Json val transport: SimpleHttpRendezvousTransportDetails,
-        @Json val algorithm: SecureRendezvousChannelAlgorithm,
-        @Json val key: String
-)
-
-@JsonClass(generateAdapter = true)
-data class ECDHRendezvousCode(
-        @Json val intent: RendezvousIntent,
-        @Json val rendezvous: ECDHRendezvous
-)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
deleted file mode 100644
index d490de0133..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.model
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-
-@JsonClass(generateAdapter = true)
-open class EmbeddedRendezvous(
-    @Json(name = "transport") val transport: RendezvousTransportDetails,
-    @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
-)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
deleted file mode 100644
index ead273e8ce..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.model
-
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-
-class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
deleted file mode 100644
index 6285c1e57a..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.model
-
-import com.squareup.moshi.Json
-
-enum class RendezvousIntent {
-    @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
-    @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
deleted file mode 100644
index 1b1826194f..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.model
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-
-@JsonClass(generateAdapter = true)
-open class RendezvousTransportDetails(
-    @Json val type: RendezvousTransportType
-)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
deleted file mode 100644
index c3b6ba7ac8..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.model
-
-import com.squareup.moshi.Json
-
-enum class RendezvousTransportType(val value: String) {
-    @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
deleted file mode 100644
index ddc0ae20e7..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.model
-
-import com.squareup.moshi.Json
-
-enum class SecureRendezvousChannelAlgorithm(val value: String) {
-    @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
deleted file mode 100644
index 3e5e1121ea..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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 org.matrix.android.sdk.internal.rendezvous.transports
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-import kotlinx.coroutines.delay
-import okhttp3.MediaType
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-import org.matrix.android.sdk.api.logger.LoggerTag
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType
-import timber.log.Timber
-import java.text.SimpleDateFormat
-import java.util.Date
-
-private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-@JsonClass(generateAdapter = true)
-data class SimpleHttpRendezvousTransportDetails(
-        @Json val uri: String
-): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
-
-/**
- * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
- */
-class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
-    override var ready = false
-    private var cancelled = false
-    private var uri: String?
-    private var etag: String? = null
-    private var expiresAt: Date? = null
-
-    init {
-        uri = rendezvousUri
-    }
-
-    override suspend fun details(): RendezvousTransportDetails {
-        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
-
-        return SimpleHttpRendezvousTransportDetails(uri)
-    }
-
-    override suspend fun send(contentType: MediaType, data: ByteArray) {
-        if (cancelled) {
-            return
-        }
-
-        val method = if (uri != null) "PUT" else "POST"
-        // TODO: properly determine endpoint
-        val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
-
-        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
-
-        val httpClient = okhttp3.OkHttpClient.Builder().build()
-
-        val request = Request.Builder()
-                .url(uri)
-                .method(method, data.toRequestBody())
-                .header("content-type", contentType.toString())
-
-        etag ?.let {
-            request.header("if-match", it)
-        }
-
-        val response = httpClient.newCall(request.build()).execute()
-
-        if (response.code == 404) {
-            cancel(RendezvousFailureReason.Unknown)
-        }
-        etag = response.header("etag")
-
-        Timber.tag(TAG).i("Sent data to $uri new etag $etag")
-
-        if (method == "POST") {
-            val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
-
-            response.header("expires") ?.let {
-                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
-                expiresAt = format.parse(it)
-            }
-
-            // resolve location header which could be relative or absolute
-            this.uri = response.request.url.toUri().resolve(location).toString()
-            ready = true
-        }
-    }
-
-    override suspend fun receive(): ByteArray? {
-        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
-        var done = false
-        val httpClient = okhttp3.OkHttpClient.Builder().build()
-        while (!done) {
-            if (cancelled) {
-                return null
-            }
-            Timber.tag(TAG).i("Polling: $uri after etag $etag")
-            val request = Request.Builder()
-                    .url(uri)
-                    .get()
-
-            etag ?.let {
-                request.header("if-none-match", it)
-            }
-
-            val response = httpClient.newCall(request.build()).execute()
-
-            try {
-
-                Timber.tag(TAG).i("Received polling response: ${response.code} from $uri")
-
-                if (response.code == 404) {
-                    cancel(RendezvousFailureReason.Unknown)
-                    return null
-                }
-
-                // rely on server expiring the channel rather than checking ourselves
-
-                if (response.header("content-type") != "application/json") {
-                    response.header("etag")?.let {
-                        etag = it
-                    }
-                } else if (response.code == 200) {
-                    response.header("etag")?.let {
-                        etag = it
-                    }
-                    val data = response.body?.bytes()
-                    Timber.tag(TAG).i("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
-                    return data
-                }
-
-                done = false
-                delay(1000)
-            } finally {
-                response.close()
-            }
-        }
-
-        return null
-    }
-
-    override suspend fun cancel(reason: RendezvousFailureReason) {
-        var mappedReason = reason
-        if (mappedReason == RendezvousFailureReason.Unknown &&
-                expiresAt != null && Date() > expiresAt) {
-            mappedReason = RendezvousFailureReason.Expired
-        }
-
-        cancelled = true
-        ready = false
-        onCancelled ?.let { it(mappedReason) }
-
-        if (mappedReason == RendezvousFailureReason.UserDeclined) {
-            uri ?.let {
-                try {
-                    val httpClient = okhttp3.OkHttpClient.Builder().build()
-                    val request = Request.Builder()
-                            .url(it)
-                            .delete()
-                            .build()
-                    httpClient.newCall(request).execute()
-                } catch (e: Exception) {
-                    Timber.tag(TAG).w(e, "Failed to delete channel")
-                }
-            }
-        }
-    }
-}

From 9429a4f22aabfbff23ffc223c3b090626be553dc Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:48:33 +0100
Subject: [PATCH 138/400] Revert "Retry scanning if not a QR code"

This reverts commit 87956e943897333f3f7e9be9a6e59d9a7f0c4547.
---
 .../app/features/login/qr/QrCodeLoginInstructionsFragment.kt    | 2 --
 1 file changed, 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index 17b4ff2409..ae3ba9574b 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -83,7 +83,6 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
         if (activityResult.resultCode == Activity.RESULT_OK) {
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
-            Timber.d("Scanned QR code: $scannedQrCode $wasQrCode")
 
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
@@ -99,6 +98,5 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     private fun onQrCodeScannerFailed() {
         Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
-        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 }

From 88238c0f04648a7468c2ee33d86068fa79e4fe5f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:11:15 +0100
Subject: [PATCH 139/400] Support for login by m.login.token during QR code
 sign in

---
 .../sdk/api/auth/AuthenticationService.kt     | 13 +++
 .../matrix/android/sdk/api/auth/LoginType.kt  |  4 +-
 .../auth/DefaultAuthenticationService.kt      | 15 +++-
 .../sdk/internal/auth/data/LoginParams.kt     |  2 +
 .../internal/auth/data/PasswordLoginParams.kt |  4 +-
 .../internal/auth/data/TokenLoginParams.kt    |  4 +-
 .../internal/auth/login/QrLoginTokenTask.kt   | 88 +++++++++++++++++++
 7 files changed, 125 insertions(+), 5 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index d040f9c67b..6bb47dda5b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -130,4 +130,17 @@ interface AuthenticationService {
      * Return true if qr code login is supported by the server, false otherwise.
      */
     suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
+     * Authenticate using m.login.token method during sign in with QR code.
+     * @param homeServerConnectionConfig the information about the homeserver and other configuration
+     * @param loginToken the m.login.token
+     * @param initialDeviceName the initial device name
+     * @param deviceId the device id, optional. If not provided or null, the server will generate one.
+     */
+
+    suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String? = null
+    ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
index 627a825679..991b7b654d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt
@@ -22,7 +22,8 @@ enum class LoginType {
     UNSUPPORTED,
     CUSTOM,
     DIRECT,
-    UNKNOWN;
+    UNKNOWN,
+    QR;
 
     companion object {
 
@@ -32,6 +33,7 @@ enum class LoginType {
             UNSUPPORTED.name -> UNSUPPORTED
             CUSTOM.name -> CUSTOM
             DIRECT.name -> DIRECT
+            QR.name -> QR
             else -> UNKNOWN
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 4f45f807df..5b6f0686c4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -40,6 +40,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig
 import org.matrix.android.sdk.internal.auth.db.PendingSessionData
 import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
@@ -64,7 +65,8 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val sessionCreator: SessionCreator,
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
-        private val directLoginTask: DirectLoginTask
+        private val directLoginTask: DirectLoginTask,
+        private val loginTokenAuthTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -420,6 +422,17 @@ internal class DefaultAuthenticationService @Inject constructor(
         }
     }
 
+    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+        return loginTokenAuthTask.execute(
+                QrLoginTokenTask.Params(
+                        homeServerConnectionConfig = homeServerConnectionConfig,
+                        loginToken = loginToken,
+                        deviceName = initialDeviceName,
+                        deviceId = deviceId
+                )
+        )
+    }
+
     private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
         val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
         return retrofit.create(AuthAPI::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
index ea8578ed8c..8646752083 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt
@@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data
 
 internal interface LoginParams {
     val type: String
+    val deviceDisplayName: String?
+    val deviceId: String?
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
index 5f0a2298cb..062b2466e5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt
@@ -30,8 +30,8 @@ internal data class PasswordLoginParams(
         @Json(name = "identifier") val identifier: Map<String, String>,
         @Json(name = "password") val password: String,
         @Json(name = "type") override val type: String,
-        @Json(name = "initial_device_display_name") val deviceDisplayName: String?,
-        @Json(name = "device_id") val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams {
 
     companion object {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 0c6fb45e78..22cc185fa7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 @JsonClass(generateAdapter = true)
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
-        @Json(name = "token") val token: String
+        @Json(name = "token") val token: String,
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
+        @Json(name = "device_id") override val deviceId: String?
 ) : LoginParams
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
new file mode 100644
index 0000000000..477719f607
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.auth.login
+
+import dagger.Lazy
+import okhttp3.OkHttpClient
+import org.matrix.android.sdk.api.auth.LoginType
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.auth.AuthAPI
+import org.matrix.android.sdk.internal.auth.SessionCreator
+import org.matrix.android.sdk.internal.auth.data.TokenLoginParams
+import org.matrix.android.sdk.internal.di.Unauthenticated
+import org.matrix.android.sdk.internal.network.RetrofitFactory
+import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory
+import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException
+import org.matrix.android.sdk.internal.task.Task
+import javax.inject.Inject
+
+internal interface QrLoginTokenTask : Task<QrLoginTokenTask.Params, Session> {
+    data class Params(
+            val homeServerConnectionConfig: HomeServerConnectionConfig,
+            val loginToken: String,
+            val deviceName: String?,
+            val deviceId: String?
+    )
+}
+
+internal class DefaultQrLoginTokenTask @Inject constructor(
+        @Unauthenticated
+        private val okHttpClient: Lazy<OkHttpClient>,
+        private val retrofitFactory: RetrofitFactory,
+        private val sessionCreator: SessionCreator,
+) : QrLoginTokenTask {
+
+    override suspend fun execute(params: QrLoginTokenTask.Params): Session {
+        val client = buildClient(params.homeServerConnectionConfig)
+        val homeServerUrl = params.homeServerConnectionConfig.homeServerUriBase.toString()
+
+        val authAPI = retrofitFactory.create(client, homeServerUrl)
+                .create(AuthAPI::class.java)
+
+        val loginParams = TokenLoginParams(
+                token = params.loginToken,
+                deviceDisplayName = params.deviceName,
+                deviceId = params.deviceId
+        )
+
+        val credentials = try {
+            executeRequest(null) {
+                authAPI.login(loginParams)
+            }
+        } catch (throwable: Throwable) {
+            throw when (throwable) {
+                is UnrecognizedCertificateException -> Failure.UnrecognizedCertificateFailure(
+                        homeServerUrl,
+                        throwable.fingerprint
+                )
+                else -> throwable
+            }
+        }
+
+        return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.QR)
+    }
+
+    private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient {
+        return okHttpClient.get()
+                .newBuilder()
+                .addSocketFactory(homeServerConnectionConfig)
+                .build()
+    }
+}

From 282825db7952fd6516a3b1531fb709ca6831cd78 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:18:31 +0100
Subject: [PATCH 140/400] Changelog

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

diff --git a/changelog.d/7358.sdk b/changelog.d/7358.sdk
new file mode 100644
index 0000000000..3d17076a44
--- /dev/null
+++ b/changelog.d/7358.sdk
@@ -0,0 +1 @@
+Add support for `m.login.token` auth during QR code based sign in

From d0898a2b89e7ff0c2858fece93656a2b0427a19e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:19:39 +0100
Subject: [PATCH 141/400] Linting

---
 .../sdk/internal/auth/DefaultAuthenticationService.kt      | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 5b6f0686c4..773f2118b1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -422,7 +422,12 @@ internal class DefaultAuthenticationService @Inject constructor(
         }
     }
 
-    override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session {
+    override suspend fun loginUsingQrLoginToken(
+            homeServerConnectionConfig: HomeServerConnectionConfig,
+            loginToken: String,
+            initialDeviceName: String,
+            deviceId: String?,
+    ): Session {
         return loginTokenAuthTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,

From 3b3e11e5681874a7540e0885ee44da1885d29e30 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:32:49 +0100
Subject: [PATCH 142/400] Retry scanning if not a QR code


From de611ca81a1c9f16ded4b6b8d3d48b12b7edd68e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:33:30 +0100
Subject: [PATCH 143/400] Implementations of MSC3886 and MSC3903


From bfab07d716bddf91b2e7cab707dd2fe8e5ffb982 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 11 Oct 2022 23:34:05 +0100
Subject: [PATCH 144/400] Partial implementation of QR login logic

---
 .../features/login/qr/QrCodeLoginViewModel.kt | 27 ++++++++++++++-----
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 --------------
 2 files changed, 21 insertions(+), 26 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index da3348653c..05344f0a67 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -59,13 +59,28 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
-        if (isValidQrCode(action.qrCode)) {
-            setState {
-                copy(
-                        connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
-                )
+        Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
+
+        val rendezvous = Rendezvous.buildChannelFromCode(action.qrCode) { reason ->
+            Timber.tag(TAG).d("Rendezvous cancelled: $reason")
+            onFailed(reason)
+        }
+
+        setState {
+            copy(
+                    connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice
+            )
+        }
+
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+
+        viewModelScope.launch(Dispatchers.IO) {
+            val confirmationCode = rendezvous.startAfterScanningCode()
+            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
+            confirmationCode ?.let {
+                onConnectionEstablished(it)
             }
-            _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+            rendezvous.completeOnNewDevice()
         }
 
         // TODO. UI test purpose. Fixme remove!
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
deleted file mode 100644
index d7d74b08e9..0000000000
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * 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.voicebroadcast
-
-/** Voice Broadcast State Event. */
-const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From ef574bd82fbc5e33f0d4da51958158de9773a45c Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Wed, 12 Oct 2022 14:32:09 +0300
Subject: [PATCH 145/400] Merge branch 'develop' into
 feature/ons/qr_code_login_ui

# Conflicts:
#	library/ui-strings/src/main/res/values/strings.xml
#	library/ui-styles/src/main/res/values/stylable_sessions_list_header_view.xml
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
#	vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
#	vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt
#	vector/src/main/java/im/vector/app/features/VectorFeatures.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
#	vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionsListHeaderView.kt
#	vector/src/main/res/layout/fragment_other_sessions.xml
#	vector/src/main/res/layout/fragment_settings_devices.xml
---
 .../voicebroadcast/VoiceBroadcastConstants.kt | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
new file mode 100644
index 0000000000..d7d74b08e9
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.voicebroadcast
+
+/** Voice Broadcast State Event. */
+const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"

From b03240330d9905fc422a2fd3d166376c5d80f3a7 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Wed, 12 Oct 2022 13:08:01 +0100
Subject: [PATCH 146/400] Only do completeOnNewDevice if we received a
 confirmation code

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 05344f0a67..877d32a11d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -79,8 +79,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
             confirmationCode ?.let {
                 onConnectionEstablished(it)
+                rendezvous.completeOnNewDevice()
             }
-            rendezvous.completeOnNewDevice()
         }
 
         // TODO. UI test purpose. Fixme remove!

From 1e60f3c25b457e2cb40e5c8770e673c25f2fa941 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:35:45 +0100
Subject: [PATCH 147/400] Make initialDeviceName optional

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 6bb47dda5b..9fde6d9326 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -140,7 +140,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String? = null
     ): Session
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 773f2118b1..7aeeacd10b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -425,7 +425,7 @@ internal class DefaultAuthenticationService @Inject constructor(
     override suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String,
+            initialDeviceName: String?,
             deviceId: String?,
     ): Session {
         return loginTokenAuthTask.execute(

From e2f3dde5c1579c88521ae7b2e16b725f7e7c48e5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 15:58:19 +0100
Subject: [PATCH 148/400] Use correct var name

---
 .../android/sdk/internal/auth/DefaultAuthenticationService.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
index 7aeeacd10b..5449c0a735 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt
@@ -66,7 +66,7 @@ internal class DefaultAuthenticationService @Inject constructor(
         private val pendingSessionStore: PendingSessionStore,
         private val getWellknownTask: GetWellknownTask,
         private val directLoginTask: DirectLoginTask,
-        private val loginTokenAuthTask: QrLoginTokenTask
+        private val qrLoginTokenTask: QrLoginTokenTask
 ) : AuthenticationService {
 
     private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData()
@@ -428,7 +428,7 @@ internal class DefaultAuthenticationService @Inject constructor(
             initialDeviceName: String?,
             deviceId: String?,
     ): Session {
-        return loginTokenAuthTask.execute(
+        return qrLoginTokenTask.execute(
                 QrLoginTokenTask.Params(
                         homeServerConnectionConfig = homeServerConnectionConfig,
                         loginToken = loginToken,

From ca7a6efadee7430c4ac431a7cb89733db7486cc6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:02:57 +0100
Subject: [PATCH 149/400] Add missing binding

---
 .../java/org/matrix/android/sdk/internal/auth/AuthModule.kt  | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
index 463692e574..b1f65194f1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt
@@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule
 import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore
 import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore
 import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask
 import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
+import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask
 import org.matrix.android.sdk.internal.database.RealmKeysUtils
 import org.matrix.android.sdk.internal.di.AuthDatabase
 import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter
@@ -94,4 +96,7 @@ internal abstract class AuthModule {
 
     @Binds
     abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService
+
+    @Binds
+    abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask
 }

From ac80ae5632c4543b199189b90662ed4c971373eb Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:08:50 +0100
Subject: [PATCH 150/400] Set default value for optional params

---
 .../matrix/android/sdk/internal/auth/data/TokenLoginParams.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
index 22cc185fa7..52045a1d7a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt
@@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 internal data class TokenLoginParams(
         @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN,
         @Json(name = "token") val token: String,
-        @Json(name = "initial_device_display_name") override val deviceDisplayName: String?,
-        @Json(name = "device_id") override val deviceId: String?
+        @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null,
+        @Json(name = "device_id") override val deviceId: String? = null
 ) : LoginParams

From bc0843eddf1712983dd4990ac6835dd68fc0bbb5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:11:41 +0100
Subject: [PATCH 151/400] Another default value fix

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 9fde6d9326..1cc53b311c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -140,7 +140,7 @@ interface AuthenticationService {
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,
-            initialDeviceName: String?,
+            initialDeviceName: String? = null,
             deviceId: String? = null
     ): Session
 }

From 991eeb1de6c29515aa43d4c551b8cf12580f5f53 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 16:28:47 +0100
Subject: [PATCH 152/400] Map for soft logout

---
 .../im/vector/app/features/signout/soft/SoftLogoutController.kt  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
index b1a240e942..a1ed27df1d 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt
@@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor(
             LoginType.SSO -> buildLoginSSOForm()
             LoginType.DIRECT,
             LoginType.CUSTOM,
+            LoginType.QR,
             LoginType.UNSUPPORTED -> buildLoginUnsupportedForm()
             LoginType.UNKNOWN -> Unit
         }

From 9a72d6529b0a76bc83c28ed217f79c8a69c6acc4 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:08:45 +0100
Subject: [PATCH 153/400] Support for navigation to home screen

---
 .../im/vector/app/features/login/qr/QrCodeLoginActivity.kt | 7 +++++++
 .../vector/app/features/login/qr/QrCodeLoginViewEvents.kt  | 1 +
 2 files changed, 8 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 042f885231..fac31ce4e3 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -25,6 +25,7 @@ import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.app.features.home.HomeActivity
 import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
 
@@ -74,6 +75,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
             when (it) {
                 QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
                 QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen()
+                QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen()
             }
         }
     }
@@ -94,6 +96,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         )
     }
 
+    private fun handleNavigateToHomeScreen() {
+        val intent = HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true)
+        startActivity(intent)
+    }
+
     companion object {
 
         private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG"
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index dc258408e7..0f282fee38 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewEvents
 sealed class QrCodeLoginViewEvents : VectorViewEvents {
     object NavigateToStatusScreen : QrCodeLoginViewEvents()
     object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents()
+    object NavigateToHomeScreen : QrCodeLoginViewEvents()
 }

From dd47297dfd4bc774c509d06ccc1316fbb70e6514 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:15:52 +0100
Subject: [PATCH 154/400] QR login + E2EE set up

---
 .../sdk/internal/rendezvous/Rendezvous.kt     | 210 ++++++++++++++++++
 .../features/login/qr/QrCodeLoginViewModel.kt |  62 +++---
 2 files changed, 246 insertions(+), 26 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
new file mode 100644
index 0000000000..2f85a97c55
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
@@ -0,0 +1,210 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous
+
+import android.net.Uri
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
+import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport
+import timber.log.Timber
+
+internal enum class PayloadType(val value: String) {
+    @Json(name = "m.login.start") Start("m.login.start"),
+    @Json(name = "m.login.finish") Finish("m.login.finish"),
+    @Json(name = "m.login.progress") Progress("m.login.progress")
+}
+
+@JsonClass(generateAdapter = true)
+internal data class Payload(
+        @Json val type: PayloadType,
+        @Json val intent: RendezvousIntent? = null,
+        @Json val outcome: String? = null,
+        @Json val protocols: List<String>? = null,
+        @Json val protocol: String? = null,
+        @Json val homeserver: String? = null,
+        @Json val login_token: String? = null,
+        @Json val device_id: String? = null,
+        @Json val device_key: String? = null,
+        @Json val verifying_device_id: String? = null,
+        @Json val verifying_device_key: String? = null,
+        @Json val master_key: String? = null
+)
+
+private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+/**
+ * Implementation of MSC3906 to sign in + E2EE set up using a QR code.
+ */
+class Rendezvous(
+        val channel: RendezvousChannel,
+        val theirIntent: RendezvousIntent,
+) {
+    companion object {
+        fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
+            val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
+
+            val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri)
+
+            return Rendezvous(
+                    ECDHRendezvousChannel(transport, parsed.rendezvous.key),
+                    parsed.intent
+            )
+        }
+    }
+
+    private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java)
+    // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
+    val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
+
+    private suspend fun areIntentsIncompatible(): Boolean {
+        val incompatible = theirIntent == ourIntent
+
+        Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
+
+        if (incompatible) {
+            send(Payload(PayloadType.Finish, intent = ourIntent))
+            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn
+            channel.cancel(reason)
+        }
+
+        return incompatible
+    }
+
+    suspend fun startAfterScanningCode(): String? {
+        val checksum = channel.connect();
+
+        Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
+
+        if (areIntentsIncompatible()) {
+            return null
+        }
+
+        // get protocols
+        Timber.tag(TAG).i("Waiting for protocols");
+        val protocolsResponse = receive()
+
+        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
+            send(Payload(PayloadType.Finish, outcome = "unsupported"))
+            Timber.tag(TAG).i("No supported protocol")
+            cancel(RendezvousFailureReason.Unknown)
+            return null
+        }
+
+        send(Payload(PayloadType.Progress, protocol = "login_token"))
+
+        return checksum
+    }
+
+    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
+        Timber.tag(TAG).i("Waiting for login_token");
+
+        val loginToken = receive()
+
+        if (loginToken?.type == PayloadType.Finish) {
+            when (loginToken.outcome) {
+                "declined" -> {
+                    Timber.tag(TAG).i("Login declined by other device")
+                    channel.cancel(RendezvousFailureReason.UserDeclined)
+                    return null
+                }
+                "unsupported" -> {
+                    Timber.tag(TAG).i("Not supported")
+                    channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
+                    return null
+                }
+            }
+            channel.cancel(RendezvousFailureReason.Unknown)
+            return null
+        }
+
+        val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
+        val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
+
+        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
+
+        val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
+        return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
+    }
+
+    suspend fun completeVerificationOnNewDevice(session: Session) {
+        val userId = session.myUserId
+        val crypto = session.cryptoService()
+        val deviceId = crypto.getMyDevice().deviceId
+        val deviceKey = crypto.getMyDevice().fingerprint()
+        send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
+
+        // await confirmation of verification
+
+        val verificationResponse = receive()
+        val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
+        val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
+        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
+            return;
+        }
+
+        // set other device as verified
+        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
+        crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+
+        // TODO: what do we do with the master key?
+//        verificationResponse.master_key ?.let {
+//            // set master key as trusted
+//            crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
+//        }
+
+        // request secrets from the verifying device
+        Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+
+        session.sharedSecretStorageService() .let {
+            it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+            it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+        }
+    }
+
+    private suspend fun receive(): Payload? {
+        val data = channel.receive()?: return null
+        return adapter.fromJson(data.toString(Charsets.UTF_8))
+    }
+
+    private suspend fun send(payload: Payload) {
+        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8));
+    }
+
+    suspend fun cancel(reason: RendezvousFailureReason) {
+        channel.cancel(reason)
+    }
+
+    suspend fun close() {
+        channel.close()
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 877d32a11d..d9c30690a7 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -16,19 +16,32 @@
 
 package im.vector.app.features.login.qr
 
+import android.content.Context
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
+import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
-import kotlinx.coroutines.delay
+import im.vector.app.features.home.HomeActivity
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.auth.AuthenticationService
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.internal.rendezvous.Rendezvous
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
+        private val applicationContext: Context,
+        private val authenticationService: AuthenticationService,
+        private val activeSessionHolder: ActiveSessionHolder,
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
+    val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
     @AssistedFactory
     interface Factory : MavericksAssistedViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> {
@@ -75,28 +88,32 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
 
         viewModelScope.launch(Dispatchers.IO) {
-            val confirmationCode = rendezvous.startAfterScanningCode()
-            Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
-            confirmationCode ?.let {
-                onConnectionEstablished(it)
-                rendezvous.completeOnNewDevice()
-            }
-        }
+            try {
+                val confirmationCode = rendezvous.startAfterScanningCode()
+                Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
+                confirmationCode?.let {
+                    onConnectionEstablished(it)
+                    val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
+                    onSigningIn()
+                    session?.let {
+                        activeSessionHolder.setActiveSession(session)
+                        authenticationService.reset()
 
-        // TODO. UI test purpose. Fixme remove!
-        viewModelScope.launch {
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
-            delay(3000)
-            onConnectionEstablished("1234-ABCD-5678-EFGH")
-            delay(3000)
-            onSigningIn()
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
+                        session.configureAndStart(applicationContext)
+
+                        rendezvous.completeVerificationOnNewDevice(session)
+
+                        _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
+                    }
+                }
+            } catch (failure: Throwable) {
+                Timber.tag(TAG).e(failure, "Error occurred during sign in")
+                onFailed(RendezvousFailureReason.Unknown)
+            }
         }
     }
 
-    private fun onFailed(errorType: QrCodeLoginErrorType, canTryAgain: Boolean) {
+    private fun onFailed(reason: RendezvousFailureReason) {
         setState {
             copy(
                     connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain)
@@ -121,13 +138,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         }
     }
 
-    /**
-     * TODO. UI test purpose. Fixme accordingly.
-     */
-    private fun isValidQrCode(qrCode: String): Boolean {
-        return qrCode.startsWith("http")
-    }
-
     /**
      * TODO. UI test purpose. Fixme accordingly.
      */

From 7bc0bd3b57d1db71742051fe65d6b1003ad2037e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:26:22 +0100
Subject: [PATCH 155/400] Reduce logging

---
 .../channels/ECDHRendezvousChannel.kt         | 218 ++++++++++++++++++
 .../SimpleHttpRendezvousTransport.kt          | 185 +++++++++++++++
 2 files changed, 403 insertions(+)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
new file mode 100644
index 0000000000..cced29aab4
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -0,0 +1,218 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.channels
+
+import android.util.Base64
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import okhttp3.MediaType.Companion.toMediaType
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.extensions.toUnsignedInt
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel
+import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.olm.OlmSAS
+import timber.log.Timber
+import java.security.SecureRandom
+import java.util.LinkedList
+import javax.crypto.Cipher
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+@JsonClass(generateAdapter = true)
+data class ECDHPayload(
+        @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
+        @Json val key: String? = null,
+        @Json val ciphertext: String? = null,
+        @Json val iv: String? = null
+)
+
+private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+    val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
+    val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
+    val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
+    val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
+    val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
+    // (B0 << 5 | B1 >> 3) + 1000
+    val first = (b0.shl(5) or b1.shr(3)) + 1000
+    // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
+    val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
+    // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
+    val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
+    return "$first-$second-$third"
+}
+
+const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
+const val KEY_SPEC = "AES"
+
+/**
+ *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
+ *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
+ */
+class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel {
+    private var olmSAS: OlmSAS?
+    private val ourPublicKey: ByteArray
+    private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
+    private var theirPublicKey: ByteArray? = null
+    private var aesKey: ByteArray? = null
+
+    init {
+        theirPublicKeyBase64 ?.let {
+            theirPublicKey = Base64.decode(it, Base64.NO_WRAP)
+        }
+        olmSAS = OlmSAS()
+        ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP)
+    }
+
+    override suspend fun connect(): String {
+        if (olmSAS == null) {
+            throw RuntimeException("Channel closed")
+        }
+        val isInitiator = theirPublicKey == null
+
+        if (isInitiator) {
+//            Timber.tag(TAG).i("Waiting for other device to send their public key")
+            val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
+
+            if (res.key == null) {
+                throw RendezvousError(
+                        "Unsupported algorithm: ${res.algorithm}",
+                        RendezvousFailureReason.UnsupportedAlgorithm,
+                )
+            }
+            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
+        } else {
+            // send our public key unencrypted
+//            Timber.tag(TAG).i("Sending public key")
+            send(ECDHPayload(
+                    algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
+                    key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+            ))
+        }
+
+        olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+
+        val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
+        val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
+        val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
+
+        aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
+
+//        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
+//        Timber.tag(TAG).i("AES info: $aesInfo")
+//        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
+
+        val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
+        return getDecimalCodeRepresentation(rawChecksum)
+    }
+
+    private suspend fun send(payload: ECDHPayload) {
+        transport.send("application/json".toMediaType(), ecdhAdapter.toJson(payload).toByteArray(Charsets.UTF_8))
+    }
+
+    override suspend fun send(data: ByteArray) {
+        if (aesKey == null) {
+            throw RuntimeException("Shared secret not established")
+        }
+        send(encrypt(data))
+    }
+
+    private suspend fun receiveAsPayload(): ECDHPayload? {
+        transport.receive()?.toString(Charsets.UTF_8) ?.let {
+            return ecdhAdapter.fromJson(it)
+        } ?: return null
+    }
+
+    override suspend fun receive(): ByteArray? {
+        if (aesKey == null) {
+            throw RuntimeException("Shared secret not established")
+        }
+        val payload = receiveAsPayload() ?: return null
+        return decrypt(payload)
+    }
+
+    override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode {
+        return ECDHRendezvousCode(
+                intent,
+                rendezvous = ECDHRendezvous(
+                        transport.details() as SimpleHttpRendezvousTransportDetails,
+                        SecureRendezvousChannelAlgorithm.ECDH_V1,
+                        key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+                )
+        )
+    }
+
+    override suspend fun cancel(reason: RendezvousFailureReason) {
+        try {
+            transport.cancel(reason)
+        } finally {
+            close()
+        }
+    }
+
+    override suspend fun close() {
+        olmSAS?.releaseSas()
+        olmSAS = null
+    }
+
+    private fun encrypt(plainText: ByteArray): ECDHPayload {
+//        Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
+        val iv = ByteArray(16)
+        SecureRandom().nextBytes(iv)
+
+        val cipherText = LinkedList<Byte>()
+
+        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
+        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
+        val ivParameterSpec = IvParameterSpec(iv)
+        encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
+        cipherText.addAll(encryptCipher.update(plainText).toList())
+        cipherText.addAll(encryptCipher.doFinal().toList())
+
+        return ECDHPayload(
+                ciphertext = Base64.encodeToString(cipherText.toByteArray(), Base64.NO_WRAP),
+                iv = Base64.encodeToString(iv, Base64.NO_WRAP)
+        )
+    }
+
+    private fun decrypt(payload: ECDHPayload): ByteArray {
+        val iv = Base64.decode(payload.iv, Base64.NO_WRAP)
+        val encryptCipher = Cipher.getInstance(ALGORITHM_SPEC)
+        val secretKeySpec = SecretKeySpec(aesKey, KEY_SPEC)
+        val ivParameterSpec = IvParameterSpec(iv)
+        encryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
+
+        val plainText = LinkedList<Byte>()
+        plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList())
+        plainText.addAll(encryptCipher.doFinal().toList())
+
+        val plainTextBytes = plainText.toByteArray()
+
+//        Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
+        return plainTextBytes
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
new file mode 100644
index 0000000000..cc4346d55e
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -0,0 +1,185 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.transports
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import kotlinx.coroutines.delay
+import okhttp3.MediaType
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
+import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType
+import timber.log.Timber
+import java.text.SimpleDateFormat
+import java.util.Date
+
+private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+@JsonClass(generateAdapter = true)
+data class SimpleHttpRendezvousTransportDetails(
+        @Json val uri: String
+): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
+
+/**
+ * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
+ */
+class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
+    override var ready = false
+    private var cancelled = false
+    private var uri: String?
+    private var etag: String? = null
+    private var expiresAt: Date? = null
+
+    init {
+        uri = rendezvousUri
+    }
+
+    override suspend fun details(): RendezvousTransportDetails {
+        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
+
+        return SimpleHttpRendezvousTransportDetails(uri)
+    }
+
+    override suspend fun send(contentType: MediaType, data: ByteArray) {
+        if (cancelled) {
+            return
+        }
+
+        val method = if (uri != null) "PUT" else "POST"
+        // TODO: properly determine endpoint
+        val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
+
+//        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
+
+        val httpClient = okhttp3.OkHttpClient.Builder().build()
+
+        val request = Request.Builder()
+                .url(uri)
+                .method(method, data.toRequestBody())
+                .header("content-type", contentType.toString())
+
+        etag ?.let {
+            request.header("if-match", it)
+        }
+
+        val response = httpClient.newCall(request.build()).execute()
+
+        if (response.code == 404) {
+            cancel(RendezvousFailureReason.Unknown)
+        }
+        etag = response.header("etag")
+
+        Timber.tag(TAG).i("Sent data to $uri new etag $etag")
+
+        if (method == "POST") {
+            val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
+
+            response.header("expires") ?.let {
+                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
+                expiresAt = format.parse(it)
+            }
+
+            // resolve location header which could be relative or absolute
+            this.uri = response.request.url.toUri().resolve(location).toString()
+            ready = true
+        }
+    }
+
+    override suspend fun receive(): ByteArray? {
+        val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
+        var done = false
+        val httpClient = okhttp3.OkHttpClient.Builder().build()
+        while (!done) {
+            if (cancelled) {
+                return null
+            }
+            Timber.tag(TAG).i("Polling: $uri after etag $etag")
+            val request = Request.Builder()
+                    .url(uri)
+                    .get()
+
+            etag ?.let {
+                request.header("if-none-match", it)
+            }
+
+            val response = httpClient.newCall(request.build()).execute()
+
+            try {
+//                Timber.tag(TAG).d("Received polling response: ${response.code} from $uri")
+
+                if (response.code == 404) {
+                    cancel(RendezvousFailureReason.Unknown)
+                    return null
+                }
+
+                // rely on server expiring the channel rather than checking ourselves
+
+                if (response.header("content-type") != "application/json") {
+                    response.header("etag")?.let {
+                        etag = it
+                    }
+                } else if (response.code == 200) {
+                    response.header("etag")?.let {
+                        etag = it
+                    }
+                    val data = response.body?.bytes()
+//                    Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
+                    return data
+                }
+
+                done = false
+                delay(1000)
+            } finally {
+                response.close()
+            }
+        }
+
+        return null
+    }
+
+    override suspend fun cancel(reason: RendezvousFailureReason) {
+        var mappedReason = reason
+        Timber.tag(TAG).i("$expiresAt")
+        if (mappedReason == RendezvousFailureReason.Unknown &&
+                expiresAt != null && Date() > expiresAt) {
+            mappedReason = RendezvousFailureReason.Expired
+        }
+
+        cancelled = true
+        ready = false
+        onCancelled ?.let { it(mappedReason) }
+
+        if (mappedReason == RendezvousFailureReason.UserDeclined) {
+            uri ?.let {
+                try {
+                    val httpClient = okhttp3.OkHttpClient.Builder().build()
+                    val request = Request.Builder()
+                            .url(it)
+                            .delete()
+                            .build()
+                    httpClient.newCall(request).execute()
+                } catch (e: Exception) {
+                    Timber.tag(TAG).w(e, "Failed to delete channel")
+                }
+            }
+        }
+    }
+}

From 6399032312c743793fc79ec1f7863c95aec0db1a Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:23:26 +0100
Subject: [PATCH 156/400] Fix bad merge

---
 .../org/matrix/android/sdk/api/auth/AuthenticationService.kt   | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
index 1cc53b311c..252c33a8c4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt
@@ -130,13 +130,14 @@ interface AuthenticationService {
      * Return true if qr code login is supported by the server, false otherwise.
      */
     suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
+
+    /**
      * Authenticate using m.login.token method during sign in with QR code.
      * @param homeServerConnectionConfig the information about the homeserver and other configuration
      * @param loginToken the m.login.token
      * @param initialDeviceName the initial device name
      * @param deviceId the device id, optional. If not provided or null, the server will generate one.
      */
-
     suspend fun loginUsingQrLoginToken(
             homeServerConnectionConfig: HomeServerConnectionConfig,
             loginToken: String,

From 958ee2d3561fb4b871ba83c6cb3be2ce4fbeb943 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:32:02 +0100
Subject: [PATCH 157/400] Revert "Revert "Retry scanning if not a QR code""

This reverts commit 9429a4f22aabfbff23ffc223c3b090626be553dc.
---
 .../app/features/login/qr/QrCodeLoginInstructionsFragment.kt    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index ae3ba9574b..17b4ff2409 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -83,6 +83,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
         if (activityResult.resultCode == Activity.RESULT_OK) {
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
+            Timber.d("Scanned QR code: $scannedQrCode $wasQrCode")
 
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
@@ -98,5 +99,6 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     private fun onQrCodeScannerFailed() {
         Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
+        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 }

From 370652c04b684b5436d3e03a94ed41913efc6523 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:37:19 +0100
Subject: [PATCH 158/400] Revert "Revert "Implementations of MSC3886 and
 MSC3903""

This reverts commit 489dfd73546a78246b1f18faf97e7a36bb4e0241.
---
 .../android/sdk/api/logger/LoggerTag.kt       |  1 +
 .../internal/rendezvous/RendezvousChannel.kt  | 45 +++++++++++++++++++
 .../rendezvous/RendezvousFailureReason.kt     | 31 +++++++++++++
 .../rendezvous/RendezvousTransport.kt         | 29 ++++++++++++
 .../rendezvous/model/ECDHRendezvous.kt        | 34 ++++++++++++++
 .../rendezvous/model/EmbeddedRendezvous.kt    | 26 +++++++++++
 .../rendezvous/model/RendezvousError.kt       | 22 +++++++++
 .../rendezvous/model/RendezvousIntent.kt      | 24 ++++++++++
 .../model/RendezvousTransportDetails.kt       | 25 +++++++++++
 .../model/RendezvousTransportType.kt          | 23 ++++++++++
 .../model/SecureRendezvousChannelAlgorithm.kt | 23 ++++++++++
 11 files changed, 283 insertions(+)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
index ae65963f37..22af8cebbd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt
@@ -27,6 +27,7 @@ open class LoggerTag(name: String, parentTag: LoggerTag? = null) {
     object SYNC : LoggerTag("SYNC")
     object VOIP : LoggerTag("VOIP")
     object CRYPTO : LoggerTag("CRYPTO")
+    object RENDEZVOUS : LoggerTag("RZ")
 
     val value: String = if (parentTag == null) {
         name
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
new file mode 100644
index 0000000000..43552f46be
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
@@ -0,0 +1,45 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous
+
+import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+
+interface RendezvousChannel {
+    var transport: RendezvousTransport;
+    /**
+     * @returns the checksum/confirmation digits to be shown to the user
+     */
+    suspend fun connect(): String
+    /**
+     * Send a payload via the channel.
+     * @param data payload to send
+     */
+    suspend fun send(data: ByteArray)
+    /**
+     * Receive a payload from the channel.
+     * @returns the received payload
+     */
+    suspend fun receive(): ByteArray?
+    /**
+     * @returns a representation of the channel that can be encoded in a QR or similar
+     */
+    suspend fun close()
+    // TODO: this should be transport independent in the future
+    suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
+    suspend fun cancel(reason: RendezvousFailureReason)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
new file mode 100644
index 0000000000..0e2ea8c758
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.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 org.matrix.android.sdk.internal.rendezvous
+
+enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
+    UserDeclined("user_declined"),
+    OtherDeviceNotSignedIn("other_device_not_signed_in"),
+    OtherDeviceAlreadySignedIn("other_device_already_signed_in"),
+    Unknown("unknown"),
+    Expired("expired"),
+    UserCancelled("user_cancelled"),
+    InvalidCode("invalid_code"),
+    UnsupportedAlgorithm("unsupported_algorithm", false),
+    DataMismatch("data_mismatch"),
+    UnsupportedTransport("unsupported_transport", false),
+    HomeserverLacksSupport("homeserver_lacks_support", false)
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
new file mode 100644
index 0000000000..753b0bc6fa
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
@@ -0,0 +1,29 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous
+
+import okhttp3.MediaType
+import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+
+interface RendezvousTransport {
+    var ready: Boolean;
+    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?;
+    suspend fun details(): RendezvousTransportDetails;
+    suspend fun send(contentType: MediaType, data: ByteArray);
+    suspend fun receive(): ByteArray?;
+    suspend fun cancel(reason: RendezvousFailureReason);
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
new file mode 100644
index 0000000000..e296dce09d
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
@@ -0,0 +1,34 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvous(
+        @Json val transport: SimpleHttpRendezvousTransportDetails,
+        @Json val algorithm: SecureRendezvousChannelAlgorithm,
+        @Json val key: String
+)
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvousCode(
+        @Json val intent: RendezvousIntent,
+        @Json val rendezvous: ECDHRendezvous
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
new file mode 100644
index 0000000000..d490de0133
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
@@ -0,0 +1,26 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+open class EmbeddedRendezvous(
+    @Json(name = "transport") val transport: RendezvousTransportDetails,
+    @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
new file mode 100644
index 0000000000..ead273e8ce
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+
+class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
new file mode 100644
index 0000000000..6285c1e57a
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class RendezvousIntent {
+    @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
+    @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
new file mode 100644
index 0000000000..1b1826194f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
@@ -0,0 +1,25 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+open class RendezvousTransportDetails(
+    @Json val type: RendezvousTransportType
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
new file mode 100644
index 0000000000..c3b6ba7ac8
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
@@ -0,0 +1,23 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class RendezvousTransportType(val value: String) {
+    @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
new file mode 100644
index 0000000000..ddc0ae20e7
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -0,0 +1,23 @@
+/*
+ * 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 org.matrix.android.sdk.internal.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class SecureRendezvousChannelAlgorithm(val value: String) {
+    @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
+}

From f04f0e6fac25345b7cdc39181e0ac4743fdb37c5 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 22:42:58 +0100
Subject: [PATCH 159/400] Revert "Revert "Partial implementation of QR login
 logic""

This reverts commit e305478ddabf39c6e2a272485a1b154b3ad12263.
---
 .../src/main/res/values/strings.xml           |  1 +
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  4 +++-
 .../features/login/qr/QrCodeLoginErrorType.kt | 23 -------------------
 .../login/qr/QrCodeLoginStatusFragment.kt     | 12 ++++++----
 .../features/login/qr/QrCodeLoginViewModel.kt |  4 ++--
 5 files changed, 13 insertions(+), 31 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 822e9d3865..108fe7db7c 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3368,6 +3368,7 @@
     <string name="qr_code_login_header_failed_device_is_not_supported_description">Linking with this device is not supported.</string>
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
+    <string name="qr_code_login_header_failed_other_description">The request failed.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index 330562b874..4de191f863 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -16,9 +16,11 @@
 
 package im.vector.app.features.login.qr
 
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+
 sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
     data class Connected(val securityCode: String, val canConfirmSecurityCode: Boolean) : QrCodeLoginConnectionStatus()
     object SigningIn : QrCodeLoginConnectionStatus()
-    data class Failed(val errorType: QrCodeLoginErrorType, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
+    data class Failed(val errorType: RendezvousFailureReason, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
deleted file mode 100644
index 9a6cc13de0..0000000000
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.login.qr
-
-enum class QrCodeLoginErrorType {
-    DEVICE_IS_NOT_SUPPORTED,
-    TIMEOUT,
-    REQUEST_WAS_DENIED,
-}
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 1c0841aa11..fb372cbb2f 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -27,6 +27,7 @@ import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
 import im.vector.app.features.themes.ThemeUtils
+import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
 
 @AndroidEntryPoint
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
@@ -77,11 +78,12 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         )
     }
 
-    private fun getErrorCode(errorType: QrCodeLoginErrorType): String {
-        return when (errorType) {
-            QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
-            QrCodeLoginErrorType.TIMEOUT -> getString(R.string.qr_code_login_header_failed_timeout_description)
-            QrCodeLoginErrorType.REQUEST_WAS_DENIED -> getString(R.string.qr_code_login_header_failed_denied_description)
+    private fun getErrorCode(reason: RendezvousFailureReason): String {
+        return when (reason) {
+            RendezvousFailureReason.UnsupportedAlgorithm, RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
+            RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
+            else -> getString(R.string.qr_code_login_header_failed_other_description)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index d9c30690a7..8461d8d88f 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -39,7 +39,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
         private val applicationContext: Context,
         private val authenticationService: AuthenticationService,
-        private val activeSessionHolder: ActiveSessionHolder,
+        private val activeSessionHolder: ActiveSessionHolder
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
     val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
@@ -116,7 +116,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     private fun onFailed(reason: RendezvousFailureReason) {
         setState {
             copy(
-                    connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain)
+                    connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry)
             )
         }
     }

From f2d76be20c724f461d4b4c86bba767f52857a553 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 13 Oct 2022 23:06:43 +0000
Subject: [PATCH 160/400] Bump com.google.devtools.ksp from 1.7.20-1.0.6 to
 1.7.20-1.0.7

Bumps [com.google.devtools.ksp](https://github.com/google/ksp) from 1.7.20-1.0.6 to 1.7.20-1.0.7.
- [Release notes](https://github.com/google/ksp/releases)
- [Commits](https://github.com/google/ksp/compare/1.7.20-1.0.6...1.7.20-1.0.7)

---
updated-dependencies:
- dependency-name: com.google.devtools.ksp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index e474b68531..32a7c2c26f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -45,7 +45,7 @@ plugins {
     // Detekt
     id "io.gitlab.arturbosch.detekt" version "1.21.0"
     // Ksp
-    id "com.google.devtools.ksp" version "1.7.20-1.0.6"
+    id "com.google.devtools.ksp" version "1.7.20-1.0.7"
 
     // Dependency Analysis
     id 'com.autonomousapps.dependency-analysis' version "1.13.1"

From 1960dc3e41bd67ad1b782a2fcc2878815a99d58c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 13 Oct 2022 23:08:06 +0000
Subject: [PATCH 161/400] Bump libphonenumber from 8.12.56 to 8.12.57

Bumps [libphonenumber](https://github.com/google/libphonenumber) from 8.12.56 to 8.12.57.
- [Release notes](https://github.com/google/libphonenumber/releases)
- [Changelog](https://github.com/google/libphonenumber/blob/master/making-metadata-changes.md)
- [Commits](https://github.com/google/libphonenumber/compare/v8.12.56...v8.12.57)

---
updated-dependencies:
- dependency-name: com.googlecode.libphonenumber:libphonenumber
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index c3dd64b6a6..888f04f241 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -86,7 +86,7 @@ ext.libs = [
                 'appdistributionApi'      : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution",
                 'appdistribution'         : "com.google.firebase:firebase-appdistribution:$appDistribution",
                 // Phone number https://github.com/google/libphonenumber
-                'phonenumber'             : "com.googlecode.libphonenumber:libphonenumber:8.12.56"
+                'phonenumber'             : "com.googlecode.libphonenumber:libphonenumber:8.12.57"
         ],
         dagger      : [
                 'dagger'                  : "com.google.dagger:dagger:$dagger",

From 5abb786b6b792ea70f38ca616bd5925a329af719 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 00:59:31 +0100
Subject: [PATCH 162/400] Fix copyright on SDK

---
 .../org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt    | 2 +-
 .../matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt | 2 +-
 .../android/sdk/internal/rendezvous/RendezvousFailureReason.kt  | 2 +-
 .../android/sdk/internal/rendezvous/RendezvousTransport.kt      | 2 +-
 .../sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt   | 2 +-
 .../android/sdk/internal/rendezvous/model/ECDHRendezvous.kt     | 2 +-
 .../android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt | 2 +-
 .../android/sdk/internal/rendezvous/model/RendezvousError.kt    | 2 +-
 .../android/sdk/internal/rendezvous/model/RendezvousIntent.kt   | 2 +-
 .../sdk/internal/rendezvous/model/RendezvousTransportDetails.kt | 2 +-
 .../sdk/internal/rendezvous/model/RendezvousTransportType.kt    | 2 +-
 .../rendezvous/model/SecureRendezvousChannelAlgorithm.kt        | 2 +-
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt      | 2 +-
 13 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
index 2f85a97c55..d8fd323056 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
index 43552f46be..6655468808 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
index 0e2ea8c758..0920bf224f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
index 753b0bc6fa..f054b7cb73 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
index cced29aab4..9f347b8536 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
index e296dce09d..14976ec836 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
index d490de0133..5a609d497e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
index ead273e8ce..feb7d0cf35 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
index 6285c1e57a..d317531835 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
index 1b1826194f..ee9058daa3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
index c3b6ba7ac8..51371ddb2f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index ddc0ae20e7..122b6ede82 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index cc4346d55e..7918946d75 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 New Vector Ltd
+ * Copyright 2020 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.

From c18439f99b7056619fcf1d7f7df3697d1ee5b63c Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 01:07:19 +0100
Subject: [PATCH 163/400] Refactor code into api from internal

---
 .../rendezvous/Rendezvous.kt                  | 12 ++++----
 .../rendezvous/RendezvousChannel.kt           |  8 +++---
 .../rendezvous/RendezvousFailureReason.kt     |  4 +--
 .../rendezvous/RendezvousTransport.kt         |  6 ++--
 .../channels/ECDHRendezvousChannel.kt         | 28 ++++++++++---------
 .../rendezvous/model/ECDHRendezvous.kt        |  6 ++--
 .../rendezvous/model/EmbeddedRendezvous.kt    |  8 +++---
 .../rendezvous/model/RendezvousError.kt       |  6 ++--
 .../rendezvous/model/RendezvousIntent.kt      |  4 +--
 .../model/RendezvousTransportDetails.kt       |  4 +--
 .../model/RendezvousTransportType.kt          |  4 +--
 .../model/SecureRendezvousChannelAlgorithm.kt |  4 +--
 .../SimpleHttpRendezvousTransport.kt          | 12 ++++----
 .../login/qr/QrCodeLoginConnectionStatus.kt   |  2 +-
 .../login/qr/QrCodeLoginStatusFragment.kt     |  2 +-
 .../features/login/qr/QrCodeLoginViewModel.kt |  4 +--
 16 files changed, 58 insertions(+), 56 deletions(-)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/Rendezvous.kt (95%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/RendezvousChannel.kt (84%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/RendezvousFailureReason.kt (91%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/RendezvousTransport.kt (83%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/channels/ECDHRendezvousChannel.kt (90%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/ECDHRendezvous.kt (83%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/EmbeddedRendezvous.kt (72%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousError.kt (79%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousIntent.kt (87%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousTransportDetails.kt (87%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/RendezvousTransportType.kt (86%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/model/SecureRendezvousChannelAlgorithm.kt (87%)
 rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/rendezvous/transports/SimpleHttpRendezvousTransport.kt (93%)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
similarity index 95%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index d8fd323056..534d5df1ca 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
 import android.net.Uri
 import com.squareup.moshi.Json
@@ -29,10 +29,10 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NA
 import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import timber.log.Timber
 
 internal enum class PayloadType(val value: String) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
similarity index 84%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 6655468808..2a73e8f112 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
 interface RendezvousChannel {
     var transport: RendezvousTransport;
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
similarity index 91%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
index 0920bf224f..a607dc7f38 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
 enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
     UserDeclined("user_declined"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
similarity index 83%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index f054b7cb73..11471288f6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous
+package org.matrix.android.sdk.api.rendezvous
 
 import okhttp3.MediaType
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
     var ready: Boolean;
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
similarity index 90%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 9f347b8536..0f69bd7eda 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.channels
+package org.matrix.android.sdk.api.rendezvous.channels
 
 import android.util.Base64
 import com.squareup.moshi.Json
@@ -23,15 +23,15 @@ import okhttp3.MediaType.Companion.toMediaType
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel
-import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous
-import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
 import org.matrix.olm.OlmSAS
 import timber.log.Timber
 import java.security.SecureRandom
@@ -107,10 +107,12 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         } else {
             // send our public key unencrypted
 //            Timber.tag(TAG).i("Sending public key")
-            send(ECDHPayload(
+            send(
+                    ECDHPayload(
                     algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
                     key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-            ))
+            )
+            )
         }
 
         olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
similarity index 83%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
index 14976ec836..b203101b66 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvous(
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
similarity index 72%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
index 5a609d497e..785ce1fed7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 open class EmbeddedRendezvous(
-    @Json(name = "transport") val transport: RendezvousTransportDetails,
-    @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
+        @Json(name = "transport") val transport: RendezvousTransportDetails,
+        @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
similarity index 79%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
index feb7d0cf35..f731c89649 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
 class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
similarity index 87%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
index d317531835..1c070599b0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
similarity index 87%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
index ee9058daa3..55b3bbb5d9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
similarity index 86%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index 51371ddb2f..9c3e44f25b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
similarity index 87%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index 122b6ede82..9a9db58a41 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.model
+package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
similarity index 93%
rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 7918946d75..ba60459544 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Matrix.org Foundation C.I.C.
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.matrix.android.sdk.internal.rendezvous.transports
+package org.matrix.android.sdk.api.rendezvous.transports
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
@@ -23,10 +23,10 @@ import okhttp3.MediaType
 import okhttp3.Request
 import okhttp3.RequestBody.Companion.toRequestBody
 import org.matrix.android.sdk.api.logger.LoggerTag
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
-import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails
-import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportType
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.Date
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
index 4de191f863..4bef41b6c1 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.login.qr
 
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
 sealed class QrCodeLoginConnectionStatus {
     object ConnectingToDevice : QrCodeLoginConnectionStatus()
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index fb372cbb2f..5451a16b44 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -27,7 +27,7 @@ import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
 import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding
 import im.vector.app.features.themes.ThemeUtils
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
 @AndroidEntryPoint
 class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBinding>() {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 8461d8d88f..276cedad43 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -31,8 +31,8 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.internal.rendezvous.Rendezvous
-import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.Rendezvous
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(

From c00ce91214cea1a0308d7885d46b5ea5224f5aea Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 01:11:25 +0100
Subject: [PATCH 164/400] Linting

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 27 ++++++++++---------
 .../sdk/api/rendezvous/RendezvousChannel.kt   |  7 ++++-
 .../sdk/api/rendezvous/RendezvousTransport.kt | 12 ++++-----
 .../channels/ECDHRendezvousChannel.kt         |  9 +++----
 .../api/rendezvous/model/RendezvousError.kt   |  3 +--
 .../SimpleHttpRendezvousTransport.kt          |  2 +-
 .../features/login/qr/QrCodeLoginViewModel.kt |  2 --
 7 files changed, 32 insertions(+), 30 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 534d5df1ca..e33130e529 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -22,6 +22,10 @@ import com.squareup.moshi.JsonClass
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
+import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
 import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
@@ -29,10 +33,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NA
 import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
 import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
-import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import timber.log.Timber
 
 internal enum class PayloadType(val value: String) {
@@ -80,6 +80,7 @@ class Rendezvous(
     }
 
     private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java)
+
     // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
     val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
 
@@ -98,7 +99,7 @@ class Rendezvous(
     }
 
     suspend fun startAfterScanningCode(): String? {
-        val checksum = channel.connect();
+        val checksum = channel.connect()
 
         Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
 
@@ -107,7 +108,7 @@ class Rendezvous(
         }
 
         // get protocols
-        Timber.tag(TAG).i("Waiting for protocols");
+        Timber.tag(TAG).i("Waiting for protocols")
         val protocolsResponse = receive()
 
         if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
@@ -123,7 +124,7 @@ class Rendezvous(
     }
 
     suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
-        Timber.tag(TAG).i("Waiting for login_token");
+        Timber.tag(TAG).i("Waiting for login_token")
 
         val loginToken = receive()
 
@@ -147,7 +148,7 @@ class Rendezvous(
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
         val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
 
-        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver");
+        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver")
 
         val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
         return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
@@ -167,11 +168,11 @@ class Rendezvous(
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
             Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
-            return;
+            return
         }
 
         // set other device as verified
-        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified");
+        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
         // TODO: what do we do with the master key?
@@ -183,7 +184,7 @@ class Rendezvous(
         // request secrets from the verifying device
         Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
 
-        session.sharedSecretStorageService() .let {
+        session.sharedSecretStorageService().let {
             it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
             it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
             it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
@@ -192,12 +193,12 @@ class Rendezvous(
     }
 
     private suspend fun receive(): Payload? {
-        val data = channel.receive()?: return null
+        val data = channel.receive() ?: return null
         return adapter.fromJson(data.toString(Charsets.UTF_8))
     }
 
     private suspend fun send(payload: Payload) {
-        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8));
+        channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8))
     }
 
     suspend fun cancel(reason: RendezvousFailureReason) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 2a73e8f112..588d034f10 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -20,25 +20,30 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
 interface RendezvousChannel {
-    var transport: RendezvousTransport;
+    var transport: RendezvousTransport
+
     /**
      * @returns the checksum/confirmation digits to be shown to the user
      */
     suspend fun connect(): String
+
     /**
      * Send a payload via the channel.
      * @param data payload to send
      */
     suspend fun send(data: ByteArray)
+
     /**
      * Receive a payload from the channel.
      * @returns the received payload
      */
     suspend fun receive(): ByteArray?
+
     /**
      * @returns a representation of the channel that can be encoded in a QR or similar
      */
     suspend fun close()
+
     // TODO: this should be transport independent in the future
     suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
     suspend fun cancel(reason: RendezvousFailureReason)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index 11471288f6..de0aed7efc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -20,10 +20,10 @@ import okhttp3.MediaType
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
-    var ready: Boolean;
-    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?;
-    suspend fun details(): RendezvousTransportDetails;
-    suspend fun send(contentType: MediaType, data: ByteArray);
-    suspend fun receive(): ByteArray?;
-    suspend fun cancel(reason: RendezvousFailureReason);
+    var ready: Boolean
+    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?
+    suspend fun details(): RendezvousTransportDetails
+    suspend fun send(contentType: MediaType, data: ByteArray)
+    suspend fun receive(): ByteArray?
+    suspend fun cancel(reason: RendezvousFailureReason)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 0f69bd7eda..1c8bca5d1c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -21,10 +21,8 @@ import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import okhttp3.MediaType.Companion.toMediaType
 import org.matrix.android.sdk.api.logger.LoggerTag
-import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.extensions.toUnsignedInt
-import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
+import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
@@ -32,8 +30,9 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.util.MatrixJsonParser
+import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
-import timber.log.Timber
 import java.security.SecureRandom
 import java.util.LinkedList
 import javax.crypto.Cipher
@@ -72,7 +71,7 @@ const val KEY_SPEC = "AES"
  *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
  *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
  */
-class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel {
+class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?) : RendezvousChannel {
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
index f731c89649..fec55ffb67 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
@@ -18,5 +18,4 @@ package org.matrix.android.sdk.api.rendezvous.model
 
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
-class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) {
-}
+class RendezvousError(val description: String, val reason: RendezvousFailureReason) : RuntimeException(description)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index ba60459544..475a4fbe6c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -36,7 +36,7 @@ private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName
 @JsonClass(generateAdapter = true)
 data class SimpleHttpRendezvousTransportDetails(
         @Json val uri: String
-): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
+) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
 
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 276cedad43..7a51098439 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -26,11 +26,9 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
-import im.vector.app.features.home.HomeActivity
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
-import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.rendezvous.Rendezvous
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import timber.log.Timber

From 343cf748714649111fda335e0260593842403d8b Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 01:45:03 +0100
Subject: [PATCH 165/400] Add flag to allow QR login on all servers + split
 flag for showing in device manager

---
 .../debug/features/DebugFeaturesStateFactory.kt        | 10 ++++++++++
 .../app/features/debug/features/DebugVectorFeatures.kt |  8 ++++++++
 .../main/java/im/vector/app/features/VectorFeatures.kt |  4 ++++
 .../app/features/onboarding/OnboardingViewModel.kt     |  9 +++++++++
 .../devices/v2/VectorSettingsDevicesFragment.kt        |  2 +-
 5 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 3a302feba0..1d8171a9e5 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -95,6 +95,16 @@ class DebugFeaturesStateFactory @Inject constructor(
                                 key = DebugFeatureKeys.qrCodeLoginEnabled,
                                 factory = VectorFeatures::isQrCodeLoginEnabled
                         ),
+                        createBooleanFeature(
+                                label = "Allow QR Code Login for all servers",
+                                key = DebugFeatureKeys.allowQrCodeLoginForAllServers,
+                                factory = VectorFeatures::allowQrCodeLoginForAllServers
+                        ),
+                        createBooleanFeature(
+                                label = "Show QR Code Login in Device Manager",
+                                key = DebugFeatureKeys.allowReciprocateQrCodeLogin,
+                                factory = VectorFeatures::allowReciprocateQrCodeLogin
+                        ),
                         createBooleanFeature(
                                 label = "Enable Voice Broadcast",
                                 key = DebugFeatureKeys.voiceBroadcastEnabled,
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index b5ffa28db7..701f2dcab8 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -82,6 +82,12 @@ class DebugVectorFeatures(
     override fun isQrCodeLoginEnabled() = read(DebugFeatureKeys.qrCodeLoginEnabled)
             ?: vectorFeatures.isQrCodeLoginEnabled()
 
+    override fun allowQrCodeLoginForAllServers() = read(DebugFeatureKeys.allowQrCodeLoginForAllServers)
+            ?: vectorFeatures.allowQrCodeLoginForAllServers()
+
+    override fun allowReciprocateQrCodeLogin() = read(DebugFeatureKeys.allowReciprocateQrCodeLogin)
+            ?: vectorFeatures.allowReciprocateQrCodeLogin()
+
     override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
             ?: vectorFeatures.isVoiceBroadcastEnabled()
 
@@ -147,5 +153,7 @@ object DebugFeatureKeys {
     val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled")
     val newDeviceManagementEnabled = booleanPreferencesKey("new-device-management-enabled")
     val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled")
+    val allowQrCodeLoginForAllServers = booleanPreferencesKey("allow-qr-code-login-for-all-servers")
+    val allowReciprocateQrCodeLogin = booleanPreferencesKey("allow-reciprocate-qr-code-login")
     val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
 }
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index 62eb0523b0..061276ba21 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -42,6 +42,8 @@ interface VectorFeatures {
     fun isNewAppLayoutFeatureEnabled(): Boolean
     fun isNewDeviceManagementEnabled(): Boolean
     fun isQrCodeLoginEnabled(): Boolean
+    fun allowQrCodeLoginForAllServers(): Boolean
+    fun allowReciprocateQrCodeLogin(): Boolean
     fun isVoiceBroadcastEnabled(): Boolean
 }
 
@@ -60,5 +62,7 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun isNewAppLayoutFeatureEnabled(): Boolean = true
     override fun isNewDeviceManagementEnabled(): Boolean = false
     override fun isQrCodeLoginEnabled(): Boolean = false
+    override fun allowQrCodeLoginForAllServers(): Boolean = false
+    override fun allowReciprocateQrCodeLogin(): Boolean = false
     override fun isVoiceBroadcastEnabled(): Boolean = false
 }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 03cf2f43e6..8e64948c6a 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -124,7 +124,16 @@ class OnboardingViewModel @AssistedInject constructor(
                         canLoginWithQrCode = false
                 )
             }
+        } else if (vectorFeatures.allowQrCodeLoginForAllServers()) {
+            // allow for all servers
+            setState {
+                copy(
+                        canLoginWithQrCode = true
+                )
+            }
         } else {
+            // check if selected server supports MSC3882 first
+            // FIXME: this should be checking the selected homeserver not defaultHomeserverUrl
             homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
                 val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
                 setState {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index 48f66cfc75..7e6da851dd 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -158,7 +158,7 @@ class VectorSettingsDevicesFragment :
     }
 
     private fun initQrLoginView() {
-        if (!vectorFeatures.isQrCodeLoginEnabled()) {
+        if (!vectorFeatures.allowReciprocateQrCodeLogin()) {
             views.deviceListHeaderSignInWithQrCode.isVisible = false
             views.deviceListHeaderScanQrCodeButton.isVisible = false
             views.deviceListHeaderShowQrCodeButton.isVisible = false

From 4c7c86100fc90175e243d027e1356788e07e2411 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 02:04:08 +0100
Subject: [PATCH 166/400] Fix logic for showing confirm button

---
 .../vector/app/features/login/qr/QrCodeLoginStatusFragment.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 1c0841aa11..4250431fcf 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -113,7 +113,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     }
 
     private fun handleConnectionEstablished(connectionStatus: QrCodeLoginConnectionStatus.Connected, loginType: QrCodeLoginType) {
-        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = true
+        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = loginType == QrCodeLoginType.LINK_A_DEVICE
         views.qrCodeLoginStatusLoadingLayout.isVisible = false
         views.qrCodeLoginStatusHeaderView.isVisible = true
         views.qrCodeLoginStatusSecurityCode.isVisible = true

From efa70fa0ff92b56d15f638dbd240a050f9b3ff25 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Thu, 13 Oct 2022 21:48:33 +0100
Subject: [PATCH 167/400] Revert "Retry scanning if not a QR code"

This reverts commit 87956e943897333f3f7e9be9a6e59d9a7f0c4547.
---
 .../app/features/login/qr/QrCodeLoginInstructionsFragment.kt    | 2 --
 1 file changed, 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index 17b4ff2409..ae3ba9574b 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -83,7 +83,6 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
         if (activityResult.resultCode == Activity.RESULT_OK) {
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
-            Timber.d("Scanned QR code: $scannedQrCode $wasQrCode")
 
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
@@ -99,6 +98,5 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     private fun onQrCodeScannerFailed() {
         Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
-        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 }

From d72371906e08b040e57020dc63eabac48eaf73e6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 01:45:03 +0100
Subject: [PATCH 168/400] Add flag to allow QR login on all servers + split
 flag for showing in device manager

---
 .../debug/features/DebugFeaturesStateFactory.kt        | 10 ++++++++++
 .../app/features/debug/features/DebugVectorFeatures.kt |  8 ++++++++
 .../main/java/im/vector/app/features/VectorFeatures.kt |  4 ++++
 .../app/features/onboarding/OnboardingViewModel.kt     |  9 +++++++++
 .../devices/v2/VectorSettingsDevicesFragment.kt        |  2 +-
 5 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 3a302feba0..1d8171a9e5 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -95,6 +95,16 @@ class DebugFeaturesStateFactory @Inject constructor(
                                 key = DebugFeatureKeys.qrCodeLoginEnabled,
                                 factory = VectorFeatures::isQrCodeLoginEnabled
                         ),
+                        createBooleanFeature(
+                                label = "Allow QR Code Login for all servers",
+                                key = DebugFeatureKeys.allowQrCodeLoginForAllServers,
+                                factory = VectorFeatures::allowQrCodeLoginForAllServers
+                        ),
+                        createBooleanFeature(
+                                label = "Show QR Code Login in Device Manager",
+                                key = DebugFeatureKeys.allowReciprocateQrCodeLogin,
+                                factory = VectorFeatures::allowReciprocateQrCodeLogin
+                        ),
                         createBooleanFeature(
                                 label = "Enable Voice Broadcast",
                                 key = DebugFeatureKeys.voiceBroadcastEnabled,
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index b5ffa28db7..701f2dcab8 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -82,6 +82,12 @@ class DebugVectorFeatures(
     override fun isQrCodeLoginEnabled() = read(DebugFeatureKeys.qrCodeLoginEnabled)
             ?: vectorFeatures.isQrCodeLoginEnabled()
 
+    override fun allowQrCodeLoginForAllServers() = read(DebugFeatureKeys.allowQrCodeLoginForAllServers)
+            ?: vectorFeatures.allowQrCodeLoginForAllServers()
+
+    override fun allowReciprocateQrCodeLogin() = read(DebugFeatureKeys.allowReciprocateQrCodeLogin)
+            ?: vectorFeatures.allowReciprocateQrCodeLogin()
+
     override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
             ?: vectorFeatures.isVoiceBroadcastEnabled()
 
@@ -147,5 +153,7 @@ object DebugFeatureKeys {
     val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled")
     val newDeviceManagementEnabled = booleanPreferencesKey("new-device-management-enabled")
     val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled")
+    val allowQrCodeLoginForAllServers = booleanPreferencesKey("allow-qr-code-login-for-all-servers")
+    val allowReciprocateQrCodeLogin = booleanPreferencesKey("allow-reciprocate-qr-code-login")
     val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
 }
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index 62eb0523b0..061276ba21 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -42,6 +42,8 @@ interface VectorFeatures {
     fun isNewAppLayoutFeatureEnabled(): Boolean
     fun isNewDeviceManagementEnabled(): Boolean
     fun isQrCodeLoginEnabled(): Boolean
+    fun allowQrCodeLoginForAllServers(): Boolean
+    fun allowReciprocateQrCodeLogin(): Boolean
     fun isVoiceBroadcastEnabled(): Boolean
 }
 
@@ -60,5 +62,7 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun isNewAppLayoutFeatureEnabled(): Boolean = true
     override fun isNewDeviceManagementEnabled(): Boolean = false
     override fun isQrCodeLoginEnabled(): Boolean = false
+    override fun allowQrCodeLoginForAllServers(): Boolean = false
+    override fun allowReciprocateQrCodeLogin(): Boolean = false
     override fun isVoiceBroadcastEnabled(): Boolean = false
 }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 03cf2f43e6..8e64948c6a 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -124,7 +124,16 @@ class OnboardingViewModel @AssistedInject constructor(
                         canLoginWithQrCode = false
                 )
             }
+        } else if (vectorFeatures.allowQrCodeLoginForAllServers()) {
+            // allow for all servers
+            setState {
+                copy(
+                        canLoginWithQrCode = true
+                )
+            }
         } else {
+            // check if selected server supports MSC3882 first
+            // FIXME: this should be checking the selected homeserver not defaultHomeserverUrl
             homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
                 val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
                 setState {
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index 48f66cfc75..7e6da851dd 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -158,7 +158,7 @@ class VectorSettingsDevicesFragment :
     }
 
     private fun initQrLoginView() {
-        if (!vectorFeatures.isQrCodeLoginEnabled()) {
+        if (!vectorFeatures.allowReciprocateQrCodeLogin()) {
             views.deviceListHeaderSignInWithQrCode.isVisible = false
             views.deviceListHeaderScanQrCodeButton.isVisible = false
             views.deviceListHeaderShowQrCodeButton.isVisible = false

From de4232dff519b05063e0e671789802bbd96830cd Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 02:04:08 +0100
Subject: [PATCH 169/400] Fix logic for showing confirm button

---
 .../vector/app/features/login/qr/QrCodeLoginStatusFragment.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 5451a16b44..05ed070915 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -115,7 +115,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     }
 
     private fun handleConnectionEstablished(connectionStatus: QrCodeLoginConnectionStatus.Connected, loginType: QrCodeLoginType) {
-        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = true
+        views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = loginType == QrCodeLoginType.LINK_A_DEVICE
         views.qrCodeLoginStatusLoadingLayout.isVisible = false
         views.qrCodeLoginStatusHeaderView.isVisible = true
         views.qrCodeLoginStatusSecurityCode.isVisible = true

From b6797272460d1528e7883c0d9305a38d6ee4667e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 14 Oct 2022 09:48:15 +0200
Subject: [PATCH 170/400] Bump dokka-gradle-plugin from 1.7.10 to 1.7.20
 (#7329)

Bumps dokka-gradle-plugin from 1.7.10 to 1.7.20.

---
updated-dependencies:
- dependency-name: org.jetbrains.dokka:dokka-gradle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index e474b68531..1121ed5508 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ buildscript {
         classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
         classpath "com.likethesalad.android:stem-plugin:2.2.2"
         classpath 'org.owasp:dependency-check-gradle:7.2.1'
-        classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10"
+        classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20"
         classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
         classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
         classpath 'app.cash.paparazzi:paparazzi-gradle-plugin:1.1.0'

From 81ef1415dc43d291afc96b53e0dad8bfbc2ccdcf Mon Sep 17 00:00:00 2001
From: Jorge Martin Espinosa <jorgem@element.io>
Date: Fri, 14 Oct 2022 09:59:43 +0200
Subject: [PATCH 171/400] Apply design changes to WYSIWYG editor (#7354)

* Apply design changes to WYSIWYG editor

* Add changelog

* Remove unneeded attribute in RichTextComposerLayout

* Fix lint issues

* Fix style naming issue.

* Change rich text editor border radius depending on the number of lines.

* Replace standby voice recorder icon.
---
 changelog.d/7354.misc                         |  1 +
 .../src/main/res/values/strings.xml           |  8 ++-
 .../ui-styles/src/main/res/values/colors.xml  |  5 ++
 .../ui-styles/src/main/res/values/dimens.xml  |  3 +-
 .../src/main/res/values/styles_edit_text.xml  | 12 ++++-
 .../src/main/res/values/theme_dark.xml        |  3 ++
 .../src/main/res/values/theme_light.xml       |  3 ++
 vector-app/proguard-rules.pro                 |  6 ++-
 .../detail/composer/RichTextComposerLayout.kt | 51 ++++++++++++++++---
 .../composer/voice/VoiceMessageViews.kt       |  2 +-
 .../color/selector_rich_text_menu_icon.xml    |  8 +++
 .../bg_composer_rich_edit_text_expanded.xml   | 13 +++++
 ...bg_composer_rich_edit_text_single_line.xml | 13 +++++
 .../res/drawable/bg_rich_text_menu_button.xml | 18 +++++++
 .../src/main/res/drawable/ic_microphone.xml   |  9 ++++
 vector/src/main/res/drawable/ic_voice_mic.xml | 12 -----
 .../res/layout/composer_rich_text_layout.xml  |  6 ++-
 ...ich_text_layout_constraint_set_compact.xml | 30 ++++++-----
 ...ch_text_layout_constraint_set_expanded.xml | 28 +++++-----
 .../res/layout/view_rich_text_menu_button.xml | 11 ++--
 .../layout/view_voice_message_recorder.xml    |  4 +-
 21 files changed, 188 insertions(+), 58 deletions(-)
 create mode 100644 changelog.d/7354.misc
 create mode 100644 vector/src/main/res/color/selector_rich_text_menu_icon.xml
 create mode 100644 vector/src/main/res/drawable/bg_composer_rich_edit_text_expanded.xml
 create mode 100644 vector/src/main/res/drawable/bg_composer_rich_edit_text_single_line.xml
 create mode 100644 vector/src/main/res/drawable/bg_rich_text_menu_button.xml
 create mode 100644 vector/src/main/res/drawable/ic_microphone.xml
 delete mode 100644 vector/src/main/res/drawable/ic_voice_mic.xml

diff --git a/changelog.d/7354.misc b/changelog.d/7354.misc
new file mode 100644
index 0000000000..0e146a8e02
--- /dev/null
+++ b/changelog.d/7354.misc
@@ -0,0 +1 @@
+Update WYSIWYG editor designs.
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 47d3d3bda5..c164b0a519 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -447,7 +447,7 @@
     <string name="labs_enable_deferred_dm_summary">Create DM only on first message</string>
 
     <string name="labs_enable_rich_text_editor_title">Enable rich text editor</string>
-    <string name="labs_enable_rich_text_editor_summary">Use a rich text editor to send formatted messages</string>
+    <string name="labs_enable_rich_text_editor_summary">Try out the rich text editor (plain text mode coming soon)</string>
 
     <!-- Home fragment -->
     <string name="invitations_header">Invites</string>
@@ -3362,4 +3362,10 @@
     <string name="onboarding_new_app_layout_feedback_message">Tap top right to see the option to feedback.</string>
     <string name="onboarding_new_app_layout_button_try">Try it out</string>
 
+    <!-- WYSIWYG Composer -->
+    <string name="rich_text_editor_format_bold">Apply bold format</string>
+    <string name="rich_text_editor_format_italic">Apply italic format</string>
+    <string name="rich_text_editor_format_strikethrough">Apply strikethrough format</string>
+    <string name="rich_text_editor_format_underline">Apply underline format</string>
+
 </resources>
diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml
index f4384adb40..85646adb42 100644
--- a/library/ui-styles/src/main/res/values/colors.xml
+++ b/library/ui-styles/src/main/res/values/colors.xml
@@ -152,4 +152,9 @@
     <color name="vctr_badge_color_border_light">@color/palette_white</color>
     <color name="vctr_badge_color_border_dark">@color/palette_black_950</color>
 
+    <!-- WYSIWYG Colors -->
+    <attr name="vctr_rich_text_editor_menu_button_background" format="color" />
+    <color name="vctr_rich_text_editor_menu_button_background_light">#EEF8F4</color>
+    <color name="vctr_rich_text_editor_menu_button_background_dark">#1D292A</color>
+
 </resources>
diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml
index 0fb03f0ea3..52d16eae7d 100644
--- a/library/ui-styles/src/main/res/values/dimens.xml
+++ b/library/ui-styles/src/main/res/values/dimens.xml
@@ -47,7 +47,8 @@
     <dimen name="composer_min_height">56dp</dimen>
     <dimen name="composer_attachment_size">52dp</dimen>
     <dimen name="composer_attachment_margin">1dp</dimen>
-
+    <dimen name="rich_text_composer_corner_radius_single_line">28dp</dimen>
+    <dimen name="rich_text_composer_corner_radius_expanded">14dp</dimen>
 
     <dimen name="chat_bubble_margin_start">28dp</dimen>
     <dimen name="chat_bubble_margin_end">6dp</dimen>
diff --git a/library/ui-styles/src/main/res/values/styles_edit_text.xml b/library/ui-styles/src/main/res/values/styles_edit_text.xml
index 8de548dd03..b640fc49d9 100644
--- a/library/ui-styles/src/main/res/values/styles_edit_text.xml
+++ b/library/ui-styles/src/main/res/values/styles_edit_text.xml
@@ -11,4 +11,14 @@
         <item name="android:textColor">?vctr_message_text_color</item>
     </style>
 
-</resources>
\ No newline at end of file
+    <style name="Widget.Vector.EditText.RichTextComposer" parent="Widget.AppCompat.EditText">
+        <item name="android:background">@android:color/transparent</item>
+        <item name="android:inputType">textCapSentences|textMultiLine</item>
+        <item name="android:maxLines">12</item>
+        <item name="android:minHeight">20dp</item>
+        <item name="android:padding">0dp</item>
+        <item name="android:textSize">15sp</item>
+        <item name="android:textColor">?vctr_message_text_color</item>
+    </style>
+
+</resources>
diff --git a/library/ui-styles/src/main/res/values/theme_dark.xml b/library/ui-styles/src/main/res/values/theme_dark.xml
index 9f4e5c1e28..d5aaa88ab8 100644
--- a/library/ui-styles/src/main/res/values/theme_dark.xml
+++ b/library/ui-styles/src/main/res/values/theme_dark.xml
@@ -152,6 +152,9 @@
 
         <!-- Material 3 -->
         <item name="collapsingToolbarLayoutMediumSize">@dimen/collapsing_toolbar_layout_medium_size</item>
+
+        <!-- WYSIWYG Editor -->
+        <item name="vctr_rich_text_editor_menu_button_background">@color/vctr_rich_text_editor_menu_button_background_dark</item>
     </style>
 
     <style name="Theme.Vector.Dark" parent="Base.Theme.Vector.Dark" />
diff --git a/library/ui-styles/src/main/res/values/theme_light.xml b/library/ui-styles/src/main/res/values/theme_light.xml
index c8182abecc..1978db9139 100644
--- a/library/ui-styles/src/main/res/values/theme_light.xml
+++ b/library/ui-styles/src/main/res/values/theme_light.xml
@@ -153,6 +153,9 @@
 
         <!-- Material 3 -->
         <item name="collapsingToolbarLayoutMediumSize">@dimen/collapsing_toolbar_layout_medium_size</item>
+
+        <!-- WYSIWYG Editor -->
+        <item name="vctr_rich_text_editor_menu_button_background">@color/vctr_rich_text_editor_menu_button_background_light</item>
     </style>
 
     <style name="Theme.Vector.Light" parent="Base.Theme.Vector.Light" />
diff --git a/vector-app/proguard-rules.pro b/vector-app/proguard-rules.pro
index 7fd72ba895..57559bf7b4 100644
--- a/vector-app/proguard-rules.pro
+++ b/vector-app/proguard-rules.pro
@@ -75,4 +75,8 @@
 
 -keep class org.bouncycastle.** { *; }
 -keepnames class org.bouncycastle.** { *; }
--dontwarn org.bouncycastle.**
\ No newline at end of file
+-dontwarn org.bouncycastle.**
+
+# JNA
+-keep class com.sun.jna.** { *; }
+-keep class * implements com.sun.jna.** { *; }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt
index 76bdcfc9a8..07b7d151ad 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/RichTextComposerLayout.kt
@@ -27,6 +27,7 @@ import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.core.text.toSpannable
@@ -42,7 +43,10 @@ import im.vector.app.core.animations.SimpleTransitionListener
 import im.vector.app.core.extensions.setTextIfDifferent
 import im.vector.app.databinding.ComposerRichTextLayoutBinding
 import im.vector.app.databinding.ViewRichTextMenuButtonBinding
+import io.element.android.wysiwyg.EditorEditText
 import io.element.android.wysiwyg.InlineFormat
+import uniffi.wysiwyg_composer.ComposerAction
+import uniffi.wysiwyg_composer.MenuState
 
 class RichTextComposerLayout @JvmOverloads constructor(
         context: Context,
@@ -91,10 +95,18 @@ class RichTextComposerLayout @JvmOverloads constructor(
         collapse(false)
 
         views.composerEditText.addTextChangedListener(object : TextWatcher {
+            private var previousTextWasExpanded = false
+
             override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
             override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
             override fun afterTextChanged(s: Editable) {
                 callback?.onTextChanged(s)
+
+                val isExpanded = s.lines().count() > 1
+                if (previousTextWasExpanded != isExpanded) {
+                    updateTextFieldBorder(isExpanded)
+                }
+                previousTextWasExpanded = isExpanded
             }
         })
 
@@ -116,32 +128,57 @@ class RichTextComposerLayout @JvmOverloads constructor(
     }
 
     private fun setupRichTextMenu() {
-        addRichTextMenuItem(R.drawable.ic_composer_bold, "Bold") {
+        addRichTextMenuItem(R.drawable.ic_composer_bold, R.string.rich_text_editor_format_bold, ComposerAction.Bold) {
             views.composerEditText.toggleInlineFormat(InlineFormat.Bold)
         }
-        addRichTextMenuItem(R.drawable.ic_composer_italic, "Italic") {
+        addRichTextMenuItem(R.drawable.ic_composer_italic, R.string.rich_text_editor_format_italic, ComposerAction.Italic) {
             views.composerEditText.toggleInlineFormat(InlineFormat.Italic)
         }
-        addRichTextMenuItem(R.drawable.ic_composer_underlined, "Underline") {
+        addRichTextMenuItem(R.drawable.ic_composer_underlined, R.string.rich_text_editor_format_underline, ComposerAction.Underline) {
             views.composerEditText.toggleInlineFormat(InlineFormat.Underline)
         }
-        addRichTextMenuItem(R.drawable.ic_composer_strikethrough, "Strikethrough") {
+        addRichTextMenuItem(R.drawable.ic_composer_strikethrough, R.string.rich_text_editor_format_strikethrough, ComposerAction.StrikeThrough) {
             views.composerEditText.toggleInlineFormat(InlineFormat.StrikeThrough)
         }
+
+        views.composerEditText.menuStateChangedListener = EditorEditText.OnMenuStateChangedListener { state ->
+            if (state is MenuState.Update) {
+                updateMenuStateFor(ComposerAction.Bold, state)
+                updateMenuStateFor(ComposerAction.Italic, state)
+                updateMenuStateFor(ComposerAction.Underline, state)
+                updateMenuStateFor(ComposerAction.StrikeThrough, state)
+            }
+        }
     }
 
-    private fun addRichTextMenuItem(@DrawableRes iconId: Int, description: String, action: () -> Unit) {
+    private fun addRichTextMenuItem(@DrawableRes iconId: Int, @StringRes description: Int, action: ComposerAction, onClick: () -> Unit) {
         val inflater = LayoutInflater.from(context)
         val button = ViewRichTextMenuButtonBinding.inflate(inflater, views.richTextMenu, true)
+        button.root.tag = action
         with(button.root) {
-            contentDescription = description
+            contentDescription = resources.getString(description)
             setImageResource(iconId)
             setOnClickListener {
-                action()
+                onClick()
             }
         }
     }
 
+    private fun updateMenuStateFor(action: ComposerAction, menuState: MenuState.Update) {
+        val button = findViewWithTag<ImageButton>(action) ?: return
+        button.isEnabled = !menuState.disabledActions.contains(action)
+        button.isSelected = menuState.reversedActions.contains(action)
+    }
+
+    private fun updateTextFieldBorder(isExpanded: Boolean) {
+        val borderResource = if (isExpanded) {
+            R.drawable.bg_composer_rich_edit_text_expanded
+        } else {
+            R.drawable.bg_composer_rich_edit_text_single_line
+        }
+        views.composerEditTextOuterBorder.setBackgroundResource(borderResource)
+    }
+
     override fun replaceFormattedContent(text: CharSequence) {
         views.composerEditText.setHtml(text.toString())
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt
index 1eac3b440a..cd41219371 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt
@@ -260,7 +260,7 @@ class VoiceMessageViews(
 
     fun resetMicButtonUi() {
         views.voiceMessageMicButton.isVisible = true
-        views.voiceMessageMicButton.setImageResource(R.drawable.ic_voice_mic)
+        views.voiceMessageMicButton.setImageResource(R.drawable.ic_microphone)
         views.voiceMessageMicButton.setAttributeBackground(android.R.attr.selectableItemBackgroundBorderless)
         views.voiceMessageMicButton.updateLayoutParams<ViewGroup.MarginLayoutParams> {
             if (rtlXMultiplier == -1) {
diff --git a/vector/src/main/res/color/selector_rich_text_menu_icon.xml b/vector/src/main/res/color/selector_rich_text_menu_icon.xml
new file mode 100644
index 0000000000..0d8789d6c5
--- /dev/null
+++ b/vector/src/main/res/color/selector_rich_text_menu_icon.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="?attr/vctr_content_quinary" />
+    <item android:state_pressed="true" android:color="?attr/colorSecondary" />
+    <item android:state_hovered="true" android:color="?attr/colorSecondary" />
+    <item android:state_selected="true" android:color="?attr/colorSecondary" />
+    <item android:color="?attr/vctr_content_tertiary" />
+</selector>
diff --git a/vector/src/main/res/drawable/bg_composer_rich_edit_text_expanded.xml b/vector/src/main/res/drawable/bg_composer_rich_edit_text_expanded.xml
new file mode 100644
index 0000000000..26d997e7db
--- /dev/null
+++ b/vector/src/main/res/drawable/bg_composer_rich_edit_text_expanded.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <solid android:color="@android:color/transparent" />
+
+    <stroke
+        android:width="1dp"
+        android:color="?vctr_content_quaternary" />
+
+    <corners android:radius="@dimen/rich_text_composer_corner_radius_expanded" />
+
+</shape>
diff --git a/vector/src/main/res/drawable/bg_composer_rich_edit_text_single_line.xml b/vector/src/main/res/drawable/bg_composer_rich_edit_text_single_line.xml
new file mode 100644
index 0000000000..7e2745a137
--- /dev/null
+++ b/vector/src/main/res/drawable/bg_composer_rich_edit_text_single_line.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <solid android:color="@android:color/transparent" />
+
+    <stroke
+        android:width="1dp"
+        android:color="?vctr_content_quaternary" />
+
+    <corners android:radius="@dimen/rich_text_composer_corner_radius_single_line" />
+
+</shape>
diff --git a/vector/src/main/res/drawable/bg_rich_text_menu_button.xml b/vector/src/main/res/drawable/bg_rich_text_menu_button.xml
new file mode 100644
index 0000000000..7ac477e9f3
--- /dev/null
+++ b/vector/src/main/res/drawable/bg_rich_text_menu_button.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_hovered="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="8dp" />
+            <solid android:color="?attr/vctr_rich_text_editor_menu_button_background" />
+        </shape>
+    </item>
+    <item android:state_selected="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="8dp" />
+            <solid android:color="?attr/vctr_rich_text_editor_menu_button_background" />
+        </shape>
+    </item>
+    <item>
+        <ripple android:color="?attr/vctr_rich_text_editor_menu_button_background" />
+    </item>
+</selector>
diff --git a/vector/src/main/res/drawable/ic_microphone.xml b/vector/src/main/res/drawable/ic_microphone.xml
new file mode 100644
index 0000000000..270fe456fa
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_microphone.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,15C13.66,15 14.99,13.66 14.99,12L15,6C15,4.34 13.66,3 12,3C10.34,3 9,4.34 9,6V12C9,13.66 10.34,15 12,15ZM18.08,12C17.66,12 17.31,12.3 17.25,12.71C16.88,15.32 14.53,17.1 12,17.1C9.47,17.1 7.12,15.33 6.75,12.71C6.69,12.3 6.33,12 5.92,12C5.4,12 5,12.46 5.07,12.97C5.53,15.94 8.03,18.27 11,18.72V21C11,21.55 11.45,22 12,22C12.55,22 13,21.55 13,21V18.72C15.96,18.29 18.47,15.94 18.93,12.97C19,12.46 18.6,12 18.08,12Z"
+      android:fillColor="#8D97A5"/>
+</vector>
diff --git a/vector/src/main/res/drawable/ic_voice_mic.xml b/vector/src/main/res/drawable/ic_voice_mic.xml
deleted file mode 100644
index fef5fb9dcf..0000000000
--- a/vector/src/main/res/drawable/ic_voice_mic.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="32dp"
-    android:viewportWidth="32"
-    android:viewportHeight="32">
-  <path
-      android:pathData="M10.8,8.2C10.8,5.3281 13.1282,3 16,3C18.8719,3 21.2,5.3281 21.2,8.2V15.9767C21.2,18.8486 18.8719,21.1767 16,21.1767C13.1282,21.1767 10.8,18.8486 10.8,15.9767V8.2Z"
-      android:fillColor="?vctr_content_tertiary"/>
-  <path
-      android:pathData="M6.8998,14.3167C7.8203,14.3167 8.5665,15.0629 8.5665,15.9834C8.5665,20.0737 11.8818,23.3944 15.98,23.4051C15.9867,23.405 15.9934,23.4049 16.0001,23.4049C16.0068,23.4049 16.0134,23.405 16.0201,23.4051C20.1181,23.3941 23.4332,20.0735 23.4332,15.9834C23.4332,15.0629 24.1793,14.3167 25.0998,14.3167C26.0203,14.3167 26.7665,15.0629 26.7665,15.9834C26.7665,21.3586 22.8201,25.8101 17.6667,26.6103V27.6683C17.6667,28.5888 16.9206,29.335 16.0001,29.335C15.0796,29.335 14.3334,28.5888 14.3334,27.6683V26.6104C9.1798,25.8104 5.2332,21.3587 5.2332,15.9834C5.2332,15.0629 5.9794,14.3167 6.8998,14.3167Z"
-      android:fillColor="?vctr_content_tertiary"/>
-</vector>
diff --git a/vector/src/main/res/layout/composer_rich_text_layout.xml b/vector/src/main/res/layout/composer_rich_text_layout.xml
index 3130061c10..09e4b03887 100644
--- a/vector/src/main/res/layout/composer_rich_text_layout.xml
+++ b/vector/src/main/res/layout/composer_rich_text_layout.xml
@@ -91,6 +91,7 @@
         android:id="@+id/attachmentButton"
         android:layout_width="0dp"
         android:layout_height="0dp"
+        android:paddingTop="2dp"
         android:background="?android:attr/selectableItemBackground"
         android:contentDescription="@string/option_send_files"
         android:src="@drawable/ic_attachment"
@@ -100,16 +101,17 @@
         android:id="@+id/composerEditTextOuterBorder"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:background="@drawable/bg_composer_edit_text" />
+        android:background="@drawable/bg_composer_rich_edit_text_single_line" />
 
     <io.element.android.wysiwyg.EditorEditText
         android:id="@+id/composerEditText"
-        style="@style/Widget.Vector.EditText.Composer"
+        style="@style/Widget.Vector.EditText.RichTextComposer"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:nextFocusLeft="@id/composerEditText"
         android:nextFocusUp="@id/composerEditText"
         tools:hint="@string/room_message_placeholder"
+        tools:text="@tools:sample/lorem/random"
         tools:ignore="MissingConstraints" />
 
     <ImageButton
diff --git a/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_compact.xml b/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_compact.xml
index 585ba2913e..391a4596bf 100644
--- a/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_compact.xml
+++ b/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_compact.xml
@@ -124,28 +124,30 @@
         android:id="@+id/composerEditTextOuterBorder"
         android:layout_width="0dp"
         android:layout_height="0dp"
+        android:minHeight="40dp"
         android:layout_marginTop="8dp"
-        android:layout_marginBottom="8dp"
-        app:layout_constraintBottom_toBottomOf="@id/composerEditText"
-        app:layout_constraintStart_toStartOf="@id/composerEditText"
-        app:layout_constraintEnd_toEndOf="@id/composerEditText"
-        app:layout_constraintTop_toTopOf="@id/composerEditText"
-        app:layout_goneMarginEnd="12dp" />
+        android:layout_marginHorizontal="12dp"
+        android:background="@drawable/bg_composer_rich_edit_text_single_line"
+        app:layout_constraintVertical_bias="0"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/sendButton"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
 
     <io.element.android.wysiwyg.EditorEditText
         android:id="@+id/composerEditText"
-        style="@style/Widget.Vector.EditText.Composer"
+        style="@style/Widget.Vector.EditText.RichTextComposer"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:hint="@string/room_message_placeholder"
         android:nextFocusLeft="@id/composerEditText"
         android:nextFocusUp="@id/composerEditText"
-        android:layout_marginHorizontal="10dp"
-        app:layout_constraintVertical_bias="0"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
+        android:layout_marginHorizontal="12dp"
+        android:layout_marginVertical="10dp"
+        app:layout_constraintBottom_toBottomOf="@id/composerEditTextOuterBorder"
+        app:layout_constraintEnd_toEndOf="@id/composerEditTextOuterBorder"
+        app:layout_constraintStart_toStartOf="@id/composerEditTextOuterBorder"
+        app:layout_constraintTop_toTopOf="@id/composerEditTextOuterBorder"
         tools:text="@tools:sample/lorem/random" />
 
     <ImageButton
@@ -158,7 +160,7 @@
         android:scaleType="center"
         android:src="@drawable/ic_send"
         android:visibility="invisible"
-        app:layout_constraintTop_toBottomOf="@id/composerEditText"
+        app:layout_constraintTop_toBottomOf="@id/composerEditTextOuterBorder"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         tools:ignore="MissingPrefix"
diff --git a/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_expanded.xml b/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_expanded.xml
index f810b12ed1..aa372165b8 100644
--- a/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_expanded.xml
+++ b/vector/src/main/res/layout/composer_rich_text_layout_constraint_set_expanded.xml
@@ -130,33 +130,38 @@
         app:layout_constraintBottom_toBottomOf="@id/sendButton"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@id/sendButton"
+        app:layout_goneMarginBottom="57dp"
         tools:ignore="MissingPrefix" />
 
     <FrameLayout
         android:id="@+id/composerEditTextOuterBorder"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        android:layout_marginStart="8dp"
-        android:layout_marginEnd="8dp"
+        android:minHeight="40dp"
         android:layout_marginTop="8dp"
-        android:layout_marginBottom="8dp"
-        app:layout_constraintBottom_toTopOf="@id/sendButton"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
+        android:layout_marginBottom="0dp"
+        android:layout_marginHorizontal="12dp"
+        android:background="@drawable/bg_composer_rich_edit_text_single_line"
+        app:layout_constraintVertical_bias="0"
         app:layout_constraintTop_toBottomOf="@id/composer_preview_barrier"
-        app:layout_goneMarginEnd="12dp" />
+        app:layout_constraintBottom_toTopOf="@id/sendButton"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
 
     <io.element.android.wysiwyg.EditorEditText
         android:id="@+id/composerEditText"
-        style="@style/Widget.Vector.EditText.Composer"
+        style="@style/Widget.Vector.EditText.RichTextComposer"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
+        android:hint="@string/room_message_placeholder"
         android:nextFocusLeft="@id/composerEditText"
         android:nextFocusUp="@id/composerEditText"
-        app:layout_constraintBottom_toTopOf="@id/sendButton"
+        android:layout_marginHorizontal="12dp"
+        android:layout_marginVertical="10dp"
+        app:layout_constraintBottom_toBottomOf="@id/composerEditTextOuterBorder"
         app:layout_constraintEnd_toEndOf="@id/composerEditTextOuterBorder"
         app:layout_constraintStart_toStartOf="@id/composerEditTextOuterBorder"
-        app:layout_constraintTop_toBottomOf="@id/composer_preview_barrier"
+        app:layout_constraintTop_toTopOf="@id/composerEditTextOuterBorder"
         tools:text="@tools:sample/lorem/random" />
 
     <ImageButton
@@ -169,10 +174,9 @@
         android:scaleType="center"
         android:src="@drawable/ic_send"
         android:visibility="invisible"
+        app:layout_constraintTop_toBottomOf="@id/composerEditTextOuterBorder"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/composerEditText"
-        app:layout_constraintVertical_bias="1"
         tools:ignore="MissingPrefix"
         tools:visibility="visible" />
 
diff --git a/vector/src/main/res/layout/view_rich_text_menu_button.xml b/vector/src/main/res/layout/view_rich_text_menu_button.xml
index a63a01e7c2..24b19c10b5 100644
--- a/vector/src/main/res/layout/view_rich_text_menu_button.xml
+++ b/vector/src/main/res/layout/view_rich_text_menu_button.xml
@@ -1,10 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <ImageButton 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="48dp"
     android:layout_height="48dp"
     android:layout_marginHorizontal="2dp"
-    android:background="@android:color/transparent"
-    android:contentDescription="@string/app_name">
-    <!-- The contentDescription attr is populated programmatically. This is just to fix lint issues. -->
-
-</ImageButton>
+    android:background="@drawable/bg_rich_text_menu_button"
+    app:tint="@color/selector_rich_text_menu_icon"
+    tools:src="@drawable/ic_composer_bold"
+    tools:ignore="ContentDescription" />
diff --git a/vector/src/main/res/layout/view_voice_message_recorder.xml b/vector/src/main/res/layout/view_voice_message_recorder.xml
index f7b5f5da1d..ee20c2eea1 100644
--- a/vector/src/main/res/layout/view_voice_message_recorder.xml
+++ b/vector/src/main/res/layout/view_voice_message_recorder.xml
@@ -23,11 +23,13 @@
         android:id="@+id/voiceMessageMicButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:minWidth="32dp"
+        android:minHeight="32dp"
         android:layout_marginEnd="12dp"
         android:layout_marginBottom="12dp"
         android:background="?android:attr/selectableItemBackground"
         android:contentDescription="@string/a11y_start_voice_message"
-        android:src="@drawable/ic_voice_mic"
+        android:src="@drawable/ic_microphone"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent" />
 

From 989d4a6ed2731ae6169ee70536eac3a3ccbff5f3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 14 Oct 2022 10:37:11 +0200
Subject: [PATCH 172/400] Bump flipper from 0.169.0 to 0.170.0 (#7348)

Bumps `flipper` from 0.169.0 to 0.170.0.

Updates `flipper` from 0.169.0 to 0.170.0
- [Release notes](https://github.com/facebook/flipper/releases)
- [Commits](https://github.com/facebook/flipper/compare/v0.169.0...v0.170.0)

Updates `flipper-network-plugin` from 0.169.0 to 0.170.0
- [Release notes](https://github.com/facebook/flipper/releases)
- [Commits](https://github.com/facebook/flipper/compare/v0.169.0...v0.170.0)

---
updated-dependencies:
- dependency-name: com.facebook.flipper:flipper
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: com.facebook.flipper:flipper-network-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index c3dd64b6a6..baa0994236 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -18,7 +18,7 @@ def markwon = "4.6.2"
 def moshi = "1.14.0"
 def lifecycle = "2.5.1"
 def flowBinding = "1.2.0"
-def flipper = "0.169.0"
+def flipper = "0.170.0"
 def epoxy = "5.0.0"
 def mavericks = "3.0.1"
 def glide = "4.14.2"

From 8fc35ef7928327115ed963ba3e69b2413c2f6aa6 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 14 Oct 2022 11:25:41 +0200
Subject: [PATCH 173/400] Remove unused param

---
 .../devices/DeviceListBottomSheetViewModel.kt                  | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
index 92687e1a37..e6f01ed144 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
@@ -42,7 +42,6 @@ data class DeviceListViewState(
         val userId: String,
         val allowDeviceAction: Boolean,
         val userItem: MatrixItem? = null,
-        val isMine: Boolean = false,
         val memberCrossSigningKey: MXCrossSigningInfo? = null,
         val cryptoDevices: Async<List<CryptoDeviceInfo>> = Loading(),
         val selectedDevice: CryptoDeviceInfo? = null
@@ -70,14 +69,12 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(
                         userId = userId,
                         allowDeviceAction = args.allowDeviceAction,
                         userItem = it,
-                        isMine = userId == session.myUserId
                 )
             } ?: return super.initialState(viewModelContext)
         }
     }
 
     init {
-
         session.flow().liveUserCryptoDevices(initialState.userId)
                 .execute {
                     copy(cryptoDevices = it).also {

From 0a6d620f27d3160d273f6d0a2b5e13936a333d23 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 14 Oct 2022 11:38:24 +0200
Subject: [PATCH 174/400] getUser() can return null more often than before,
 since the SDK will retrieve data asynchronously. So ensure that the initial
 state can always be built.

---
 .../devices/DeviceListBottomSheetViewModel.kt | 28 +++++++++++++------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
index e6f01ed144..eb23c5654e 100644
--- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt
@@ -29,11 +29,13 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.SingletonEntryPoint
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.platform.VectorViewModel
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
 import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
 import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.util.MatrixItem
 import org.matrix.android.sdk.api.util.toMatrixItem
 import org.matrix.android.sdk.flow.flow
@@ -60,17 +62,15 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(
 
     companion object : MavericksViewModelFactory<DeviceListBottomSheetViewModel, DeviceListViewState> by hiltMavericksViewModelFactory() {
 
-        override fun initialState(viewModelContext: ViewModelContext): DeviceListViewState? {
+        override fun initialState(viewModelContext: ViewModelContext): DeviceListViewState {
             val args = viewModelContext.args<DeviceListBottomSheet.Args>()
             val userId = args.userId
             val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
-            return session.getUser(userId)?.toMatrixItem()?.let {
-                DeviceListViewState(
-                        userId = userId,
-                        allowDeviceAction = args.allowDeviceAction,
-                        userItem = it,
-                )
-            } ?: return super.initialState(viewModelContext)
+            return DeviceListViewState(
+                    userId = userId,
+                    allowDeviceAction = args.allowDeviceAction,
+                    userItem = session.getUserOrDefault(userId).toMatrixItem(),
+            )
         }
     }
 
@@ -86,6 +86,16 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(
                 .execute {
                     copy(memberCrossSigningKey = it.invoke()?.getOrNull())
                 }
+
+        updateMatrixItem()
+    }
+
+    private fun updateMatrixItem() {
+        viewModelScope.launch {
+            tryOrNull { session.userService().resolveUser(initialState.userId) }
+                    ?.toMatrixItem()
+                    ?.let { setState { copy(userItem = it) } }
+        }
     }
 
     override fun handle(action: DeviceListAction) {

From 5a2d74443d7be8c969639153554af46d324aa73b Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 14 Oct 2022 12:07:52 +0200
Subject: [PATCH 175/400] Let GetProfileInfoTask store result into DB, except
 when we want to do bulk insertion.

---
 .../session/profile/GetProfileInfoTask.kt       | 17 +++++++++++++++--
 .../session/sync/handler/UpdateUserWorker.kt    | 14 ++++++++++----
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
index 40444edcab..4a20c68caf 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
@@ -17,26 +17,39 @@
 
 package org.matrix.android.sdk.internal.session.profile
 
+import com.zhuinden.monarchy.Monarchy
+import org.matrix.android.sdk.api.session.user.model.User
 import org.matrix.android.sdk.api.util.JsonDict
 import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
+import org.matrix.android.sdk.internal.session.user.UserEntityFactory
 import org.matrix.android.sdk.internal.task.Task
+import org.matrix.android.sdk.internal.util.awaitTransaction
 import javax.inject.Inject
 
 internal abstract class GetProfileInfoTask : Task<GetProfileInfoTask.Params, JsonDict> {
     data class Params(
-            val userId: String
+            val userId: String,
+            val storeInDatabase: Boolean = true,
     )
 }
 
 internal class DefaultGetProfileInfoTask @Inject constructor(
         private val profileAPI: ProfileAPI,
-        private val globalErrorReceiver: GlobalErrorReceiver
+        private val globalErrorReceiver: GlobalErrorReceiver,
+        private val monarchy: Monarchy,
 ) : GetProfileInfoTask() {
 
     override suspend fun execute(params: Params): JsonDict {
         return executeRequest(globalErrorReceiver) {
             profileAPI.getProfile(params.userId)
+        }.also { user ->
+            if (params.storeInDatabase) {
+                // Insert into DB
+                monarchy.awaitTransaction {
+                    it.insertOrUpdate(UserEntityFactory.create(User.fromJson(params.userId, user)))
+                }
+            }
         }
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UpdateUserWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UpdateUserWorker.kt
index 1f840a82d5..1ee2fc4802 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UpdateUserWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/UpdateUserWorker.kt
@@ -71,10 +71,16 @@ internal class UpdateUserWorker(context: Context, params: WorkerParameters, sess
                 ?.saveLocally()
     }
 
-    private suspend fun fetchUsers(userIdsToFetch: Collection<String>) = userIdsToFetch.mapNotNull {
-        tryOrNull {
-            val profileJson = getProfileInfoTask.execute(GetProfileInfoTask.Params(it))
-            User.fromJson(it, profileJson)
+    private suspend fun fetchUsers(userIdsToFetch: Collection<String>): List<User> {
+        return userIdsToFetch.mapNotNull { userId ->
+            tryOrNull {
+                val profileJson = getProfileInfoTask.execute(GetProfileInfoTask.Params(
+                        userId = userId,
+                        // Bulk insert later, so tell the task not to store the User.
+                        storeInDatabase = false,
+                ))
+                User.fromJson(userId, profileJson)
+            }
         }
     }
 

From 032fa37b64486ad4ad2bd8846fbcd811f9e6b61c Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 14 Oct 2022 12:13:21 +0200
Subject: [PATCH 176/400] Create UserDataSource.getUserOrDefault().

---
 .../matrix/android/sdk/internal/session/user/UserDataSource.kt  | 2 ++
 .../sdk/internal/session/widgets/helper/WidgetFactory.kt        | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt
index f9feb04e97..98108008fe 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt
@@ -66,6 +66,8 @@ internal class UserDataSource @Inject constructor(
         }
     }
 
+    fun getUserOrDefault(userId: String): User = getUser(userId) ?: User(userId)
+
     fun getUserLive(userId: String): LiveData<Optional<User>> {
         val liveData = monarchy.findAllMappedWithChanges(
                 { UserEntity.where(it, userId) },
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
index 8bd61a7bdf..8ede63c365 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
@@ -74,7 +74,7 @@ internal class WidgetFactory @Inject constructor(
     // Ref: https://github.com/matrix-org/matrix-widget-api/blob/master/src/templating/url-template.ts#L29-L33
     fun computeURL(widget: Widget, isLightTheme: Boolean): String? {
         var computedUrl = widget.widgetContent.url ?: return null
-        val myUser = userDataSource.getUser(userId) ?: User(userId)
+        val myUser = userDataSource.getUserOrDefault(userId)
 
         val keyValue = widget.widgetContent.data.mapKeys { "\$${it.key}" }.toMutableMap()
 

From 7699628959c32b3743ca039f3e5c6f89abffc7f3 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 14 Oct 2022 12:25:55 +0200
Subject: [PATCH 177/400] Fix other potential issue when using
 Session.getUser()

---
 .../timeline/helper/LocationPinProvider.kt    |  8 +++---
 .../app/features/html/PillsPostProcessor.kt   |  4 +--
 .../location/LocationSharingViewModel.kt      |  4 +--
 .../map/UserLiveLocationViewStateMapper.kt    | 26 +++++++++----------
 .../notifications/NotifiableEventResolver.kt  |  4 +--
 .../NotificationDrawerManager.kt              |  8 +++---
 .../signout/soft/SoftLogoutViewModel.kt       |  6 +++--
 .../invite/SpaceInviteBottomSheetViewModel.kt |  6 ++---
 .../usercode/UserCodeSharedViewModel.kt       |  6 ++---
 9 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt
index 8ef910c931..7f276f2f73 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/LocationPinProvider.kt
@@ -29,7 +29,7 @@ import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.glide.GlideApp
 import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.features.home.AvatarRenderer
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.util.toMatrixItem
 import timber.log.Timber
 import javax.inject.Inject
@@ -67,9 +67,9 @@ class LocationPinProvider @Inject constructor(
 
         activeSessionHolder
                 .getActiveSession()
-                .getUser(userId)
-                ?.toMatrixItem()
-                ?.let { userItem ->
+                .getUserOrDefault(userId)
+                .toMatrixItem()
+                .let { userItem ->
                     val size = dimensionConverter.dpToPx(44)
                     val bgTintColor = matrixItemColorProvider.getColor(userItem)
                     avatarRenderer.render(glideRequests, userItem, object : CustomTarget<Drawable>(size, size) {
diff --git a/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt b/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt
index 5f20b7278e..85cfb76ff7 100644
--- a/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt
+++ b/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt
@@ -27,7 +27,7 @@ import im.vector.app.core.glide.GlideApp
 import im.vector.app.features.home.AvatarRenderer
 import io.noties.markwon.core.spans.LinkSpan
 import org.matrix.android.sdk.api.session.getRoomSummary
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.session.permalinks.PermalinkData
 import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
@@ -101,7 +101,7 @@ class PillsPostProcessor @AssistedInject constructor(
 
     private fun PermalinkData.UserLink.toMatrixItem(roomId: String?): MatrixItem? =
             if (roomId == null) {
-                sessionHolder.getSafeActiveSession()?.getUser(userId)?.toMatrixItem()
+                sessionHolder.getSafeActiveSession()?.getUserOrDefault(userId)?.toMatrixItem()
             } else {
                 sessionHolder.getSafeActiveSession()?.roomService()?.getRoomMember(userId, roomId)?.toMatrixItem()
             }
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt
index 28e37a38eb..4c7abd99b8 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt
@@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.getRoom
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
 import org.matrix.android.sdk.api.util.toMatrixItem
 import timber.log.Timber
@@ -101,7 +101,7 @@ class LocationSharingViewModel @AssistedInject constructor(
     }
 
     private fun setUserItem() {
-        setState { copy(userItem = session.getUser(session.myUserId)?.toMatrixItem()) }
+        setState { copy(userItem = session.getUserOrDefault(session.myUserId).toMatrixItem()) }
     }
 
     private fun updatePin(isUserPin: Boolean? = true) {
diff --git a/vector/src/main/java/im/vector/app/features/location/live/map/UserLiveLocationViewStateMapper.kt b/vector/src/main/java/im/vector/app/features/location/live/map/UserLiveLocationViewStateMapper.kt
index 77f8c30fd3..f4f4a462ce 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/map/UserLiveLocationViewStateMapper.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/map/UserLiveLocationViewStateMapper.kt
@@ -20,7 +20,7 @@ import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
 import im.vector.app.features.location.toLocationData
 import kotlinx.coroutines.suspendCancellableCoroutine
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationShareAggregatedSummary
 import org.matrix.android.sdk.api.util.toMatrixItem
 import javax.inject.Inject
@@ -45,19 +45,17 @@ class UserLiveLocationViewStateMapper @Inject constructor(
                     else -> {
                         locationPinProvider.create(userId) { pinDrawable ->
                             val session = activeSessionHolder.getActiveSession()
-                            session.getUser(userId)?.toMatrixItem()?.let { matrixItem ->
-                                val locationTimestampMillis = liveLocationShareAggregatedSummary.lastLocationDataContent?.getBestTimestampMillis()
-                                val viewState = UserLiveLocationViewState(
-                                        matrixItem = matrixItem,
-                                        pinDrawable = pinDrawable,
-                                        locationData = locationData,
-                                        endOfLiveTimestampMillis = liveLocationShareAggregatedSummary.endOfLiveTimestampMillis,
-                                        locationTimestampMillis = locationTimestampMillis,
-                                        showStopSharingButton = userId == session.myUserId
-                                )
-                                continuation.resume(viewState) {
-                                    // do nothing on cancellation
-                                }
+                            val locationTimestampMillis = liveLocationShareAggregatedSummary.lastLocationDataContent?.getBestTimestampMillis()
+                            val viewState = UserLiveLocationViewState(
+                                    matrixItem = session.getUserOrDefault(userId).toMatrixItem(),
+                                    pinDrawable = pinDrawable,
+                                    locationData = locationData,
+                                    endOfLiveTimestampMillis = liveLocationShareAggregatedSummary.endOfLiveTimestampMillis,
+                                    locationTimestampMillis = locationTimestampMillis,
+                                    showStopSharingButton = userId == session.myUserId
+                            )
+                            continuation.resume(viewState) {
+                                // do nothing on cancellation
                             }
                         }
                     }
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt
index 4ee7da4b64..ba1d5c7f6f 100644
--- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt
@@ -38,7 +38,7 @@ import org.matrix.android.sdk.api.session.events.model.supportsNotification
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.getRoom
 import org.matrix.android.sdk.api.session.getRoomSummary
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.session.room.getTimelineEvent
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
@@ -112,7 +112,7 @@ class NotifiableEventResolver @Inject constructor(
         val notificationAction = actions.toNotificationAction()
 
         return if (notificationAction.shouldNotify) {
-            val user = session.getUser(event.senderId!!) ?: return null
+            val user = session.getUserOrDefault(event.senderId!!)
 
             val timelineEvent = TimelineEvent(
                     root = event,
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt
index 5f43ff6b90..2623045cf3 100644
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt
@@ -27,7 +27,7 @@ import im.vector.app.features.displayname.getBestName
 import im.vector.app.features.settings.VectorPreferences
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.content.ContentUrlResolver
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.util.toMatrixItem
 import timber.log.Timber
 import javax.inject.Inject
@@ -186,11 +186,11 @@ class NotificationDrawerManager @Inject constructor(
     }
 
     private fun renderEvents(session: Session, eventsToRender: List<ProcessedEvent<NotifiableEvent>>) {
-        val user = session.getUser(session.myUserId)
+        val user = session.getUserOrDefault(session.myUserId)
         // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
-        val myUserDisplayName = user?.toMatrixItem()?.getBestName() ?: session.myUserId
+        val myUserDisplayName = user.toMatrixItem().getBestName()
         val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(
-                contentUrl = user?.avatarUrl,
+                contentUrl = user.avatarUrl,
                 width = avatarSize,
                 height = avatarSize,
                 method = ContentUrlResolver.ThumbnailMethod.SCALE
diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
index f3e2f82edc..117c298878 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt
@@ -32,6 +32,7 @@ import im.vector.app.core.di.SingletonEntryPoint
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.extensions.hasUnsavedKeys
 import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.features.displayname.getBestName
 import im.vector.app.features.login.LoginMode
 import im.vector.app.features.login.toSsoState
 import kotlinx.coroutines.launch
@@ -39,7 +40,8 @@ import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.LoginType
 import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
+import org.matrix.android.sdk.api.util.toMatrixItem
 import timber.log.Timber
 
 class SoftLogoutViewModel @AssistedInject constructor(
@@ -68,7 +70,7 @@ class SoftLogoutViewModel @AssistedInject constructor(
                         homeServerUrl = session.sessionParams.homeServerUrl,
                         userId = userId,
                         deviceId = session.sessionParams.deviceId.orEmpty(),
-                        userDisplayName = session.getUser(userId)?.displayName ?: userId,
+                        userDisplayName = session.getUserOrDefault(userId).toMatrixItem().getBestName(),
                         hasUnsavedKeys = session.hasUnsavedKeys(),
                         loginType = session.sessionParams.loginType,
                 )
diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt
index ea36908dd2..27f194e8d2 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt
@@ -34,7 +34,7 @@ import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.getRoomSummary
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import org.matrix.android.sdk.api.session.room.peeking.PeekResult
@@ -49,7 +49,7 @@ class SpaceInviteBottomSheetViewModel @AssistedInject constructor(
         session.getRoomSummary(initialState.spaceId)?.let { roomSummary ->
             val knownMembers = roomSummary.otherMemberIds.filter {
                 session.roomService().getExistingDirectRoomWithUser(it) != null
-            }.mapNotNull { session.getUser(it) }
+            }.map { session.getUserOrDefault(it) }
             // put one with avatar first, and take 5
             val peopleYouKnow = (knownMembers.filter { it.avatarUrl != null } + knownMembers.filter { it.avatarUrl == null })
                     .take(5)
@@ -57,7 +57,7 @@ class SpaceInviteBottomSheetViewModel @AssistedInject constructor(
             setState {
                 copy(
                         summary = Success(roomSummary),
-                        inviterUser = roomSummary.inviterId?.let { session.getUser(it) }?.let { Success(it) } ?: Uninitialized,
+                        inviterUser = roomSummary.inviterId?.let { session.getUserOrDefault(it) }?.let { Success(it) } ?: Uninitialized,
                         peopleYouKnow = Success(peopleYouKnow)
                 )
             }
diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt
index 8c377cafd5..e76837f182 100644
--- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt
@@ -30,7 +30,7 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.Session
-import org.matrix.android.sdk.api.session.getUser
+import org.matrix.android.sdk.api.session.getUserOrDefault
 import org.matrix.android.sdk.api.session.permalinks.PermalinkData
 import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
 import org.matrix.android.sdk.api.session.user.model.User
@@ -46,10 +46,10 @@ class UserCodeSharedViewModel @AssistedInject constructor(
     companion object : MavericksViewModelFactory<UserCodeSharedViewModel, UserCodeState> by hiltMavericksViewModelFactory()
 
     init {
-        val user = session.getUser(initialState.userId)
+        val user = session.getUserOrDefault(initialState.userId)
         setState {
             copy(
-                    matrixItem = user?.toMatrixItem(),
+                    matrixItem = user.toMatrixItem(),
                     shareLink = session.permalinkService().createPermalink(initialState.userId)
             )
         }

From 8bb5dcd553f866cbbe7bdcf4ceac7f79ed564238 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 14 Oct 2022 12:33:27 +0200
Subject: [PATCH 178/400] Changelog

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

diff --git a/changelog.d/7372.bugfix b/changelog.d/7372.bugfix
new file mode 100644
index 0000000000..e63e00035b
--- /dev/null
+++ b/changelog.d/7372.bugfix
@@ -0,0 +1 @@
+Handle properly when getUser returns null - prefer using getUserOrDefault

From fd2814ea183a823ba5a7091f2723e96d3b695ba7 Mon Sep 17 00:00:00 2001
From: Kat Gerasimova <ekaterinag@element.io>
Date: Fri, 14 Oct 2022 11:49:24 +0100
Subject: [PATCH 179/400] Update issue automation for design

Put only high priority issues in front of the design team, all of which the design team will aim to action to keep the queue at zero
---
 .github/workflows/triage-labelled.yml | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml
index 174e3c54c0..18ce26171c 100644
--- a/.github/workflows/triage-labelled.yml
+++ b/.github/workflows/triage-labelled.yml
@@ -48,7 +48,13 @@ jobs:
     # Skip in forks
     if: >
       github.repository == 'vector-im/element-android' &&
-      contains(github.event.issue.labels.*.name, 'X-Needs-Design')
+      contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
+      (contains(github.event.issue.labels.*.name, 'S-Critical') &&
+       (contains(github.event.issue.labels.*.name, 'O-Frequent') ||
+        contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
+       contains(github.event.issue.labels.*.name, 'S-Major') &&
+       contains(github.event.issue.labels.*.name, 'O-Frequent') ||
+       contains(github.event.issue.labels.*.name, 'A11y'))
     steps:
       - uses: octokit/graphql-action@v2.x
         id: add_to_project

From 86132219d81f32f9329d2e1070455660a9878cc1 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 14 Oct 2022 13:45:23 +0200
Subject: [PATCH 180/400] Adding settings for visibility of labs flags

---
 vector-config/src/main/res/values/config-settings.xml | 2 ++
 vector/src/main/res/xml/vector_settings_labs.xml      | 6 ++++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml
index ced678fb98..7b7aac8156 100755
--- a/vector-config/src/main/res/values/config-settings.xml
+++ b/vector-config/src/main/res/values/config-settings.xml
@@ -42,7 +42,9 @@
     <bool name="settings_labs_thread_messages_default">false</bool>
     <bool name="settings_labs_new_app_layout_default">true</bool>
     <bool name="settings_labs_new_session_manager_default">false</bool>
+    <bool name="settings_labs_new_session_manager_visible">true</bool>
     <bool name="settings_labs_client_info_recording_default">false</bool>
+    <bool name="settings_labs_client_info_recording_visible">true</bool>
     <bool name="settings_timeline_show_live_sender_info_visible">true</bool>
     <bool name="settings_timeline_show_live_sender_info_default">false</bool>
     <bool name="settings_labs_rich_text_editor_visible">true</bool>
diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml
index ea4d39aa47..5b519bdd91 100644
--- a/vector/src/main/res/xml/vector_settings_labs.xml
+++ b/vector/src/main/res/xml/vector_settings_labs.xml
@@ -107,12 +107,14 @@
         android:defaultValue="@bool/settings_labs_new_session_manager_default"
         android:key="SETTINGS_LABS_NEW_SESSION_MANAGER_KEY"
         android:summary="@string/labs_enable_session_manager_summary"
-        android:title="@string/labs_enable_session_manager_title" />
+        android:title="@string/labs_enable_session_manager_title"
+        app:isPreferenceVisible="@bool/settings_labs_new_session_manager_visible" />
 
     <im.vector.app.core.preference.VectorSwitchPreference
         android:defaultValue="@bool/settings_labs_client_info_recording_default"
         android:key="SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY"
         android:summary="@string/labs_enable_client_info_recording_summary"
-        android:title="@string/labs_enable_client_info_recording_title" />
+        android:title="@string/labs_enable_client_info_recording_title"
+        app:isPreferenceVisible="@bool/settings_labs_client_info_recording_visible" />
 
 </androidx.preference.PreferenceScreen>

From 189e77211e14b4e1e1eca8ae0dcce4753fe92ae7 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 14 Oct 2022 13:46:33 +0200
Subject: [PATCH 181/400] Improving when structure

---
 .../features/settings/labs/VectorSettingsLabsFragment.kt    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
index 37998fa2a0..6c31e32567 100644
--- a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
@@ -151,9 +151,9 @@ class VectorSettingsLabsFragment :
     private fun configureEnableClientInfoRecordingPreference() {
         findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY)?.onPreferenceChangeListener =
                 OnPreferenceChangeListener { _, newValue ->
-                    when {
-                        (newValue as? Boolean) == false -> viewModel.handle(VectorSettingsLabsAction.DeleteRecordedClientInfo)
-                        (newValue as? Boolean) == true -> viewModel.handle(VectorSettingsLabsAction.UpdateClientInfo)
+                    when (newValue as? Boolean) {
+                        false -> viewModel.handle(VectorSettingsLabsAction.DeleteRecordedClientInfo)
+                        true -> viewModel.handle(VectorSettingsLabsAction.UpdateClientInfo)
                         else -> Unit
                     }
                     true

From cf25b81e9aa420d284b841176e79eb907c8a3dad Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 14 Oct 2022 14:00:01 +0200
Subject: [PATCH 182/400] Adding new shield color into resources

---
 .../ui-styles/src/main/res/values/colors.xml  |  1 +
 vector/build.gradle                           |  2 ++
 .../main/res/drawable/ic_shield_unknown.xml   | 28 +++++++++----------
 .../drawable/ic_shield_unknown_no_border.xml  | 24 ++++++++--------
 4 files changed, 29 insertions(+), 26 deletions(-)

diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml
index f4384adb40..c0bdde1ba9 100644
--- a/library/ui-styles/src/main/res/values/colors.xml
+++ b/library/ui-styles/src/main/res/values/colors.xml
@@ -146,6 +146,7 @@
     <color name="shield_color_gray">#91A1C0</color>
     <color name="shield_color_warning">#FF4B55</color>
     <color name="shield_color_warning_background">#0FFF4B55</color>
+    <color name="shield_color_unknown">@color/palette_gray_200</color>
 
     <!-- Badge Colors -->
     <attr name="vctr_badge_color_border" format="color" />
diff --git a/vector/build.gradle b/vector/build.gradle
index c59e1a3028..048bb885bc 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -53,6 +53,8 @@ android {
         // "pm clear" command after each test invocation. This command ensures
         // that the app's state is completely cleared between tests.
         testInstrumentationRunnerArguments clearPackageData: 'true'
+
+        vectorDrawables.useSupportLibrary = true
     }
 
     testOptions {
diff --git a/vector/src/main/res/drawable/ic_shield_unknown.xml b/vector/src/main/res/drawable/ic_shield_unknown.xml
index 7f5fbc730b..de56aa475e 100644
--- a/vector/src/main/res/drawable/ic_shield_unknown.xml
+++ b/vector/src/main/res/drawable/ic_shield_unknown.xml
@@ -3,18 +3,18 @@
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
-  <path
-      android:strokeWidth="1"
-      android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
-      android:fillColor="#737D8C"
-      android:strokeColor="#ffffff"/>
-  <path
-      android:strokeWidth="1"
-      android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
-      android:fillColor="#737D8C"
-      android:strokeColor="#ffffff"/>
-  <path
-      android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z"
-      android:fillColor="#ffffff"
-      android:fillType="evenOdd"/>
+    <path
+        android:fillColor="@color/shield_color_unknown"
+        android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
+        android:strokeWidth="1"
+        android:strokeColor="#ffffff" />
+    <path
+        android:fillColor="@color/shield_color_unknown"
+        android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
+        android:strokeWidth="1"
+        android:strokeColor="#ffffff" />
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z" />
 </vector>
diff --git a/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml
index 89331edf9e..bdc35ed891 100644
--- a/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml
+++ b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml
@@ -3,16 +3,16 @@
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
-  <path
-      android:strokeWidth="1"
-      android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
-      android:fillColor="#737D8C"
-      android:strokeColor="#ffffff"/>
-  <path
-      android:pathData="M1.5,12.405V3.075L12,0L22.5,3.075V12.405C22.5,21.945 12,24 12,24C12,24 1.5,21.945 1.5,12.405Z"
-      android:fillColor="#737D8C"/>
-  <path
-      android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z"
-      android:fillColor="#ffffff"
-      android:fillType="evenOdd"/>
+    <path
+        android:fillColor="@color/shield_color_unknown"
+        android:pathData="M12.008,23.487C12.005,23.487 12.002,23.488 12,23.489C11.998,23.488 11.995,23.487 11.992,23.487C11.92,23.471 11.813,23.445 11.675,23.409C11.399,23.337 11.002,23.223 10.523,23.058C9.565,22.725 8.292,22.186 7.022,21.361C4.49,19.715 2,16.954 2,12.405V3.45L12,0.521L22,3.45V12.405C22,16.954 19.51,19.715 16.978,21.361C15.708,22.186 14.435,22.725 13.477,23.058C12.998,23.223 12.601,23.337 12.325,23.409C12.187,23.445 12.08,23.471 12.008,23.487Z"
+        android:strokeWidth="1"
+        android:strokeColor="#ffffff" />
+    <path
+        android:fillColor="@color/shield_color_unknown"
+        android:pathData="M1.5,12.405V3.075L12,0L22.5,3.075V12.405C22.5,21.945 12,24 12,24C12,24 1.5,21.945 1.5,12.405Z" />
+    <path
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"
+        android:pathData="M12.003,16.5C12.577,16.5 13.043,16.035 13.043,15.46C13.043,14.887 12.577,14.421 12.003,14.421C11.429,14.421 10.964,14.887 10.964,15.46C10.964,16.035 11.429,16.5 12.003,16.5ZM10.559,9.823C10.559,9.023 11.208,8.379 12.003,8.379C12.795,8.379 13.447,9.031 13.447,9.823C13.447,10.19 13.29,10.321 12.671,10.748C12.398,10.937 12.022,11.2 11.729,11.586C11.416,11.999 11.223,12.513 11.223,13.168H12.783C12.783,12.847 12.868,12.665 12.971,12.528C13.096,12.364 13.276,12.226 13.558,12.031C13.587,12.01 13.618,11.989 13.651,11.967C14.162,11.619 15.006,11.045 15.006,9.823C15.006,8.17 13.656,6.82 12.003,6.82C10.352,6.82 9,8.157 9,9.823H10.559Z" />
 </vector>

From 1f0028fd7efc1d896c677b0d0720813727474ef5 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 14 Oct 2022 14:09:30 +0200
Subject: [PATCH 183/400] Adding changelog entry

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

diff --git a/changelog.d/7374.feature b/changelog.d/7374.feature
new file mode 100644
index 0000000000..aa10696dca
--- /dev/null
+++ b/changelog.d/7374.feature
@@ -0,0 +1 @@
+[Device Management] Redirect to the new screen everywhere when lab flag is on

From 111a70f6c1a7394f4244ea7a273f29fd86b0af18 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 14 Oct 2022 14:26:21 +0200
Subject: [PATCH 184/400] Checking lab flag to select session manager screen to
 navigate to

---
 .../app/features/settings/VectorSettingsActivity.kt  | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
index 9a84412482..dcf8e7b3ae 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
@@ -63,6 +63,8 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
 
     @Inject lateinit var session: Session
 
+    @Inject lateinit var vectorPreferences: VectorPreferences
+
     override fun initUiAndData() {
         setupToolbar(views.settingsToolbar)
                 .allowBack()
@@ -77,13 +79,19 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
                     replaceFragment(views.vectorSettingsPage, VectorSettingsAdvancedSettingsFragment::class.java, null, FRAGMENT_TAG)
                 SettingsActivityPayload.SecurityPrivacy ->
                     replaceFragment(views.vectorSettingsPage, VectorSettingsSecurityPrivacyFragment::class.java, null, FRAGMENT_TAG)
-                SettingsActivityPayload.SecurityPrivacyManageSessions ->
+                SettingsActivityPayload.SecurityPrivacyManageSessions -> {
+                    val fragmentClass = if (vectorPreferences.isNewSessionManagerEnabled()) {
+                        im.vector.app.features.settings.devices.v2.VectorSettingsDevicesFragment::class.java
+                    } else {
+                        VectorSettingsDevicesFragment::class.java
+                    }
                     replaceFragment(
                             views.vectorSettingsPage,
-                            VectorSettingsDevicesFragment::class.java,
+                            fragmentClass,
                             null,
                             FRAGMENT_TAG
                     )
+                }
                 SettingsActivityPayload.Notifications -> {
                     requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)
                     replaceFragment(views.vectorSettingsPage, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG)

From b04ad4915f7150b0e0ed4b79644aa37b1263d2b4 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 14 Oct 2022 15:38:28 +0300
Subject: [PATCH 185/400] Add changelog.

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

diff --git a/changelog.d/7338.wip b/changelog.d/7338.wip
new file mode 100644
index 0000000000..fc47ecb2f9
--- /dev/null
+++ b/changelog.d/7338.wip
@@ -0,0 +1 @@
+Implement QR Code Login UI

From e439b72e48170c9bfcd0b57157c60816655d52e8 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 13:46:57 +0100
Subject: [PATCH 186/400] Handle master key trust during E2EE set up

---
 .../android/sdk/api/rendezvous/Rendezvous.kt      | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index e33130e529..17f3a73181 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -176,10 +176,17 @@ class Rendezvous(
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
         // TODO: what do we do with the master key?
-//        verificationResponse.master_key ?.let {
-//            // set master key as trusted
-//            crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it)
-//        }
+        verificationResponse.master_key ?.let { masterKeyFromVerifyingDevice ->
+            // set master key as trusted
+            crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
+                if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) {
+                    Timber.tag(TAG).i("Setting master key as trusted")
+                    crypto.crossSigningService().markMyMasterKeyAsTrusted()
+                } else {
+                    Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                }
+            } ?: Timber.tag(TAG).i("No local master key")
+        } ?: Timber.tag(TAG).i("No master key given by verifying device")
 
         // request secrets from the verifying device
         Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")

From f999e7275952633a87d9cff123a4211546e7e735 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 14:01:13 +0100
Subject: [PATCH 187/400] Changelog

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

diff --git a/changelog.d/7369.feature b/changelog.d/7369.feature
new file mode 100644
index 0000000000..240fac3516
--- /dev/null
+++ b/changelog.d/7369.feature
@@ -0,0 +1 @@
+Add logic for sign in with QR code

From 411b766890ab33722b10be177bb1f3e64d963e75 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 14:17:19 +0100
Subject: [PATCH 188/400] Refactor to camelcase

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 22 +++++++++----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 17f3a73181..67bdeb2235 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -49,12 +49,12 @@ internal data class Payload(
         @Json val protocols: List<String>? = null,
         @Json val protocol: String? = null,
         @Json val homeserver: String? = null,
-        @Json val login_token: String? = null,
-        @Json val device_id: String? = null,
-        @Json val device_key: String? = null,
-        @Json val verifying_device_id: String? = null,
-        @Json val verifying_device_key: String? = null,
-        @Json val master_key: String? = null
+        @Json(name = "login_token") val loginToken: String? = null,
+        @Json(name = "device_id") val deviceId: String? = null,
+        @Json(name = "device_key") val deviceKey: String? = null,
+        @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null,
+        @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null,
+        @Json(name = "master_key") val masterKey: String? = null
 )
 
 private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
@@ -146,7 +146,7 @@ class Rendezvous(
         }
 
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
-        val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned")
+        val login_token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
 
         Timber.tag(TAG).i("Got login_token: $login_token for $homeserver")
 
@@ -159,14 +159,14 @@ class Rendezvous(
         val crypto = session.cryptoService()
         val deviceId = crypto.getMyDevice().deviceId
         val deviceKey = crypto.getMyDevice().fingerprint()
-        send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey))
+        send(Payload(PayloadType.Progress, outcome = "success", deviceId = deviceId, deviceKey = deviceKey))
 
         // await confirmation of verification
 
         val verificationResponse = receive()
-        val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned")
+        val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
-        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) {
+        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
             Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
             return
         }
@@ -176,7 +176,7 @@ class Rendezvous(
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
         // TODO: what do we do with the master key?
-        verificationResponse.master_key ?.let { masterKeyFromVerifyingDevice ->
+        verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice ->
             // set master key as trusted
             crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
                 if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) {

From 6426ff40d351396741ebf8aa71da1b87d9661673 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 14:18:50 +0100
Subject: [PATCH 189/400] Linting

---
 .../org/matrix/android/sdk/api/rendezvous/Rendezvous.kt    | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 67bdeb2235..6403d17031 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -91,7 +91,11 @@ class Rendezvous(
 
         if (incompatible) {
             send(Payload(PayloadType.Finish, intent = ourIntent))
-            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn
+            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
+                RendezvousFailureReason.OtherDeviceNotSignedIn
+            } else {
+                RendezvousFailureReason.OtherDeviceAlreadySignedIn
+            }
             channel.cancel(reason)
         }
 
@@ -175,7 +179,6 @@ class Rendezvous(
         Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
         crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
-        // TODO: what do we do with the master key?
         verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice ->
             // set master key as trusted
             crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->

From 81038bdd2367e1a15e554dbb9866424c1caeaf4f Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 14 Oct 2022 15:19:51 +0200
Subject: [PATCH 190/400] Fixing security section recommendation visibility

---
 .../settings/devices/v2/VectorSettingsDevicesFragment.kt  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index 3cf3b33942..bd68cbc0ce 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -186,14 +186,16 @@ class VectorSettingsDevicesFragment :
             unverifiedSessionsCount: Int,
             isCurrentSessionVerified: Boolean,
     ) {
-        if (unverifiedSessionsCount == 0 && inactiveSessionsCount == 0) {
+        val isUnverifiedSectionVisible = unverifiedSessionsCount > 0 && isCurrentSessionVerified
+        val isInactiveSectionVisible = inactiveSessionsCount > 0
+        if (isUnverifiedSectionVisible.not() && isInactiveSectionVisible.not()) {
             hideSecurityRecommendations()
         } else {
             views.deviceListHeaderSectionSecurityRecommendations.isVisible = true
             views.deviceListSecurityRecommendationsDivider.isVisible = true
 
-            views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0 && isCurrentSessionVerified
-            views.deviceListInactiveSessionsRecommendation.isVisible = inactiveSessionsCount > 0
+            views.deviceListUnverifiedSessionsRecommendation.isVisible = isUnverifiedSectionVisible
+            views.deviceListInactiveSessionsRecommendation.isVisible = isInactiveSectionVisible
             val unverifiedSessionsViewState = SecurityRecommendationViewState(
                     description = getString(R.string.device_manager_unverified_sessions_description),
                     sessionsCount = unverifiedSessionsCount,

From e83bdc3779f5bd56245b8144b0b7893c4295422a Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 14 Oct 2022 17:46:14 +0300
Subject: [PATCH 191/400] Use correct homeserver url to check qr code login
 support.

---
 .../im/vector/app/features/login/qr/QrCodeLoginActivity.kt | 3 ++-
 .../app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt | 2 +-
 .../app/features/login/qr/QrCodeLoginStatusFragment.kt     | 2 +-
 .../vector/app/features/onboarding/OnboardingViewModel.kt  | 7 +++----
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 042f885231..29190a70cb 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -25,6 +25,7 @@ import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
+import im.vector.lib.core.utils.compat.getParcelableCompat
 import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
 
@@ -37,7 +38,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         super.onCreate(savedInstanceState)
         views.toolbar.visibility = View.GONE
 
-        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelable(Mavericks.KEY_ARG)
+        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
             when (qrCodeLoginArgs?.loginType) {
                 QrCodeLoginType.LOGIN -> {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
index 1c68ebb2a5..efc2c9dd08 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
@@ -44,7 +44,7 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
 
     private fun initCancelButton() {
         views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks {
-            activity?.onBackPressed()
+            activity?.onBackPressedDispatcher?.onBackPressed()
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 4250431fcf..405e37af15 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -45,7 +45,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
 
     private fun initCancelButton() {
         views.qrCodeLoginStatusCancelButton.debouncedClicks {
-            activity?.onBackPressed()
+            activity?.onBackPressedDispatcher?.onBackPressed()
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index e12614abfe..d42644de7d 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -118,7 +118,7 @@ class OnboardingViewModel @AssistedInject constructor(
         }
     }
 
-    private fun observeQrCodeLoginCapability() = viewModelScope.launch {
+    private fun observeQrCodeLoginCapability(homeServerUrl: String) = viewModelScope.launch {
         if (!vectorFeatures.isQrCodeLoginEnabled()) {
             setState {
                 copy(
@@ -134,8 +134,7 @@ class OnboardingViewModel @AssistedInject constructor(
             }
         } else {
             // check if selected server supports MSC3882 first
-            // FIXME: this should be checking the selected homeserver not defaultHomeserverUrl
-            homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
+            homeServerConnectionConfigFactory.create(homeServerUrl)?.let {
                 val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
                 setState {
                     copy(
@@ -263,7 +262,6 @@ class OnboardingViewModel @AssistedInject constructor(
     private fun handleSplashAction(action: OnboardingAction.SplashAction) {
         setState { copy(onboardingFlow = action.onboardingFlow) }
         continueToPageAfterSplash(action.onboardingFlow)
-        observeQrCodeLoginCapability()
     }
 
     private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
@@ -709,6 +707,7 @@ class OnboardingViewModel @AssistedInject constructor(
             _viewEvents.post(OnboardingViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
         } else {
             startAuthenticationFlow(action, homeServerConnectionConfig, serverTypeOverride, postAction)
+            observeQrCodeLoginCapability(homeServerConnectionConfig.homeServerUri.toString())
         }
     }
 

From 6c10a9bf2d05eb57a21a0d67fdad5ec0e12d4242 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Fri, 14 Oct 2022 18:10:18 +0300
Subject: [PATCH 192/400] Code review fixes.

---
 library/ui-strings/src/main/res/values/strings.xml   |  2 +-
 .../internal/database/RealmSessionStoreMigration.kt  |  4 +++-
 .../homeserver/GetHomeServerCapabilitiesTask.kt      |  6 +++---
 .../debug/features/DebugFeaturesStateFactory.kt      |  8 ++++----
 .../features/debug/features/DebugVectorFeatures.kt   | 12 ++++++------
 .../java/im/vector/app/features/VectorFeatures.kt    |  8 ++++----
 .../app/features/onboarding/OnboardingViewModel.kt   |  2 +-
 .../devices/v2/VectorSettingsDevicesFragment.kt      |  2 +-
 8 files changed, 23 insertions(+), 21 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 2bee5a0e9c..a13db6a6db 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3331,7 +3331,7 @@
     <string name="device_manager_session_rename_edit_hint">Session name</string>
     <string name="device_manager_session_rename_description">Custom session names can help you recognize your devices more easily.</string>
     <string name="device_manager_session_rename_warning">Please be aware that session names are also visible to people you communicate with.</string>
-    <string name="device_manager_sessions_sign_in_with_qr_code_title">SIGN IN WITH QR CODE</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Sign in with QR Code</string>
     <string name="device_manager_sessions_sign_in_with_qr_code_description">You can use this device to sign in a mobile or web device with a QR code. There are two ways to do this:</string>
 
     <string name="device_manager_learn_more_sessions_inactive_title">Inactive sessions</string>
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index aef482ae2e..9a2c32f97c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -55,6 +55,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038
+import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo039
 import org.matrix.android.sdk.internal.util.Normalizer
 import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
 import javax.inject.Inject
@@ -63,7 +64,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
         private val normalizer: Normalizer
 ) : MatrixRealmMigration(
         dbName = "Session",
-        schemaVersion = 38L,
+        schemaVersion = 39L,
 ) {
     /**
      * Forces all RealmSessionStoreMigration instances to be equal.
@@ -111,5 +112,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
         if (oldVersion < 36) MigrateSessionTo036(realm).perform()
         if (oldVersion < 37) MigrateSessionTo037(realm).perform()
         if (oldVersion < 38) MigrateSessionTo038(realm).perform()
+        if (oldVersion < 39) MigrateSessionTo039(realm).perform()
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index c9a342777b..d65e629b71 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -133,9 +133,6 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 homeServerCapabilitiesEntity.roomVersionsJson = capabilities?.roomVersions?.let {
                     MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it)
                 }
-                homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
-                        getVersionResult?.doesServerSupportThreads().orFalse()
-                homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult?.doesServerSupportQrCodeLogin().orFalse()
             }
 
             if (getMediaConfigResult != null) {
@@ -146,6 +143,9 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
             if (getVersionResult != null) {
                 homeServerCapabilitiesEntity.lastVersionIdentityServerSupported = getVersionResult.isLoginAndRegistrationSupportedBySdk()
                 homeServerCapabilitiesEntity.canControlLogoutDevices = getVersionResult.doesServerSupportLogoutDevices()
+                homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
+                        getVersionResult.doesServerSupportThreads().orFalse()
+                homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult.doesServerSupportQrCodeLogin().orFalse()
             }
 
             if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 30019a630d..16e26ff3b5 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -92,13 +92,13 @@ class DebugFeaturesStateFactory @Inject constructor(
                         ),
                         createBooleanFeature(
                                 label = "Allow QR Code Login for all servers",
-                                key = DebugFeatureKeys.allowQrCodeLoginForAllServers,
-                                factory = VectorFeatures::allowQrCodeLoginForAllServers
+                                key = DebugFeatureKeys.qrCodeLoginForAllServers,
+                                factory = VectorFeatures::isQrCodeLoginForAllServers
                         ),
                         createBooleanFeature(
                                 label = "Show QR Code Login in Device Manager",
-                                key = DebugFeatureKeys.allowReciprocateQrCodeLogin,
-                                factory = VectorFeatures::allowReciprocateQrCodeLogin
+                                key = DebugFeatureKeys.reciprocateQrCodeLogin,
+                                factory = VectorFeatures::isReciprocateQrCodeLogin
                         ),
                         createBooleanFeature(
                                 label = "Enable Voice Broadcast",
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index 1bff0ffdb2..5c497c24ec 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -79,11 +79,11 @@ class DebugVectorFeatures(
     override fun isQrCodeLoginEnabled() = read(DebugFeatureKeys.qrCodeLoginEnabled)
             ?: vectorFeatures.isQrCodeLoginEnabled()
 
-    override fun allowQrCodeLoginForAllServers() = read(DebugFeatureKeys.allowQrCodeLoginForAllServers)
-            ?: vectorFeatures.allowQrCodeLoginForAllServers()
+    override fun isQrCodeLoginForAllServers() = read(DebugFeatureKeys.qrCodeLoginForAllServers)
+            ?: vectorFeatures.isQrCodeLoginForAllServers()
 
-    override fun allowReciprocateQrCodeLogin() = read(DebugFeatureKeys.allowReciprocateQrCodeLogin)
-            ?: vectorFeatures.allowReciprocateQrCodeLogin()
+    override fun isReciprocateQrCodeLogin() = read(DebugFeatureKeys.reciprocateQrCodeLogin)
+            ?: vectorFeatures.isReciprocateQrCodeLogin()
 
     override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
             ?: vectorFeatures.isVoiceBroadcastEnabled()
@@ -148,7 +148,7 @@ object DebugFeatureKeys {
     val forceUsageOfOpusEncoder = booleanPreferencesKey("force-usage-of-opus-encoder")
     val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled")
     val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled")
-    val allowQrCodeLoginForAllServers = booleanPreferencesKey("allow-qr-code-login-for-all-servers")
-    val allowReciprocateQrCodeLogin = booleanPreferencesKey("allow-reciprocate-qr-code-login")
+    val qrCodeLoginForAllServers = booleanPreferencesKey("qr-code-login-for-all-servers")
+    val reciprocateQrCodeLogin = booleanPreferencesKey("reciprocate-qr-code-login")
     val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
 }
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index 92bd90ab29..d41b5ac226 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -41,8 +41,8 @@ interface VectorFeatures {
      */
     fun isNewAppLayoutFeatureEnabled(): Boolean
     fun isQrCodeLoginEnabled(): Boolean
-    fun allowQrCodeLoginForAllServers(): Boolean
-    fun allowReciprocateQrCodeLogin(): Boolean
+    fun isQrCodeLoginForAllServers(): Boolean
+    fun isReciprocateQrCodeLogin(): Boolean
     fun isVoiceBroadcastEnabled(): Boolean
 }
 
@@ -60,7 +60,7 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun forceUsageOfOpusEncoder(): Boolean = false
     override fun isNewAppLayoutFeatureEnabled(): Boolean = true
     override fun isQrCodeLoginEnabled(): Boolean = false
-    override fun allowQrCodeLoginForAllServers(): Boolean = false
-    override fun allowReciprocateQrCodeLogin(): Boolean = false
+    override fun isQrCodeLoginForAllServers(): Boolean = false
+    override fun isReciprocateQrCodeLogin(): Boolean = false
     override fun isVoiceBroadcastEnabled(): Boolean = false
 }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index d42644de7d..fb8c29f39f 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -125,7 +125,7 @@ class OnboardingViewModel @AssistedInject constructor(
                         canLoginWithQrCode = false
                 )
             }
-        } else if (vectorFeatures.allowQrCodeLoginForAllServers()) {
+        } else if (vectorFeatures.isQrCodeLoginForAllServers()) {
             // allow for all servers
             setState {
                 copy(
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index 7e6da851dd..d4ee1d92eb 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -158,7 +158,7 @@ class VectorSettingsDevicesFragment :
     }
 
     private fun initQrLoginView() {
-        if (!vectorFeatures.allowReciprocateQrCodeLogin()) {
+        if (!vectorFeatures.isReciprocateQrCodeLogin()) {
             views.deviceListHeaderSignInWithQrCode.isVisible = false
             views.deviceListHeaderScanQrCodeButton.isVisible = false
             views.deviceListHeaderShowQrCodeButton.isVisible = false

From f1acb4712c052bd74894119103c425c748ae2e06 Mon Sep 17 00:00:00 2001
From: Jorge Martin Espinosa <jorgem@element.io>
Date: Fri, 14 Oct 2022 17:15:12 +0200
Subject: [PATCH 193/400] Fix editing formatted messages in plain text editor
 (#7360)

* Fix editing formatted messages in plain text editor

* Update changelog.d/7359.sdk

Co-authored-by: Benoit Marty <benoitm@matrix.org>

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 changelog.d/7359.bugfix                          |  1 +
 changelog.d/7359.sdk                             |  1 +
 .../api/session/room/timeline/TimelineEvent.kt   | 16 ++++++----------
 .../detail/composer/MessageComposerViewModel.kt  |  5 +++--
 4 files changed, 11 insertions(+), 12 deletions(-)
 create mode 100644 changelog.d/7359.bugfix
 create mode 100644 changelog.d/7359.sdk

diff --git a/changelog.d/7359.bugfix b/changelog.d/7359.bugfix
new file mode 100644
index 0000000000..98e29fb697
--- /dev/null
+++ b/changelog.d/7359.bugfix
@@ -0,0 +1 @@
+Fix editing formatted messages with plain text editor
diff --git a/changelog.d/7359.sdk b/changelog.d/7359.sdk
new file mode 100644
index 0000000000..c78c591d67
--- /dev/null
+++ b/changelog.d/7359.sdk
@@ -0,0 +1 @@
+Allow getting the formatted or plain text body of a message for the fun `TimelineEvent.getTextEditableContent()`.
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 7341fd922e..223acd1b9c 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
@@ -181,9 +181,13 @@ fun TimelineEvent.isRootThread(): Boolean {
 /**
  * Get the latest message body, after a possible edition, stripping the reply prefix if necessary.
  */
-fun TimelineEvent.getTextEditableContent(): String {
+fun TimelineEvent.getTextEditableContent(formatted: Boolean): String {
     val lastMessageContent = getLastMessageContent()
-    val lastContentBody = lastMessageContent.getFormattedBody() ?: return ""
+    val lastContentBody = if (formatted && lastMessageContent is MessageContentWithFormattedBody) {
+        lastMessageContent.formattedBody
+    } else {
+        lastMessageContent?.body
+    } ?: return ""
     return if (isReply()) {
         extractUsefulTextFromReply(lastContentBody)
     } else {
@@ -201,11 +205,3 @@ fun MessageContent.getTextDisplayableContent(): String {
             ?: (this as MessageTextContent?)?.matrixFormattedBody?.let { ContentUtils.formatSpoilerTextFromHtml(it) }
             ?: body
 }
-
-fun MessageContent?.getFormattedBody(): String? {
-    return if (this is MessageContentWithFormattedBody) {
-        formattedBody
-    } else {
-        this?.body
-    }
-}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
index b877c2979b..5d3465ab2e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
@@ -151,7 +151,8 @@ class MessageComposerViewModel @AssistedInject constructor(
 
     private fun handleEnterEditMode(action: MessageComposerAction.EnterEditMode) {
         room.getTimelineEvent(action.eventId)?.let { timelineEvent ->
-            setState { copy(sendMode = SendMode.Edit(timelineEvent, timelineEvent.getTextEditableContent())) }
+            val formatted = vectorPreferences.isRichTextEditorEnabled()
+            setState { copy(sendMode = SendMode.Edit(timelineEvent, timelineEvent.getTextEditableContent(formatted))) }
         }
     }
 
@@ -552,7 +553,7 @@ class MessageComposerViewModel @AssistedInject constructor(
                                     state.sendMode.timelineEvent,
                                     messageContent?.msgType ?: MessageType.MSGTYPE_TEXT,
                                     action.text,
-                                    (messageContent as? MessageContentWithFormattedBody)?.formattedBody,
+                                    action.formattedText,
                                     action.autoMarkdown
                             )
                         } else {

From b28c4c1120b68d2677717a1c3f309b6aeb230c24 Mon Sep 17 00:00:00 2001
From: waclaw66 <waclaw66@seznam.cz>
Date: Fri, 14 Oct 2022 08:58:07 +0000
Subject: [PATCH 194/400] Translated using Weblate (Czech)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/
---
 .../src/main/res/values-cs/strings.xml        | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-cs/strings.xml b/library/ui-strings/src/main/res/values-cs/strings.xml
index b23f5f102a..7f8afdad2f 100644
--- a/library/ui-strings/src/main/res/values-cs/strings.xml
+++ b/library/ui-strings/src/main/res/values-cs/strings.xml
@@ -648,8 +648,8 @@
     <string name="encryption_import_room_keys">Import klíčů místností</string>
     <string name="encryption_import_room_keys_summary">Import klíčů z místního souboru</string>
     <string name="encryption_import_import">Import</string>
-    <string name="encryption_never_send_to_unverified_devices_title">Šifruj pouze do ověřených relací</string>
-    <string name="encryption_never_send_to_unverified_devices_summary">Nikdy neposílejte šifrované zprávy do neověřených relací z této relace.</string>
+    <string name="encryption_never_send_to_unverified_devices_title">Šifrovat pouze do ověřených relací</string>
+    <string name="encryption_never_send_to_unverified_devices_summary">Nikdy neposílat šifrované zprávy do neověřených relací z této relace.</string>
     <string name="encryption_information_not_verified">Není ověřeno</string>
     <string name="encryption_information_verified">Potvrzeno</string>
     <string name="encryption_information_unknown_ip">neznámá ip</string>
@@ -1079,7 +1079,7 @@
     <string name="no_ignored_users">Neignorujete žádné uživatele</string>
     <string name="help_long_click_on_room_for_more_options">Více možností po dlouhém stisku na místnost</string>
     <string name="room_join_rules_public">%1$s učinil místnost veřejnou pro kohokoli znalého odkazu.</string>
-    <string name="room_join_rules_invite">%1$s nastavil místnost jen pro pozvané.</string>
+    <string name="room_join_rules_invite">%1$s nastavil místnost jako pouze pro pozvané.</string>
     <string name="timeline_unread_messages">Nepřečtené zprávy</string>
     <string name="login_splash_title">Je to Vaše konverzace. Vlastněte ji.</string>
     <string name="login_splash_text1">Chatujte s lidmi přímo nebo ve skupinách</string>
@@ -1218,7 +1218,7 @@
     <string name="settings_developer_mode_fail_fast_summary">${app_name} může spadnout častěji, když se objeví neočekávané chyby</string>
     <string name="command_description_shrug">Předsune ¯\\_(ツ)_/¯ do textové zprávy</string>
     <string name="create_room_encryption_title">Zapnout šifrování</string>
-    <string name="create_room_encryption_description">Jakmile zapnuto, šifrování nelze vypnout.</string>
+    <string name="create_room_encryption_description">Po zapnutí šifrování ho není možné vypnout.</string>
     <string name="login_error_threepid_denied">Vaše emailová doména není autorizována registrovat se na tomto serveru</string>
     <string name="verification_conclusion_warning">Nedůvěryhodné přihlášení</string>
     <string name="verification_sas_match">Shodují se</string>
@@ -1297,10 +1297,10 @@
     <string name="verification_conclusion_ok_self_notice">Vaše nová relace je nyní ověřena. Má přístup k Vašim zašifrovaným zprávám a ostatní uživatelé ji uvidi jako důvěryhodnou.</string>
     <string name="encryption_information_cross_signing_state">Křížové podepisování</string>
     <string name="encryption_information_dg_xsigning_complete">Křížové podpisování je zapnuto.
-\nPrivátní klíče v zařízení.</string>
+\nSoukromé klíče v zařízení.</string>
     <string name="encryption_information_dg_xsigning_trusted">Křížové podpisování je zapnuto
 \nKlíče jsou důvěryhodné.
-\nPrivátní klíče nejsou známy</string>
+\nSoukromé klíče nejsou známy</string>
     <string name="encryption_information_dg_xsigning_not_trusted">Křížové podpisování je zapnuto.
 \nKlíče nejsou důvěryhodné</string>
     <string name="encryption_information_dg_xsigning_disabled">Křížové podpisování není zapnuto</string>
@@ -2019,11 +2019,11 @@
     <string name="create_space_error_empty_field_space_name">Dejte prostoru jméno a pokračujte.</string>
     <string name="create_spaces_details_public_header">Doplňte nějaké podrobnosti, aby zaujal. Můžete je kdykoli změnit.</string>
     <string name="activity_create_space_title">Vytvořit prostor</string>
-    <string name="space_type_private_desc">Pouze na pozvání, nejlepší pro Vás nebo týmy</string>
-    <string name="space_type_private">Privátní</string>
+    <string name="space_type_private_desc">Pouze na pozvání, nejlepší pro vás nebo týmy</string>
+    <string name="space_type_private">Soukromý</string>
     <string name="space_type_public_desc">Otevřený pro všechny, nejlepší pro komunity</string>
     <string name="space_type_public">Veřejný</string>
-    <string name="create_spaces_private_teammates">Privátní prostor pro Vás a Vaše kolegy</string>
+    <string name="create_spaces_private_teammates">Soukromý prostor pro vás a vaše kolegy</string>
     <string name="create_spaces_me_and_teammates">Já a kolegové</string>
     <string name="create_spaces_organise_rooms">Soukromý prostor pro uspořádání vašich místností</string>
     <string name="create_spaces_just_me">Jen já</string>
@@ -2032,7 +2032,7 @@
     <string name="create_spaces_join_info_help">Ke vstupu do existujícího prostoru potřebujete pozvání.</string>
     <string name="create_spaces_you_can_change_later">Můžete změnit později</string>
     <string name="create_spaces_choose_type_label">Jaký typ prostoru chcete vytvořit\?</string>
-    <string name="your_private_space">Váš privátní prostor</string>
+    <string name="your_private_space">Váš soukromý prostor</string>
     <string name="your_public_space">Váš veřejný prostor</string>
     <string name="add_space">Přidat prostor</string>
     <string name="command_description_leave_room">Opustit místnost s daným id (nebo nynější místnost pokud prázdné)</string>
@@ -2044,7 +2044,7 @@
     <string name="room_settings_room_access_public_description">Kdokoliv může místnost najít a připojit se do ní</string>
     <string name="room_settings_room_access_public_title">Veřejný</string>
     <string name="room_settings_room_access_private_description">Pouze pozvaní mohou místnost najít a vstoupit do ní</string>
-    <string name="room_settings_room_access_private_title">Privátní</string>
+    <string name="room_settings_room_access_private_title">Soukromý</string>
     <string name="room_settings_room_access_entry_unknown">Neznámé nastavení přístupu (%s)</string>
     <string name="room_settings_room_access_entry_knock">Každý může na místnost zaklepat, členové pak mohou přijmout či odmítnout</string>
     <string name="room_settings_guest_access_title">Dovolit hostům vstoupit</string>
@@ -2165,7 +2165,7 @@
     <string name="spaces_which_can_access">Prostory, které mají přístup</string>
     <string name="allow_space_member_to_find_and_access">Umožněte členům prostoru ho najít a zpřístupnit.</string>
     <string name="room_create_member_of_space_name_can_join">Členové prostoru %s mohou vyhledávat, prohlížet a připojovat se.</string>
-    <string name="room_settings_room_access_private_invite_only_title">Soukromý (pouze pro pozvané)</string>
+    <string name="room_settings_room_access_private_invite_only_title">Soukromý (jen pro pozvané)</string>
     <string name="denied_permission_voice_message">Chcete-li odesílat hlasové zprávy, povolte oprávnění mikrofonu.</string>
     <string name="settings_room_upgrades">Aktualizace místnosti</string>
     <string name="settings_messages_by_bot">Zprávy od bota</string>

From 51648e81eb98f958ccd6fba2b9fe6c7f79d4b28e Mon Sep 17 00:00:00 2001
From: Vri <element@vrifox.cc>
Date: Fri, 14 Oct 2022 14:00:51 +0000
Subject: [PATCH 195/400] Translated using Weblate (German)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 library/ui-strings/src/main/res/values-de/strings.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index a749d6a2e1..b0f1ebe2e7 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -2239,7 +2239,7 @@
     <string name="create_poll_question_hint">Frage oder Thema</string>
     <string name="create_poll_question_title">Abstimmungsthema oder Frage</string>
     <string name="create_poll_title">Abstimmung erstellen</string>
-    <string name="attachment_type_poll">Abstimmung</string>
+    <string name="attachment_type_poll">Umfrage</string>
     <string name="open_discovery_settings">Auffindungseinstellungen öffnen</string>
     <string name="shortcut_disabled_reason_sign_out">Sitzung abgemeldet!</string>
     <string name="shortcut_disabled_reason_room_left">Raum verlassen!</string>
@@ -2666,7 +2666,7 @@
     <string name="device_manager_session_details_title">Sitzungsdetails</string>
     <string name="device_manager_other_sessions_clear_filter">Filter zurücksetzen</string>
     <string name="device_manager_other_sessions_no_inactive_sessions_found">Keine inaktiven Sitzungen gefunden.</string>
-    <string name="device_manager_other_sessions_no_unverified_sessions_found">Keine nicht verifizierten Sitzungen gefunden.</string>
+    <string name="device_manager_other_sessions_no_unverified_sessions_found">Keine unverifizierten Sitzungen gefunden.</string>
     <string name="device_manager_other_sessions_no_verified_sessions_found">Keine verifizierten Sitzungen gefunden.</string>
     <plurals name="device_manager_other_sessions_recommendation_description_inactive">
         <item quantity="one">Erwäge, dich aus alten (ein Tag oder mehr), nicht mehr verwendeten Sitzungen abzumelden.</item>
@@ -2707,8 +2707,8 @@
     <string name="device_manager_verification_status_detail_current_session_unverified">Verifiziere deine aktuelle Sitzung für besonders sichere Kommunikation.</string>
     <string name="device_manager_verification_status_detail_current_session_verified">Deine aktuelle Sitzung ist für sichere Kommunikation bereit.</string>
     <string name="device_manager_view_details">Details anzeigen</string>
-    <string name="device_manager_verification_status_detail_other_session_unverified">Für bestmögliche Sicherheit und Zuverlässigkeit verifiziere diese Sitzungen oder melde dich von ihr ab.</string>
-    <string name="device_manager_other_sessions_recommendation_description_verified">Für die bestmögliche Sicherheit, melde dich von allen Sitzungen ab, die du nicht erkennst oder nicht mehr benutzt.</string>
+    <string name="device_manager_verification_status_detail_other_session_unverified">Für bestmögliche Sicherheit und Zuverlässigkeit verifiziere diese Sitzung oder melde sie ab.</string>
+    <string name="device_manager_other_sessions_recommendation_description_verified">Für bestmögliche Sicherheit, melde dich von allen Sitzungen ab, die du nicht erkennst oder benutzt.</string>
     <string name="device_manager_learn_more_session_rename">Andere Nutzer in Direktnachrichten und Räumen, in denen du dich befindest, können eine vollständige Liste deiner Sitzungen einsehen.
 \n
 \nDies gibt ihnen die Sicherheit, dass sie auch wirklich mit dir kommunizieren. Allerdings bedeutet es auch, dass sie die Sitzungsnamen sehen können, die du hier angibst.</string>

From d52cc2d96dc4968344a352c9504fb3fdf1f62c35 Mon Sep 17 00:00:00 2001
From: Linerly <linerly@protonmail.com>
Date: Wed, 12 Oct 2022 15:53:16 +0000
Subject: [PATCH 196/400] Translated using Weblate (Indonesian)

Currently translated at 100.0% (2444 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/id/
---
 .../src/main/res/values-in/strings.xml        | 28 +++++++++----------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-in/strings.xml b/library/ui-strings/src/main/res/values-in/strings.xml
index 51753995bf..33772bf660 100644
--- a/library/ui-strings/src/main/res/values-in/strings.xml
+++ b/library/ui-strings/src/main/res/values-in/strings.xml
@@ -183,8 +183,8 @@
     <string name="settings_app_info_link_summary">Tampilkan info aplikasi dalam pengaturan sistem.</string>
     <string name="settings_app_info_link_title">Info aplikasi</string>
     <string name="settings_notification_ringtone">Suara pemberitahuan</string>
-    <string name="settings_enable_all_notif">Perbolehkan pemberitahuan untuk akun ini</string>
-    <string name="settings_enable_this_device">Perbolehkan pemberitahuan untuk perangkat ini</string>
+    <string name="settings_enable_all_notif">Aktifkan pemberitahuan untuk akun ini</string>
+    <string name="settings_enable_this_device">Aktifkan pemberitahuan untuk sesi ini</string>
     <string name="settings_containing_my_display_name">Pesan yang berisikan nama layarku</string>
     <string name="settings_containing_my_user_name">Pesan berisikan nama layarku</string>
     <string name="settings_messages_in_one_to_one">Pesan percakapan empat mata</string>
@@ -394,7 +394,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
 \nMohon periksa pengaturan akun anda.</string>
     <string name="settings_troubleshoot_test_account_settings_quickfix">Perbolehkan</string>
     <string name="settings_troubleshoot_test_device_settings_title">Pengaturan Perangkat.</string>
-    <string name="settings_troubleshoot_test_device_settings_success">Pemberitahuan diperbolehkan untuk perangkat ini.</string>
+    <string name="settings_troubleshoot_test_device_settings_success">Pemberitahuan diperbolehkan untuk sesi ini.</string>
     <string name="settings_troubleshoot_test_device_settings_failed">Notifikasi tidak diaktifkan pada sesi ini.
 \nMohon periksa pengaturan ${app_name}.</string>
     <string name="settings_troubleshoot_test_device_settings_quickfix">Perbolehkan</string>
@@ -414,7 +414,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
 \n%1$s</string>
     <string name="settings_troubleshoot_test_service_boot_title">Mulai ketika menyalakan perangkat</string>
     <string name="settings_troubleshoot_test_service_boot_success">Layanan akan dimulai ketika perangkat dinyalakan kembali.</string>
-    <string name="settings_troubleshoot_test_service_boot_failed">Layanan tidak akan mulai ketika perangkat dinyalakan kembali, Anda tidak akan menerima pemberitahuan hingga Anda membuka ${app_name}.</string>
+    <string name="settings_troubleshoot_test_service_boot_failed">Layanan tidak akan mulai ketika perangkat dinyalakan kembali, Anda tidak akan menerima pemberitahuan sampai Anda membuka ${app_name}.</string>
     <string name="settings_troubleshoot_test_service_boot_quickfix">Perbolehkan memulai ketika perangkat dinyalakan</string>
     <string name="settings_troubleshoot_test_bg_restricted_title">Periksa halangan di balik layar</string>
     <string name="settings_troubleshoot_test_bg_restricted_success">Larangan latar belakang dinonaktifkan untuk ${app_name}. Percobaan ini sebaiknya dijalankan menggunakan jaringan data ponsel (bukan WiFi).
@@ -442,7 +442,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="settings_notification_by_event">Urgensi pemberitahuan lewat kejadian</string>
     <string name="settings_troubleshoot_test_bing_settings_title">Pengaturan Sesukanya.</string>
     <string name="settings_troubleshoot_test_bing_settings_success_with_warn">Perhatikan bahwa sebagian jenis pesan tersetel diam (mengeluarkan pemberitahuan tanpa suara).</string>
-    <string name="settings_troubleshoot_test_bing_settings_failed">Sebagian pemberitahuan dimatikan dalam aturan Anda.</string>
+    <string name="settings_troubleshoot_test_bing_settings_failed">Sebagian pemberitahuan dimatikan dalam pengaturan Anda.</string>
     <string name="settings_troubleshoot_test_fcm_failed_too_many_registration">[%1$s]
 \nError ini di luar kendali ${app_name} dan menurut Google, error ini muncul ketika terlalu banyak aplikasi terdaftar dengan FCM pada perangkat tersebut. Error ini tidak seharusnya mempengaruhi pengguna biasa.</string>
     <string name="settings_troubleshoot_test_fcm_failed_service_not_available">[%1$s]
@@ -459,7 +459,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="settings_cryptography_manage_keys">Pengelolaan Kunci Kriptografi</string>
     <string name="settings_inline_url_preview_summary">Pratinjau tautan dalam obrolan apabila homeserver mendukung fitur ini.</string>
     <string name="settings_send_typing_notifs">Kirim pemberitahuan mengetik</string>
-    <string name="settings_send_typing_notifs_summary">Beritahu pengguna lain bahwa Anda sedang mengetik.</string>
+    <string name="settings_send_typing_notifs_summary">Beri tahu pengguna lain bahwa Anda sedang mengetik.</string>
     <string name="settings_send_markdown">Format markdown</string>
     <string name="settings_send_markdown_summary">Format pesan menggunakan sintaks markdown sebelum dikirim. Ini mengizinkan format lanjutan seperti menggunakan tanda bintang untuk menunjukkan teks miring.</string>
     <string name="settings_show_read_receipts">Tunjukkan tanda telah dibaca</string>
@@ -565,7 +565,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="room_permissions_change_main_address_for_the_room">Mengubah alamat utama untuk ruangan ini</string>
     <string name="room_permissions_change_room_avatar">Mengubah avatar ruangan</string>
     <string name="room_permissions_modify_widgets">Mengubah widget</string>
-    <string name="room_permissions_notify_everyone">Beritahu semuanya</string>
+    <string name="room_permissions_notify_everyone">Beri tahu semuanya</string>
     <string name="room_permissions_remove_messages_sent_by_others">Menghapus pesan yang dikirim dari yang lain</string>
     <string name="room_permissions_change_settings">Ubah pengaturan</string>
     <string name="room_permissions_default_role">Peran bawaan</string>
@@ -962,7 +962,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="room_settings_room_notifications_account_settings">Pengaturan akun</string>
     <string name="room_settings_room_notifications_manage_notifications">Anda dapat mengelola notifikasi di %1$s.</string>
     <string name="room_settings_room_notifications_encryption_notice">Harap dicatat bahwa pemberitahuan sebutan &amp; kata kunci tidak tersedia dalam ruangan terenkripsi di ponsel.</string>
-    <string name="room_settings_room_notifications_notify_me">Beritahu saya untuk</string>
+    <string name="room_settings_room_notifications_notify_me">Beri tahu saya untuk</string>
     <string name="settings_play_shutter_sound">Putar suara rana</string>
     <string name="media_source_choose">Pilih</string>
     <string name="settings_default_media_source">Sumber media bawaan</string>
@@ -997,7 +997,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <plurals name="seconds">
         <item quantity="other">%d detik</item>
     </plurals>
-    <string name="settings_background_fdroid_sync_mode_disabled_description">Anda tidak akan diberitahu tentang pesan masuk saat aplikasi berada di latar belakang.</string>
+    <string name="settings_background_fdroid_sync_mode_disabled_description">Anda tidak akan diberi tahu tentang pesan masuk saat aplikasi berada di latar belakang.</string>
     <string name="settings_background_fdroid_sync_mode_disabled">Tidak ada sinkronisasi latar belakang</string>
     <string name="settings_background_fdroid_sync_mode_real_time_description">${app_name} akan disinkronkan di latar belakang secara berkala pada waktu yang tepat (dapat dikonfigurasi).
 \nIni akan memengaruhi penggunaan radio dan baterai, dan ada juga pemberitahuan yang ditampilkan permanen menyatakan bahwa ${app_name} sedang mendengarkan peristiwa.</string>
@@ -1021,7 +1021,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="settings_notification_keyword_contains_dot">Kata kunci tidak boleh diawali dengan \'.\'</string>
     <string name="settings_notification_new_keyword">Tambahkan kata kunci baru</string>
     <string name="settings_notification_your_keywords">Kata kunci Anda</string>
-    <string name="settings_notification_notify_me_for">Beritahu saya untuk</string>
+    <string name="settings_notification_notify_me_for">Beri tahu saya untuk</string>
     <string name="settings_notification_other">Lainnya</string>
     <string name="settings_notification_mentions_and_keywords">Sebutan dan Kata Kunci</string>
     <string name="settings_notification_default">Notifikasi Bawaan</string>
@@ -1803,9 +1803,9 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="dev_tools_menu_name">Alat Pengembang</string>
     <string name="a11y_public_room">Ruangan publik</string>
     <string name="a11y_view_read_receipts">Lihat laporan dibaca</string>
-    <string name="a11y_rule_notify_off">Jangan beritahu</string>
-    <string name="a11y_rule_notify_silent">Beritahu tanpa suara</string>
-    <string name="a11y_rule_notify_noisy">Beritahu dengan suara</string>
+    <string name="a11y_rule_notify_off">Jangan beri tahu</string>
+    <string name="a11y_rule_notify_silent">Beri tahu tanpa suara</string>
+    <string name="a11y_rule_notify_noisy">Beri tahu dengan suara</string>
     <string name="a11y_error_message_not_sent">Pesan tidak terkirim karena kesalahan</string>
     <string name="a11y_unchecked">Tidak dicentang</string>
     <string name="a11y_checked">Dicentang</string>
@@ -2344,7 +2344,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="action_view_threads">Tampilkan Utasan</string>
     <string name="room_message_autocomplete_notification">Notifikasi ruangan</string>
     <string name="room_message_autocomplete_users">Pengguna</string>
-    <string name="room_message_notify_everyone">Beritahu seluruh ruangan</string>
+    <string name="room_message_notify_everyone">Beri tahu seluruh ruangan</string>
     <plurals name="message_reaction_show_more">
         <item quantity="other">%1$d lagi</item>
     </plurals>

From 5281718d86d8c7e101d2b39c4211de7f0bff8a19 Mon Sep 17 00:00:00 2001
From: Youngbin Han <sukso96100@gmail.com>
Date: Wed, 12 Oct 2022 18:18:29 +0000
Subject: [PATCH 197/400] Translated using Weblate (Korean)

Currently translated at 36.9% (903 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ko/
---
 .../src/main/res/values-ko/strings.xml        | 40 ++++++++++++++++++-
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ko/strings.xml b/library/ui-strings/src/main/res/values-ko/strings.xml
index 37e8849fa8..11e870f581 100644
--- a/library/ui-strings/src/main/res/values-ko/strings.xml
+++ b/library/ui-strings/src/main/res/values-ko/strings.xml
@@ -47,7 +47,8 @@
     <string name="initial_sync_start_importing_account_rooms">초기 동기화:
 \n방 가져오는 중</string>
     <string name="initial_sync_start_importing_account_joined_rooms">초기 동기화:
-\n들어간 방 가져오는 중</string>
+\n대화 가져오는 중
+\n많은 방에 참여하신 경우, 오래 걸릴 수 있습니다</string>
     <string name="initial_sync_start_importing_account_invited_rooms">초기 동기화:
 \n초대받은 방 가져오는 중</string>
     <string name="initial_sync_start_importing_account_left_rooms">초기 동기화:
@@ -958,4 +959,39 @@
     <string name="notice_room_name_changed_by_you">방 이름을 바꾸었습니다: %1$s</string>
     <string name="notice_room_avatar_changed_by_you">방 사진을 바꾸었습니다</string>
     <string name="notice_room_avatar_changed">%1$s님이 방 사진을 바꾸었습니다</string>
-</resources>
+    <string name="initial_sync_request_title">초기 동기화 요청</string>
+    <string name="initial_sync_start_downloading">초기 동기화:
+\n데이터 내려받는 중…</string>
+    <string name="initial_sync_start_server_computing">초기 동기화:
+\n서버 응답을 기다리는 중…</string>
+    <string name="room_displayname_empty_room_was">빈 방 (기존 %s)</string>
+    <plurals name="room_displayname_four_and_more_members">
+        <item quantity="other">%1$s님, %2$s님, %3$s님과 %4$d님 등</item>
+    </plurals>
+    <string name="room_displayname_4_members">%1$s님, %2$s님, %3$s님과 %4$s님</string>
+    <string name="room_displayname_3_members">%1$s님, %2$s님과 %3$s님</string>
+    <string name="room_error_access_unauthorized">이 방에 참여할 수 없습니다</string>
+    <string name="explore_rooms">방 둘러보기</string>
+    <string name="create_room">방 만들기</string>
+    <string name="start_chat">채팅 시작</string>
+    <string name="all_chats">모든 채팅</string>
+    <string name="power_level_moderator">중재자</string>
+    <string name="power_level_admin">관리자</string>
+    <string name="notice_widget_modified_by_you">%1$s위젯을 수정했습니다</string>
+    <string name="notice_widget_modified">%1$s님이 %2$s위젯을 수정했습니다</string>
+    <string name="notice_widget_removed_by_you">%1$s위젯을 삭제했습니다</string>
+    <string name="notice_widget_removed">%1$s님이 %2$s위젯을 삭제했습니다</string>
+    <string name="notice_widget_added_by_you">%1$s위젯을 추가했습니다</string>
+    <string name="notice_widget_added">%1$s님이 %2$s위젯을 추가했습니다</string>
+    <string name="notice_room_third_party_registered_invite_by_you">%1$s님의 초대를 수락했습니다</string>
+    <string name="notice_direct_room_third_party_revoked_invite_by_you">%1$s님 초대를 취소했습니다</string>
+    <string name="notice_direct_room_third_party_revoked_invite">%1$s님이 %2$s님 초대를 취소했습니다</string>
+    <string name="notice_room_third_party_revoked_invite_by_you">%1$s님에게 방에 참가하라고 보낸 초대를 취소했습니다</string>
+    <string name="notice_direct_room_third_party_invite_by_you">%1$s님을 초대했습니다</string>
+    <string name="notice_direct_room_third_party_invite">%1$s님이 %2$s님을 초대했습니다</string>
+    <string name="notice_room_third_party_invite_by_you">%1$s님에게 방 초대를 보냈습니다</string>
+    <string name="notice_room_avatar_removed_by_you">방 아바타를 삭제했습니다</string>
+    <string name="notice_room_avatar_removed">%1$s님이 방 아바타를 삭제했습니다</string>
+    <string name="notice_room_topic_removed_by_you">방 주제를 삭제했습니다</string>
+    <string name="notice_room_name_removed_by_you">방 이름을 삭제했습니다</string>
+</resources>
\ No newline at end of file

From 08ac6493ab2bbb93b7aef00a1a5860ddbd708acc Mon Sep 17 00:00:00 2001
From: Nui Harime <harime.nui@yandex.ru>
Date: Fri, 14 Oct 2022 13:08:51 +0000
Subject: [PATCH 198/400] Translated using Weblate (Russian)

Currently translated at 97.2% (2378 of 2444 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ru/
---
 .../src/main/res/values-ru/strings.xml        | 32 ++++++++++++-------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml
index cd1a63a7ac..4c00be4e3e 100644
--- a/library/ui-strings/src/main/res/values-ru/strings.xml
+++ b/library/ui-strings/src/main/res/values-ru/strings.xml
@@ -438,8 +438,8 @@
     <string name="encryption_export_room_keys">Экспорт ключей комнаты</string>
     <string name="encryption_export_room_keys_summary">Экспорт ключей в локальный файл</string>
     <string name="encryption_export_export">Экспорт</string>
-    <string name="passphrase_enter_passphrase">Введите парольную фразу</string>
-    <string name="passphrase_confirm_passphrase">Подтвердите парольную фразу</string>
+    <string name="passphrase_enter_passphrase">Введите мнемоническую фразу</string>
+    <string name="passphrase_confirm_passphrase">Подтвердите мнемоническую фразу</string>
     <string name="encryption_import_e2e_room_keys">Импорт E2E ключей комнаты</string>
     <string name="encryption_import_room_keys">Импорт ключей комнаты</string>
     <string name="encryption_import_room_keys_summary">Импортировать ключи из локального файла</string>
@@ -617,7 +617,7 @@
     </plurals>
     <string name="system_alerts_header">Системные оповещения</string>
     <string name="dialog_title_error">Ошибка</string>
-    <string name="passphrase_create_passphrase">Создать парольную фразу</string>
+    <string name="passphrase_create_passphrase">Создать мнемоническую фразу</string>
     <string name="passphrase_passphrase_does_not_match">Парольные фразы не совпадают</string>
     <string name="resource_limit_contact_admin">свяжитесь с вашим администратором</string>
     <string name="resource_limit_soft_default">Превышен один из ресурсных лимитов сервера, по этому <b>некоторые пользователи не смогут авторизоваться</b>.</string>
@@ -637,7 +637,7 @@
     <string name="settings_call_ringtone_title">Мелодия входящего вызова</string>
     <string name="settings_call_ringtone_dialog_title">Выберите мелодию звонка:</string>
     <string name="video_call_in_progress">Идёт видеозвонок …</string>
-    <string name="room_participants_action_remove">Удалить из чата</string>
+    <string name="room_participants_action_remove">Удалить из комнаты</string>
     <string name="settings_notification_troubleshoot">Поиск проблем с уведомлениями</string>
     <string name="settings_send_typing_notifs">Оправлять уведомления о наборе текста</string>
     <string name="settings_send_markdown">Markdown форматирование</string>
@@ -725,9 +725,9 @@
     <string name="settings_cryptography_manage_keys">Управление криптографическими ключами</string>
     <string name="encryption_settings_manage_message_recovery_summary">Управление резервным копированием ключей</string>
     <string name="notification_silent">Беззвучный</string>
-    <string name="passphrase_empty_error_message">Пожалуйста, введите парольную фразу</string>
+    <string name="passphrase_empty_error_message">Пожалуйста, введите мнемоническую фразу</string>
     <string name="passphrase_passphrase_too_weak">Парольная фраза слишком простая</string>
-    <string name="keys_backup_passphrase_not_empty_error_message">Пожалуйста, удалите парольную фразу, если хотите, чтобы ${app_name} сгенерировал ключ восстановления.</string>
+    <string name="keys_backup_passphrase_not_empty_error_message">Пожалуйста, удалите мнемоническую фразу, если хотите, чтобы ${app_name} сгенерировал ключ восстановления.</string>
     <string name="keys_backup_setup_step1_title">Никогда не теряйте зашифрованных сообщений</string>
     <string name="keys_backup_setup_step1_description">Сообщения в зашифрованных комнатах защищены сквозным шифрованием. Ключи для прочтения этих сообщений есть только у вас и получателя(ей).
 \n
@@ -815,9 +815,9 @@
     <string name="keys_backup_banner_in_progress">Резервное копирование ключей…</string>
     <string name="keys_backup_banner_recover_line1">Никогда не теряйте зашифрованные сообщения</string>
     <string name="keys_backup_setup_step3_share_recovery_file">Поделиться</string>
-    <string name="keys_backup_setup_step3_button_title_no_passphrase">Я сделал копию</string>
+    <string name="keys_backup_setup_step3_button_title_no_passphrase">Я сделал(а) копию</string>
     <string name="keys_backup_setup_step3_text_line2_no_passphrase">Храните ключ восстановления в надежном месте, например, в диспетчере паролей (или в сейфе)</string>
-    <string name="keys_backup_setup_step2_text_title">Защитите резервную копию парольной фразой.</string>
+    <string name="keys_backup_setup_step2_text_title">Защитите резервную копию мнемонической фразой.</string>
     <string name="encryption_message_recovery">Восстановление зашифрованных сообщений</string>
     <string name="keys_backup_setup">Начать использовать резервное копирование ключей</string>
     <string name="keys_backup_banner_recover_line2">Использовать резервное копирование ключей</string>
@@ -1222,7 +1222,7 @@
     <string name="verification_cannot_access_other_session">Используйте пароль восстановления или ключ</string>
     <string name="e2e_use_keybackup">Разблокировать историю зашифрованных сообщений</string>
     <string name="verify_cancelled_notice">Проверка была отменена. Вы можете начать проверку снова.</string>
-    <string name="recovery_passphrase">Парольная фраза для восстановления</string>
+    <string name="recovery_passphrase">Мнемоническая фраза для восстановления</string>
     <string name="enter_account_password">Введите %s, чтобы продолжить.</string>
     <string name="bootstrap_dont_reuse_pwd">Не переиспользуйте пароль учётной записи.</string>
     <string name="bootstrap_loading_text">Это может занять несколько секунд, пожалуйста, наберитесь терпения.</string>
@@ -1429,9 +1429,9 @@
     <string name="verification_emoji_notice">Сравните уникальные эмодзи, убедившись, что они появились в том же порядке.</string>
     <string name="verification_code_notice">Сравните код с тем, который отображается на экране другого пользователя.</string>
     <string name="verification_conclusion_ok_notice">Сообщения от этого пользователя зашифрованы сквозным шифрованием и не смогут быть прочитаны третьими лицами.</string>
-    <string name="verification_conclusion_ok_self_notice">Ваша новая сессия подтверждена. У нее есть доступ к вашим зашифрованным сообщениям, а другие пользователи увидят его как доверенное.</string>
+    <string name="verification_conclusion_ok_self_notice">Ваша новая сессия подтверждена. Она имеет доступ к вашим зашифрованным сообщениям, и другие пользователи будут воспринимать её как заверенную.</string>
     <string name="encryption_information_cross_signing_state">Перекрёстная подпись</string>
-    <string name="encryption_information_dg_xsigning_complete">Перекрестная подпись включена
+    <string name="encryption_information_dg_xsigning_complete">Перекрёстная подпись включена
 \nЛичные ключи хранятся на устройстве.</string>
     <string name="encryption_information_dg_xsigning_trusted">Перекрестная подпись включена
 \nКлючи являются доверенными.
@@ -2756,4 +2756,14 @@
     <string name="command_description_table_flip">Добавляет (╯°□°)╯︵ ┻━┻ в начало сообщения</string>
     <string name="settings_security_incognito_keyboard_title">Приватная клавиатура</string>
     <string name="settings_security_incognito_keyboard_summary">Запрещает клавиатуре обновлять персональные данные, такие как история набора текста и словарь, на основе того, что вы набрали при общении. Обратите внимание, что некоторые клавиатуры могут не соблюдать эту настройку.</string>
+    <string name="action_got_it">Понятно</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 В настройках безопасности вы включили шифрование только для заверенных сессий во всех комнатах.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Не отправлять зашифрованные сообщения незаверенным сессиям в этой комнате.</string>
+    <string name="device_manager_learn_more_sessions_inactive">Неактивные сессии — это сессии, которыми вы не пользовались определенное время, но они продолжают получать ключи шифрования.
+\n
+\nУдаление неактивных сессий повышает безопасность и производительность, а также облегчает выявление подозрительных новых сессий.</string>
+    <string name="device_manager_learn_more_session_rename_title">Переименование сессий</string>
+    <string name="device_manager_learn_more_session_rename">Другие пользователи в личных сообщениях и комнатах, к которым вы присоединились, могут просматривать весь список ваших сессий.
+\n
+\nЭто даёт им уверенность в том, что они действительно общаются с вами, но это также означает, что они могут видеть название сессии, которое вы ввели здесь.</string>
 </resources>
\ No newline at end of file

From fdc55965ca0e3328d6ed4f32b00bde97f00941b9 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 17:23:26 +0100
Subject: [PATCH 199/400] Linting

---
 .../matrix/android/sdk/api/rendezvous/RendezvousChannel.kt   | 5 ++++-
 .../app/features/login/qr/QrCodeLoginStatusFragment.kt       | 3 ++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 588d034f10..9d2843ce66 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -19,6 +19,9 @@ package org.matrix.android.sdk.api.rendezvous
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
+/**
+ * Representation of a rendezvous channel such as that described by MSC3903
+ */
 interface RendezvousChannel {
     var transport: RendezvousTransport
 
@@ -44,7 +47,7 @@ interface RendezvousChannel {
      */
     suspend fun close()
 
-    // TODO: this should be transport independent in the future
+    // In future we probably want this to be a more generic RendezvousCode but it is suffice for now
     suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
     suspend fun cancel(reason: RendezvousFailureReason)
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 556be3af4d..f0030d6763 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -80,7 +80,8 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
 
     private fun getErrorCode(reason: RendezvousFailureReason): String {
         return when (reason) {
-            RendezvousFailureReason.UnsupportedAlgorithm, RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            RendezvousFailureReason.UnsupportedAlgorithm,
+            RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
             RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
             RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
             else -> getString(R.string.qr_code_login_header_failed_other_description)

From bfe3daa37fda9cd42bc541b5c13c4f75bb58e221 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 17:25:06 +0100
Subject: [PATCH 200/400] Fix compile error from bad merge

---
 .../vector/app/features/login/qr/QrCodeLoginViewModel.kt   | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 7a51098439..af0df22ca3 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -24,8 +24,8 @@ import dagger.assisted.AssistedInject
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
-import im.vector.app.core.extensions.configureAndStart
 import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.core.session.ConfigureAndStartSessionUseCase
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
@@ -37,7 +37,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
         private val applicationContext: Context,
         private val authenticationService: AuthenticationService,
-        private val activeSessionHolder: ActiveSessionHolder
+        private val activeSessionHolder: ActiveSessionHolder,
+        private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
     val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
@@ -97,7 +98,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
                         activeSessionHolder.setActiveSession(session)
                         authenticationService.reset()
 
-                        session.configureAndStart(applicationContext)
+                        configureAndStartSessionUseCase.execute(session, startSyncing)
 
                         rendezvous.completeVerificationOnNewDevice(session)
 

From ba49ee4e1314ff89ca3f9e71f529eec13b28d104 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 14 Oct 2022 18:53:54 +0200
Subject: [PATCH 201/400] Remove unused string
 `device_manager_settings_active_sessions_show_all`

---
 library/ui-strings/src/main/res/values-ca/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-cs/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-de/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-es/strings.xml     | 1 -
 library/ui-strings/src/main/res/values-et/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-fa/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-fr/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-hu/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-in/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-it/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-nl/strings.xml     | 1 -
 library/ui-strings/src/main/res/values-pl/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-pt-rBR/strings.xml | 3 +--
 library/ui-strings/src/main/res/values-ru/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-sk/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-sv/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-uk/strings.xml     | 3 +--
 library/ui-strings/src/main/res/values-zh-rCN/strings.xml | 3 +--
 library/ui-strings/src/main/res/values-zh-rTW/strings.xml | 3 +--
 library/ui-strings/src/main/res/values/strings.xml        | 2 --
 20 files changed, 17 insertions(+), 38 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ca/strings.xml b/library/ui-strings/src/main/res/values-ca/strings.xml
index 05795417dd..eddae8297e 100644
--- a/library/ui-strings/src/main/res/values-ca/strings.xml
+++ b/library/ui-strings/src/main/res/values-ca/strings.xml
@@ -2616,7 +2616,6 @@
     <string name="home_layout_preferences_sort_by">Ordena per</string>
     <string name="home_layout_preferences_recents">Mostra recents</string>
     <string name="home_layout_preferences_filters">Mostra filtres</string>
-    <string name="device_manager_settings_active_sessions_show_all">Mostra totes les sessions (V2, WIP)</string>
     <string name="create_room">Crea sala</string>
     <string name="start_chat">Inicia xat</string>
     <string name="device_manager_other_sessions_description_verified">Verificada · Última activitat %1$s</string>
@@ -2743,4 +2742,4 @@
     <string name="settings_security_incognito_keyboard_summary">Sol·licita que no es desi cap dada personalitzada del teclat en funció del que escrius a les converses (per exemple l\'historial d\'escriptura o el diccionari). Tingues en compte que alguns teclats poden no respectar aquesta configuració.</string>
     <string name="settings_security_incognito_keyboard_title">Teclat incògnit</string>
     <string name="room_settings_global_block_unverified_info_text">🔒 Has activat el xifrat a només en sessions verificades a totes les sales, a Configuració &gt; Seguretat.</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-cs/strings.xml b/library/ui-strings/src/main/res/values-cs/strings.xml
index 7f8afdad2f..ffa7123dae 100644
--- a/library/ui-strings/src/main/res/values-cs/strings.xml
+++ b/library/ui-strings/src/main/res/values-cs/strings.xml
@@ -2650,7 +2650,6 @@
 \nTento domovský server nemusí být nakonfigurován pro zobrazování map.</string>
     <string name="a11y_open_settings">Otevřít nastavení</string>
     <string name="all_chats">Všechny konverzace</string>
-    <string name="device_manager_settings_active_sessions_show_all">Zobrazit všechny relace (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">V zájmu co nejlepšího zabezpečení ověřujte své relace a odhlašujte se ze všech relací, které již nepoznáváte nebo nepoužíváte.</string>
     <string name="device_manager_sessions_other_title">Ostatní relace</string>
     <string name="settings_sessions_list">Relace</string>
@@ -2797,4 +2796,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ V této místnosti jsou neověřená zařízení, která nebudou schopna dešifrovat odeslané zprávy.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Nikdy neodesílat šifrované zprávy do neověřených relací v této místnosti.</string>
     <string name="action_got_it">Rozumím</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index b0f1ebe2e7..da59f03e18 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -2586,7 +2586,6 @@
     <string name="room_list_filter_unreads">Ungelesene</string>
     <string name="room_list_filter_people">Personen</string>
     <string name="send_your_first_msg_to_invite">Schreibe deine erste Nachricht, um %s zur Unterhaltung einzuladen</string>
-    <string name="device_manager_settings_active_sessions_show_all">Alle Sitzungen anzeigen (V2, in Arbeit)</string>
     <string name="device_manager_sessions_other_description">Für bestmögliche Sicherheit verifiziere deine Sitzungen und melde dich von allen ab, die du nicht erkennst oder nutzt.</string>
     <string name="device_manager_sessions_other_title">Andere Sitzungen</string>
     <string name="settings_sessions_list">Sitzungen</string>
@@ -2742,4 +2741,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Es befinden sich nicht verifizierte Geräte in diesem Raum. Sie werden deine Nachrichten nicht entschlüsseln können.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Niemals verschlüsselte Nachrichten zu unverifizierten Sitzungen in diesem Raum senden.</string>
     <string name="action_got_it">Verstanden</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-es/strings.xml b/library/ui-strings/src/main/res/values-es/strings.xml
index bc4299c1bd..f73c4952c6 100644
--- a/library/ui-strings/src/main/res/values-es/strings.xml
+++ b/library/ui-strings/src/main/res/values-es/strings.xml
@@ -2548,7 +2548,6 @@
     <string name="a11y_device_manager_device_type_desktop">Escritorio</string>
     <string name="a11y_device_manager_device_type_web">Web</string>
     <string name="a11y_device_manager_device_type_mobile">Móvil</string>
-    <string name="device_manager_settings_active_sessions_show_all">Mostrar todas las sesiones (V2, WIP)</string>
     <string name="labs_enable_element_call_permission_shortcuts_summary">Auto aprovar widgets de Element Call y dar permisos de cámara y micrófono</string>
     <plurals name="room_removed_messages">
         <item quantity="one">%d mensaje borrado</item>
diff --git a/library/ui-strings/src/main/res/values-et/strings.xml b/library/ui-strings/src/main/res/values-et/strings.xml
index 10453a4fc9..d0f3b540e2 100644
--- a/library/ui-strings/src/main/res/values-et/strings.xml
+++ b/library/ui-strings/src/main/res/values-et/strings.xml
@@ -2591,7 +2591,6 @@
 \nSee koduserver ei pruugi olla seadistatud kuvama kaarte.</string>
     <string name="a11y_open_settings">Ava seadistused</string>
     <string name="all_chats">Kõik vestlused</string>
-    <string name="device_manager_settings_active_sessions_show_all">Näita kõiki sessioone (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">Parima turvalisuse nimel verifitseeri kõik oma sessioonid ning logi välja neist, mida sa enam ei kasuta.</string>
     <string name="device_manager_sessions_other_title">Muud sessioonid</string>
     <string name="settings_sessions_list">Sessioonid</string>
@@ -2734,4 +2733,4 @@
     <string name="command_description_devtools">Ava arendaja töövahendite vaade</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Ära iialgi saada selles jututoas krüptitud sõnumeid verifitseerimata sessioonidesse.</string>
     <string name="action_got_it">Selge lugu</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-fa/strings.xml b/library/ui-strings/src/main/res/values-fa/strings.xml
index 1191e98df9..47cade0bf8 100644
--- a/library/ui-strings/src/main/res/values-fa/strings.xml
+++ b/library/ui-strings/src/main/res/values-fa/strings.xml
@@ -2600,7 +2600,6 @@
 \nشاید این کارساز خانگی برای نمایش نقشه‌ها پیکربندی نشده باشد.</string>
     <string name="a11y_open_settings">گشودن تنظیمات</string>
     <string name="all_chats">تمامی گپ‌ها</string>
-    <string name="device_manager_settings_active_sessions_show_all">نمایش تمامی نشست‌ها (ن۲، دح‌ت)</string>
     <string name="device_manager_sessions_other_description">برای امنیت بیش‌تر، نشست‌هایتان را تأیید و از هر نشستی که تشخیصش نمی‌دهید یا دیگر استفاده نمی‌کنید خارج شوید.</string>
     <string name="device_manager_sessions_other_title">دیگر نشست‌ها</string>
     <string name="settings_sessions_list">نشست‌ها</string>
@@ -2721,4 +2720,4 @@
     <string name="command_description_devtools">گشودن صفحهٔ ابزارهای توسعه‌دهنده</string>
     <string name="labs_enable_deferred_dm_title">به کار انداختن پیام‌های مستقیم تعویقی</string>
     <string name="action_got_it">گرفتم</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-fr/strings.xml b/library/ui-strings/src/main/res/values-fr/strings.xml
index cd7d7e14a8..860840486e 100644
--- a/library/ui-strings/src/main/res/values-fr/strings.xml
+++ b/library/ui-strings/src/main/res/values-fr/strings.xml
@@ -2600,7 +2600,6 @@
 \nCe serveur d’accueil n’a peut-être pas été configuré pour afficher les cartes.</string>
     <string name="a11y_open_settings">Ouvrir les paramètres</string>
     <string name="all_chats">Toutes les conversations</string>
-    <string name="device_manager_settings_active_sessions_show_all">Afficher toutes les sessions (V2, en cours)</string>
     <string name="device_manager_sessions_other_description">Pour une meilleure sécurité, vérifiez vos sessions et déconnectez toutes les sessions que vous ne connaissez pas ou que vous n’utilisez plus.</string>
     <string name="device_manager_sessions_other_title">Autres sessions</string>
     <string name="settings_sessions_list">Sessions</string>
@@ -2743,4 +2742,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Il y a des appareils non vérifiés dans ce salon, ils ne pourront pas déchiffrer vos messages envoyés.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Ne jamais envoyer de messages chiffrés aux sessions non vérifiées dans ce salon.</string>
     <string name="action_got_it">Compris</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-hu/strings.xml b/library/ui-strings/src/main/res/values-hu/strings.xml
index cac0a2eb5d..d2e9568053 100644
--- a/library/ui-strings/src/main/res/values-hu/strings.xml
+++ b/library/ui-strings/src/main/res/values-hu/strings.xml
@@ -2611,7 +2611,6 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="a11y_device_manager_device_type_desktop">Asztali</string>
     <string name="a11y_device_manager_device_type_web">Web</string>
     <string name="a11y_device_manager_device_type_mobile">Mobil</string>
-    <string name="device_manager_settings_active_sessions_show_all">Minden munkamenet megjelenítése (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">A legjobb biztonság érdekében ellenőrizd a munkameneteket, és jelentkezz ki minden olyan munkamenetből, melyet már nem ismersz fel vagy nem használsz.</string>
     <string name="device_manager_sessions_other_title">Más munkamenetek</string>
     <string name="settings_sessions_list">Munkamenetek</string>
@@ -2710,4 +2709,4 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="labs_enable_deferred_dm_title">Késleltetett közvetlen üzenetek engedélyezése</string>
     <string name="labs_enable_new_app_layout_summary">Egyszerűsített Element opcionálisan lapokkal</string>
     <string name="labs_enable_new_app_layout_title">Új kinézet engedélyezése</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-in/strings.xml b/library/ui-strings/src/main/res/values-in/strings.xml
index 33772bf660..7a389a3fca 100644
--- a/library/ui-strings/src/main/res/values-in/strings.xml
+++ b/library/ui-strings/src/main/res/values-in/strings.xml
@@ -2552,7 +2552,6 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="search_space_two_parents">%1$s dan %2$s</string>
     <string name="auth_reset_password_error_unverified">Email belum diverifikasi, periksa kotak masuk Anda</string>
     <string name="all_chats">Semua Obrolan</string>
-    <string name="device_manager_settings_active_sessions_show_all">Tampilkan Semua Sesi (V2, Dalam Pengembangan)</string>
     <string name="device_manager_sessions_other_description">Untuk keamanan terbaik, verifikasi sesi Anda dan keluarkan sesi apa pun yang Anda tidak kenal atau Anda tidak gunakan lagi.</string>
     <string name="device_manager_sessions_other_title">Sesi lainnya</string>
     <string name="settings_sessions_list">Sesi</string>
@@ -2691,4 +2690,4 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Ada perangkat yang belum diverifikasi di ruangan ini, mereka tidak akan mendekripsikan pesan yang Anda kirim.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Jangan kirim pesan terenkripsi ke sesi yang belum diverifikasi di ruangan ini.</string>
     <string name="action_got_it">Saya mengerti</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-it/strings.xml b/library/ui-strings/src/main/res/values-it/strings.xml
index 2b78078ff4..f65aca5bb7 100644
--- a/library/ui-strings/src/main/res/values-it/strings.xml
+++ b/library/ui-strings/src/main/res/values-it/strings.xml
@@ -2591,7 +2591,6 @@
 \nQuesto homeserver potrebbe non essere configurato per mostrare mappe.</string>
     <string name="a11y_open_settings">Apri le impostazioni</string>
     <string name="all_chats">Tutte le chat</string>
-    <string name="device_manager_settings_active_sessions_show_all">Mostra tutte le sessioni (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">Per una maggiore sicurezza, verifica le tue sessioni e disconnetti quelle che non riconosci o che non usi più.</string>
     <string name="device_manager_sessions_other_title">Altre sessioni</string>
     <string name="settings_sessions_list">Sessioni</string>
@@ -2734,4 +2733,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Ci sono dispositivi non verificati in questa stanza, non potranno decifrare i messaggi che invii.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Non inviare mai messaggi cifrati a sessioni non verificate in questa stanza.</string>
     <string name="action_got_it">Capito</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-nl/strings.xml b/library/ui-strings/src/main/res/values-nl/strings.xml
index ce122b0646..e8ede9b079 100644
--- a/library/ui-strings/src/main/res/values-nl/strings.xml
+++ b/library/ui-strings/src/main/res/values-nl/strings.xml
@@ -2596,7 +2596,6 @@
     </plurals>
     <string name="search_space_two_parents">%1$s en %2$s</string>
     <string name="auth_reset_password_error_unverified">E-mailadres niet geverifieerd, controleer je inbox</string>
-    <string name="device_manager_settings_active_sessions_show_all">Alle sessies weergeven (V2, WIP)</string>
     <string name="location_share_loading_map_error">Kan kaart niet laden
 \nDeze server is mogelijk niet geconfigureerd om kaarten weer te geven.</string>
     <string name="a11y_open_settings">Open instellingen</string>
diff --git a/library/ui-strings/src/main/res/values-pl/strings.xml b/library/ui-strings/src/main/res/values-pl/strings.xml
index c9bac8977b..ab9c367824 100644
--- a/library/ui-strings/src/main/res/values-pl/strings.xml
+++ b/library/ui-strings/src/main/res/values-pl/strings.xml
@@ -2688,7 +2688,6 @@
     <string name="auth_reset_password_error_unverified">Email nie został zweryfikowany, sprawdź swoją skrzynkę</string>
     <string name="settings_troubleshoot_test_endpoint_registration_failed">Nie udało się zarejestrować tokena punktu końcowego na serwerze domowym:
 \n%1$s</string>
-    <string name="device_manager_settings_active_sessions_show_all">Wyświetl wszystkie sesje (V2, WIP)</string>
     <string name="settings_troubleshoot_test_current_gateway">Bieżąca brama: %s</string>
     <string name="settings_troubleshoot_test_current_gateway_title">Wejście</string>
     <string name="settings_troubleshoot_test_current_endpoint_failed">Nie można znaleźć punktu końcowego.</string>
@@ -2744,4 +2743,4 @@
     <string name="home_empty_space_no_rooms_title">%s
 \nwygląda nieco pusto.</string>
     <string name="space_list_empty_title">Brak przestrzeni.</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
index 8d89aa29bd..e0d6e6aa86 100644
--- a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
+++ b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
@@ -2600,7 +2600,6 @@
 \nEste servidor casa pode não estar configurado para exibir mapas.</string>
     <string name="a11y_open_settings">Abrir configurações</string>
     <string name="all_chats">Todos os Chats</string>
-    <string name="device_manager_settings_active_sessions_show_all">Mostrar Todas Sessões (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">Para a melhor segurança, verifique suas sessões e faça signout de qualquer sessão que você não reconhece ou usa mais.</string>
     <string name="device_manager_sessions_other_title">Outras sessões</string>
     <string name="settings_sessions_list">Sessões</string>
@@ -2743,4 +2742,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Existem dispositivos não-verificados nesta sala, eles não vão ser capazes de decriptar mensagens que você enviar.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Nunca enviar mensagens encriptadas a sessões não-verificadas nesta sala.</string>
     <string name="action_got_it">Entendido</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml
index 4c00be4e3e..8d19e8d9d0 100644
--- a/library/ui-strings/src/main/res/values-ru/strings.xml
+++ b/library/ui-strings/src/main/res/values-ru/strings.xml
@@ -2664,7 +2664,6 @@
     <string name="device_manager_sessions_other_title">Другие сессии</string>
     <string name="settings_sessions_list">Сессии</string>
     <string name="a11y_create_message">Создать беседу или комнату</string>
-    <string name="device_manager_settings_active_sessions_show_all">Показать все сессии (V2, в разработке)</string>
     <string name="room_list_filter_people">ЛС</string>
     <string name="home_layout_preferences">Настройки вида</string>
     <string name="home_layout_preferences_filters">Фильтры</string>
@@ -2766,4 +2765,4 @@
     <string name="device_manager_learn_more_session_rename">Другие пользователи в личных сообщениях и комнатах, к которым вы присоединились, могут просматривать весь список ваших сессий.
 \n
 \nЭто даёт им уверенность в том, что они действительно общаются с вами, но это также означает, что они могут видеть название сессии, которое вы ввели здесь.</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-sk/strings.xml b/library/ui-strings/src/main/res/values-sk/strings.xml
index 467fc8b06f..d4c3f8c40c 100644
--- a/library/ui-strings/src/main/res/values-sk/strings.xml
+++ b/library/ui-strings/src/main/res/values-sk/strings.xml
@@ -2650,7 +2650,6 @@
 \nTento domovský server nemusí byť nakonfigurovaný na zobrazovanie máp.</string>
     <string name="a11y_open_settings">Otvoriť nastavenia</string>
     <string name="all_chats">Všetky konverzácie</string>
-    <string name="device_manager_settings_active_sessions_show_all">Zobraziť všetky relácie (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">V záujme čo najlepšieho zabezpečenia, overte svoje relácie a odhláste sa z každej relácie, ktorú už nepoznáte alebo nepoužívate.</string>
     <string name="device_manager_sessions_other_title">Iné relácie</string>
     <string name="settings_sessions_list">Relácie</string>
@@ -2797,4 +2796,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ V tejto miestnosti sa nachádzajú neoverené zariadenia, ktoré nebudú schopné dešifrovať odoslané správy.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Nikdy neposielať šifrované správy do neoverených relácií v tejto miestnosti.</string>
     <string name="action_got_it">Rozumiem</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-sv/strings.xml b/library/ui-strings/src/main/res/values-sv/strings.xml
index 89697e74ea..bf083a1117 100644
--- a/library/ui-strings/src/main/res/values-sv/strings.xml
+++ b/library/ui-strings/src/main/res/values-sv/strings.xml
@@ -2626,7 +2626,6 @@
     <string name="a11y_device_manager_device_type_mobile">Mobil</string>
     <string name="device_manager_sessions_other_description">För bäst säkerhet, verifiera dina sessioner och logga ut från alla sessioner du inte känner igen eller använder längre.</string>
     <string name="device_manager_sessions_other_title">Andra sessioner</string>
-    <string name="device_manager_settings_active_sessions_show_all">Visa alla sessioner (V2, WIP)</string>
     <string name="labs_enable_element_call_permission_shortcuts_summary">Godkänn automatiskt Element Call-widgets och ge kamera-/mikrofonåtkomst</string>
     <string name="labs_enable_element_call_permission_shortcuts">Aktivera Element Call-behörighetsgenvägar</string>
     <string name="tooltip_attachment_voice_broadcast">Starta en röstsändning</string>
@@ -2652,4 +2651,4 @@
     <string name="room_list_filter_favourites">Favoriter</string>
     <string name="room_list_filter_unreads">Olästa</string>
     <string name="room_list_filter_all">Alla</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-uk/strings.xml b/library/ui-strings/src/main/res/values-uk/strings.xml
index 9b5cff2140..45ee44213c 100644
--- a/library/ui-strings/src/main/res/values-uk/strings.xml
+++ b/library/ui-strings/src/main/res/values-uk/strings.xml
@@ -2700,7 +2700,6 @@
 \nМожливо, цей домашній сервер не налаштовано для показу карт.</string>
     <string name="a11y_open_settings">Відкрити налаштування</string>
     <string name="all_chats">Усі бесіди</string>
-    <string name="device_manager_settings_active_sessions_show_all">Показати всі сеанси (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">Звірте свої сеанси та вийдіть з усіх сеансів, які ви більше не розпізнаєте або не використовуєте для кращої безпеки.</string>
     <string name="device_manager_sessions_other_title">Інші сеанси</string>
     <string name="settings_sessions_list">Сеанси</string>
@@ -2851,4 +2850,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ У цій кімнаті є неперевірені пристрої, вони не зможуть розшифрувати повідомлення, які ви надсилаєте.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Ніколи не надсилати зашифровані повідомлення на неперевірені сеанси в цій кімнаті.</string>
     <string name="action_got_it">Зрозуміло</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
index b0a1eb1402..39992ff418 100644
--- a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
@@ -2550,7 +2550,6 @@
 \n此主服务器可能没有设置好显示地图。</string>
     <string name="a11y_open_settings">打开设置</string>
     <string name="all_chats">全部聊天</string>
-    <string name="device_manager_settings_active_sessions_show_all">显示全部会话(V2, WIP)</string>
     <string name="device_manager_sessions_other_description">为获得最佳安全性,请验证你的会话,并从任何你不认识或不再使用的会话登出。</string>
     <string name="device_manager_sessions_other_title">其他会话</string>
     <string name="settings_sessions_list">会话</string>
@@ -2624,4 +2623,4 @@
     <string name="labs_enable_deferred_dm_summary">仅在首条消息创建私聊消息</string>
     <string name="labs_enable_deferred_dm_title">启用延迟的私聊消息</string>
     <string name="labs_enable_new_app_layout_summary">简化的Element,带有可选的标签</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
index 096bf9e36f..e3cd44adca 100644
--- a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
@@ -2550,7 +2550,6 @@
 \n此家伺服器可能未設定好顯示地圖。</string>
     <string name="a11y_open_settings">開啟設定</string>
     <string name="all_chats">所有聊天</string>
-    <string name="device_manager_settings_active_sessions_show_all">顯示所有工作階段 (V2, WIP)</string>
     <string name="device_manager_sessions_other_description">為了取得最佳安全性,請驗證您的工作階段並登出任何您無法識別或不再使用的工作階段。</string>
     <string name="device_manager_sessions_other_title">其他工作階段</string>
     <string name="settings_sessions_list">工作階段</string>
@@ -2689,4 +2688,4 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ 此聊天室中有未驗證的裝置,它們將無法解密您傳送的訊息。</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">切莫向此聊天室中未經驗證的工作階段傳送加密訊息。</string>
     <string name="action_got_it">知道了</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 20d00563dc..549d176ad3 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3244,8 +3244,6 @@
     <string name="labs_enable_element_call_permission_shortcuts_summary">Auto-approve Element Call widgets and grant camera / mic access</string>
 
     <!-- Device Manager -->
-    <!-- TODO remove this key -->
-    <string name="device_manager_settings_active_sessions_show_all" tools:ignore="UnusedResources">Show All Sessions (V2, WIP)</string>
     <string name="device_manager_sessions_other_title">Other sessions</string>
     <string name="device_manager_sessions_other_description">For best security, verify your sessions and sign out from any session that you don’t recognize or use anymore.</string>
     <string name="a11y_device_manager_device_type_mobile">Mobile</string>

From a3fc78594502953d86944d3f1ec0509c13921e7d Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Fri, 14 Oct 2022 18:52:42 +0100
Subject: [PATCH 202/400] Fix missing param

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index af0df22ca3..81a00cf548 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -98,7 +98,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
                         activeSessionHolder.setActiveSession(session)
                         authenticationService.reset()
 
-                        configureAndStartSessionUseCase.execute(session, startSyncing)
+                        configureAndStartSessionUseCase.execute(session)
 
                         rendezvous.completeVerificationOnNewDevice(session)
 

From 75c97bc7c5a4bdbe93f6e052ec9ccc4d3007e8f4 Mon Sep 17 00:00:00 2001
From: Eric Decanini <eddecanini@gmail.com>
Date: Fri, 14 Oct 2022 15:02:11 -0400
Subject: [PATCH 203/400] Device Manager Notification and Pusher Fixes (#7370)

* Fixes existing pushers being overwritten on app startup

* Refreshes pushers in SessionOverview screen

* Fixes push toggle not working for non pusher sessions

* Various code improvements

* Further code improvements for safety

* Fixes CI errors
---
 .../vector/app/features/home/HomeActivity.kt  |  2 +-
 .../features/home/HomeActivityViewModel.kt    |  8 +++
 .../v2/overview/SessionOverviewFragment.kt    | 20 ++----
 .../v2/overview/SessionOverviewViewModel.kt   | 33 ++++++---
 .../v2/overview/SessionOverviewViewState.kt   |  3 +-
 .../overview/TogglePushNotificationUseCase.kt | 45 +++++++++++++
 .../app/core/pushers/PushersManagerTest.kt    |  2 +-
 .../overview/SessionOverviewViewModelTest.kt  | 38 ++++++-----
 .../TogglePushNotificationUseCaseTest.kt      | 67 +++++++++++++++++++
 .../app/test/fakes/FakePushersService.kt      | 13 +---
 .../fakes/FakeSessionAccountDataService.kt    |  2 +-
 .../FakeTogglePushNotificationUseCase.kt      | 33 +++++++++
 12 files changed, 211 insertions(+), 55 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCase.kt
 create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCaseTest.kt
 create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt

diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 228d6c5547..552b78bbb8 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -218,7 +218,7 @@ class HomeActivity :
                 fcmHelper.ensureFcmTokenIsRetrieved(
                         this,
                         pushersManager,
-                        vectorPreferences.areNotificationEnabledForDevice()
+                        homeActivityViewModel.shouldAddHttpPusher()
                 )
             }
         }
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
index 595878ecc1..61a8e5b79e 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
@@ -143,6 +143,14 @@ class HomeActivityViewModel @AssistedInject constructor(
         }
     }
 
+    fun shouldAddHttpPusher() = if (vectorPreferences.areNotificationEnabledForDevice()) {
+        val currentSession = activeSessionHolder.getActiveSession()
+        val currentPushers = currentSession.pushersService().getPushers()
+        currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId }
+    } else {
+        false
+    }
+
     fun observeLocalNotificationsSilenced() {
         val currentSession = activeSessionHolder.getActiveSession()
         val deviceId = currentSession.cryptoService().getMyDevice().deviceId
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
index 463d5bb495..a1cd7ea586 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt
@@ -47,7 +47,6 @@ import im.vector.app.features.workers.signout.SignOutUiWorker
 import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
-import org.matrix.android.sdk.api.session.pushers.Pusher
 import javax.inject.Inject
 
 /**
@@ -178,7 +177,7 @@ class SessionOverviewFragment :
         updateEntryDetails(state.deviceId)
         updateSessionInfo(state)
         updateLoading(state.isLoading)
-        updatePushNotificationToggle(state.deviceId, state.pushers.invoke().orEmpty())
+        updatePushNotificationToggle(state.deviceId, state.notificationsEnabled)
     }
 
     private fun updateToolbar(viewState: SessionOverviewViewState) {
@@ -219,18 +218,13 @@ class SessionOverviewFragment :
         }
     }
 
-    private fun updatePushNotificationToggle(deviceId: String, pushers: List<Pusher>) {
+    private fun updatePushNotificationToggle(deviceId: String, enabled: Boolean) {
         views.sessionOverviewPushNotifications.apply {
-            if (pushers.isEmpty()) {
-                isVisible = false
-            } else {
-                val allPushersAreEnabled = pushers.all { it.enabled }
-                setOnCheckedChangeListener(null)
-                setChecked(allPushersAreEnabled)
-                post {
-                    setOnCheckedChangeListener { _, isChecked ->
-                        viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked))
-                    }
+            setOnCheckedChangeListener(null)
+            setChecked(enabled)
+            post {
+                setOnCheckedChangeListener { _, isChecked ->
+                    viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked))
                 }
             }
         }
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt
index a7b0435e29..21054270f8 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModel.kt
@@ -36,24 +36,27 @@ import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSes
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
 import org.matrix.android.sdk.api.auth.UIABaseAuth
 import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
 import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.failure.Failure
-import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS
 import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
+import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
 import org.matrix.android.sdk.flow.flow
+import org.matrix.android.sdk.flow.unwrap
 import timber.log.Timber
 import javax.net.ssl.HttpsURLConnection
 import kotlin.coroutines.Continuation
 
 class SessionOverviewViewModel @AssistedInject constructor(
         @Assisted val initialState: SessionOverviewViewState,
-        private val session: Session,
         private val stringProvider: StringProvider,
         private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
         private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
@@ -61,6 +64,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
         private val interceptSignoutFlowResponseUseCase: InterceptSignoutFlowResponseUseCase,
         private val pendingAuthHandler: PendingAuthHandler,
         private val activeSessionHolder: ActiveSessionHolder,
+        private val togglePushNotificationUseCase: TogglePushNotificationUseCase,
         refreshDevicesUseCase: RefreshDevicesUseCase,
 ) : VectorSessionsListViewModel<SessionOverviewViewState, SessionOverviewAction, SessionOverviewViewEvent>(
         initialState, activeSessionHolder, refreshDevicesUseCase
@@ -74,11 +78,16 @@ class SessionOverviewViewModel @AssistedInject constructor(
     }
 
     init {
+        refreshPushers()
         observeSessionInfo(initialState.deviceId)
         observeCurrentSessionInfo()
         observePushers(initialState.deviceId)
     }
 
+    private fun refreshPushers() {
+        activeSessionHolder.getSafeActiveSession()?.pushersService()?.refreshPushers()
+    }
+
     private fun observeSessionInfo(deviceId: String) {
         getDeviceFullInfoUseCase.execute(deviceId)
                 .onEach { setState { copy(deviceInfo = Success(it)) } }
@@ -99,10 +108,20 @@ class SessionOverviewViewModel @AssistedInject constructor(
     }
 
     private fun observePushers(deviceId: String) {
-        session.flow()
+        val session = activeSessionHolder.getSafeActiveSession() ?: return
+        val pusherFlow = session.flow()
                 .livePushers()
                 .map { it.filter { pusher -> pusher.deviceId == deviceId } }
-                .execute { copy(pushers = it) }
+                .map { it.takeIf { it.isNotEmpty() }?.any { pusher -> pusher.enabled } }
+
+        val accountDataFlow = session.flow()
+                .liveUserAccountData(TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId)
+                .unwrap()
+                .map { it.content.toModel<LocalNotificationSettingsContent>()?.isSilenced?.not() }
+
+        merge(pusherFlow, accountDataFlow)
+                .onEach { it?.let { setState { copy(notificationsEnabled = it) } } }
+                .launchIn(viewModelScope)
     }
 
     override fun handle(action: SessionOverviewAction) {
@@ -213,10 +232,8 @@ class SessionOverviewViewModel @AssistedInject constructor(
 
     private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) {
         viewModelScope.launch {
-            val devicePushers = awaitState().pushers.invoke()?.filter { it.deviceId == action.deviceId }
-            devicePushers?.forEach { pusher ->
-                session.pushersService().togglePusher(pusher, action.enabled)
-            }
+            togglePushNotificationUseCase.execute(action.deviceId, action.enabled)
+            setState { copy(notificationsEnabled = action.enabled) }
         }
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt
index c2d4a858b3..440805bad6 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt
@@ -20,14 +20,13 @@ import com.airbnb.mvrx.Async
 import com.airbnb.mvrx.MavericksState
 import com.airbnb.mvrx.Uninitialized
 import im.vector.app.features.settings.devices.v2.DeviceFullInfo
-import org.matrix.android.sdk.api.session.pushers.Pusher
 
 data class SessionOverviewViewState(
         val deviceId: String,
         val isCurrentSessionTrusted: Boolean = false,
         val deviceInfo: Async<DeviceFullInfo> = Uninitialized,
         val isLoading: Boolean = false,
-        val pushers: Async<List<Pusher>> = Uninitialized,
+        val notificationsEnabled: Boolean = false,
 ) : MavericksState {
     constructor(args: SessionOverviewArgs) : this(
             deviceId = args.deviceId
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCase.kt
new file mode 100644
index 0000000000..45c234aaef
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCase.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.settings.devices.v2.overview
+
+import im.vector.app.core.di.ActiveSessionHolder
+import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
+import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
+import org.matrix.android.sdk.api.session.events.model.toContent
+import javax.inject.Inject
+
+class TogglePushNotificationUseCase @Inject constructor(
+        private val activeSessionHolder: ActiveSessionHolder,
+) {
+
+    suspend fun execute(deviceId: String, enabled: Boolean) {
+        val session = activeSessionHolder.getSafeActiveSession() ?: return
+        val devicePusher = session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
+        devicePusher?.let { pusher ->
+            session.pushersService().togglePusher(pusher, enabled)
+        }
+
+        val accountData = session.accountDataService().getUserAccountDataEvent(UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId)
+        if (accountData != null) {
+            val newNotificationSettingsContent = LocalNotificationSettingsContent(isSilenced = !enabled)
+            session.accountDataService().updateUserAccountData(
+                    UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + deviceId,
+                    newNotificationSettingsContent.toContent(),
+            )
+        }
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/core/pushers/PushersManagerTest.kt b/vector/src/test/java/im/vector/app/core/pushers/PushersManagerTest.kt
index 750e50d578..113a810ac2 100644
--- a/vector/src/test/java/im/vector/app/core/pushers/PushersManagerTest.kt
+++ b/vector/src/test/java/im/vector/app/core/pushers/PushersManagerTest.kt
@@ -114,6 +114,6 @@ class PushersManagerTest {
 
         pushersManager.togglePusherForCurrentSession(true)
 
-        pushersService.verifyOnlyGetPushersAndTogglePusherCalled(pusher, true)
+        pushersService.verifyTogglePusherCalled(pusher, true)
     }
 }
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt
index 73c9c5aa95..544059b77f 100644
--- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt
@@ -18,7 +18,6 @@ package im.vector.app.features.settings.devices.v2.overview
 
 import android.os.SystemClock
 import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.airbnb.mvrx.Loading
 import com.airbnb.mvrx.Success
 import com.airbnb.mvrx.test.MavericksTestRule
 import im.vector.app.R
@@ -30,8 +29,8 @@ import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase
 import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
 import im.vector.app.test.fakes.FakeActiveSessionHolder
 import im.vector.app.test.fakes.FakePendingAuthHandler
-import im.vector.app.test.fakes.FakeSession
 import im.vector.app.test.fakes.FakeStringProvider
+import im.vector.app.test.fakes.FakeTogglePushNotificationUseCase
 import im.vector.app.test.fakes.FakeVerificationService
 import im.vector.app.test.fixtures.PusherFixture.aPusher
 import im.vector.app.test.test
@@ -79,7 +78,6 @@ class SessionOverviewViewModelTest {
     private val args = SessionOverviewArgs(
             deviceId = A_SESSION_ID_1
     )
-    private val fakeSession = FakeSession()
     private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>(relaxed = true)
     private val fakeActiveSessionHolder = FakeActiveSessionHolder()
     private val fakeStringProvider = FakeStringProvider()
@@ -88,10 +86,10 @@ class SessionOverviewViewModelTest {
     private val interceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
     private val fakePendingAuthHandler = FakePendingAuthHandler()
     private val refreshDevicesUseCase = mockk<RefreshDevicesUseCase>()
+    private val togglePushNotificationUseCase = FakeTogglePushNotificationUseCase()
 
     private fun createViewModel() = SessionOverviewViewModel(
             initialState = SessionOverviewViewState(args),
-            session = fakeSession,
             stringProvider = fakeStringProvider.instance,
             getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
             checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
@@ -100,6 +98,7 @@ class SessionOverviewViewModelTest {
             pendingAuthHandler = fakePendingAuthHandler.instance,
             activeSessionHolder = fakeActiveSessionHolder.instance,
             refreshDevicesUseCase = refreshDevicesUseCase,
+            togglePushNotificationUseCase = togglePushNotificationUseCase.instance,
     )
 
     @Before
@@ -116,6 +115,13 @@ class SessionOverviewViewModelTest {
         unmockkAll()
     }
 
+    @Test
+    fun `given the viewModel has been initialized then pushers are refreshed`() {
+        createViewModel()
+
+        fakeActiveSessionHolder.fakeSession.pushersService().verifyRefreshPushers()
+    }
+
     @Test
     fun `given the viewModel has been initialized then viewState is updated with session info`() {
         val deviceFullInfo = mockk<DeviceFullInfo>()
@@ -125,7 +131,7 @@ class SessionOverviewViewModelTest {
                 deviceId = A_SESSION_ID_1,
                 deviceInfo = Success(deviceFullInfo),
                 isCurrentSessionTrusted = true,
-                pushers = Loading(),
+                notificationsEnabled = true,
         )
 
         val viewModel = createViewModel()
@@ -221,7 +227,7 @@ class SessionOverviewViewModelTest {
                 isCurrentSessionTrusted = true,
                 deviceInfo = Success(deviceFullInfo),
                 isLoading = false,
-                pushers = Loading(),
+                notificationsEnabled = true,
         )
 
         // When
@@ -258,7 +264,7 @@ class SessionOverviewViewModelTest {
                 isCurrentSessionTrusted = true,
                 deviceInfo = Success(deviceFullInfo),
                 isLoading = false,
-                pushers = Loading(),
+                notificationsEnabled = true,
         )
         fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE)
 
@@ -293,7 +299,7 @@ class SessionOverviewViewModelTest {
                 isCurrentSessionTrusted = true,
                 deviceInfo = Success(deviceFullInfo),
                 isLoading = false,
-                pushers = Loading(),
+                notificationsEnabled = true,
         )
         fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE)
 
@@ -461,26 +467,22 @@ class SessionOverviewViewModelTest {
     @Test
     fun `when viewModel init, then observe pushers and emit to state`() {
         val pushers = listOf(aPusher(deviceId = A_SESSION_ID_1))
-        fakeSession.pushersService().givenPushersLive(pushers)
+        fakeActiveSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
 
         val viewModel = createViewModel()
 
         viewModel.test()
-                .assertLatestState { state -> state.pushers.invoke() == pushers }
+                .assertLatestState { state -> state.notificationsEnabled }
                 .finish()
     }
 
     @Test
-    fun `when handle TogglePushNotifications, then toggle enabled for device pushers`() {
-        val pushers = listOf(
-                aPusher(deviceId = A_SESSION_ID_1, enabled = false),
-                aPusher(deviceId = "another id", enabled = false)
-        )
-        fakeSession.pushersService().givenPushersLive(pushers)
-
+    fun `when handle TogglePushNotifications, then execute use case and update state`() {
         val viewModel = createViewModel()
+
         viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true))
 
-        fakeSession.pushersService().verifyOnlyTogglePusherCalled(pushers.first(), true)
+        togglePushNotificationUseCase.verifyExecute(A_SESSION_ID_1, true)
+        viewModel.test().assertLatestState { state -> state.notificationsEnabled }.finish()
     }
 }
diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCaseTest.kt
new file mode 100644
index 0000000000..dc64c74836
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/TogglePushNotificationUseCaseTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.settings.devices.v2.overview
+
+import im.vector.app.test.fakes.FakeActiveSessionHolder
+import im.vector.app.test.fixtures.PusherFixture
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.matrix.android.sdk.api.account.LocalNotificationSettingsContent
+import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
+import org.matrix.android.sdk.api.session.events.model.toContent
+
+class TogglePushNotificationUseCaseTest {
+
+    private val activeSessionHolder = FakeActiveSessionHolder()
+    private val togglePushNotificationUseCase = TogglePushNotificationUseCase(activeSessionHolder.instance)
+
+    @Test
+    fun `when execute, then toggle enabled for device pushers`() = runTest {
+        val sessionId = "a_session_id"
+        val pushers = listOf(
+                PusherFixture.aPusher(deviceId = sessionId, enabled = false),
+                PusherFixture.aPusher(deviceId = "another id", enabled = false)
+        )
+        activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
+        activeSessionHolder.fakeSession.pushersService().givenGetPushers(pushers)
+
+        togglePushNotificationUseCase.execute(sessionId, true)
+
+        activeSessionHolder.fakeSession.pushersService().verifyTogglePusherCalled(pushers.first(), true)
+    }
+
+    @Test
+    fun `when execute, then toggle local notification settings`() = runTest {
+        val sessionId = "a_session_id"
+        val pushers = listOf(
+                PusherFixture.aPusher(deviceId = sessionId, enabled = false),
+                PusherFixture.aPusher(deviceId = "another id", enabled = false)
+        )
+        activeSessionHolder.fakeSession.pushersService().givenPushersLive(pushers)
+        activeSessionHolder.fakeSession.accountDataService().givenGetUserAccountDataEventReturns(
+                UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
+                LocalNotificationSettingsContent(isSilenced = true).toContent()
+        )
+
+        togglePushNotificationUseCase.execute(sessionId, true)
+
+        activeSessionHolder.fakeSession.accountDataService().verifyUpdateUserAccountDataEventSucceeds(
+                UserAccountDataTypes.TYPE_LOCAL_NOTIFICATION_SETTINGS + sessionId,
+                LocalNotificationSettingsContent(isSilenced = false).toContent(),
+        )
+    }
+}
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakePushersService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakePushersService.kt
index 4ad3052538..4e4323275b 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakePushersService.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakePushersService.kt
@@ -17,7 +17,6 @@
 package im.vector.app.test.fakes
 
 import androidx.lifecycle.liveData
-import io.mockk.Ordering
 import io.mockk.coVerify
 import io.mockk.every
 import io.mockk.justRun
@@ -38,16 +37,8 @@ class FakePushersService : PushersService by mockk(relaxed = true) {
         every { getPushersLive() } returns liveData { emit(pushers) }
     }
 
-    fun verifyOnlyGetPushersAndTogglePusherCalled(pusher: Pusher, enable: Boolean) {
-        coVerify(ordering = Ordering.ALL) {
-            getPushers()
-            togglePusher(pusher, enable)
-        }
-    }
-
-    fun verifyOnlyTogglePusherCalled(pusher: Pusher, enable: Boolean) {
-        coVerify(ordering = Ordering.ALL) {
-            getPushersLive() // verifies only getPushersLive and the following togglePusher was called
+    fun verifyTogglePusherCalled(pusher: Pusher, enable: Boolean) {
+        coVerify {
             togglePusher(pusher, enable)
         }
     }
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt
index 34a0d8edb3..615330463b 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSessionAccountDataService.kt
@@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
 import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
 import org.matrix.android.sdk.api.session.events.model.Content
 
-class FakeSessionAccountDataService : SessionAccountDataService by mockk() {
+class FakeSessionAccountDataService : SessionAccountDataService by mockk(relaxed = true) {
 
     fun givenGetUserAccountDataEventReturns(type: String, content: Content) {
         every { getUserAccountDataEvent(type) } returns UserAccountDataEvent(type, content)
diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt
new file mode 100644
index 0000000000..92e311cfb7
--- /dev/null
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeTogglePushNotificationUseCase.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.test.fakes
+
+import im.vector.app.features.settings.devices.v2.overview.TogglePushNotificationUseCase
+import io.mockk.coJustRun
+import io.mockk.coVerify
+import io.mockk.mockk
+
+class FakeTogglePushNotificationUseCase {
+
+    val instance = mockk<TogglePushNotificationUseCase> {
+        coJustRun { execute(any(), any()) }
+    }
+
+    fun verifyExecute(deviceId: String, enabled: Boolean) {
+        coVerify { instance.execute(deviceId, enabled) }
+    }
+}

From 93a346392b3a4d3551d6a8591ed7e8457fe109ba Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 17 Oct 2022 09:14:28 +0200
Subject: [PATCH 204/400] Bump gradle from 7.2.2 to 7.3.1 (#7364)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Bump gradle from 7.2.2 to 7.3.1

Bumps gradle from 7.2.2 to 7.3.1.

---
updated-dependencies:
- dependency-name: com.android.tools.build:gradle
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Try to fix build issue caused by KSP workaround

* Add missing dependency

* Suppress false positive in linter

* Suppress more false positives, fix 2 possible leaks

* Fix tests

* Ignore flaky test

* Use namespaces instead of packagenames in AndroidManifest files

* Reorder dependency groups

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
---
 build.gradle                                     | 16 ++++++++++------
 dependencies.gradle                              |  2 +-
 dependencies_groups.gradle                       |  1 +
 library/attachment-viewer/build.gradle           |  1 +
 .../src/main/AndroidManifest.xml                 |  2 +-
 library/core-utils/build.gradle                  |  2 ++
 library/core-utils/src/main/AndroidManifest.xml  |  2 +-
 library/external/dialpad/build.gradle            |  2 ++
 .../dialpad/src/main/AndroidManifest.xml         |  2 +-
 library/external/jsonviewer/build.gradle         |  2 ++
 .../jsonviewer/src/main/AndroidManifest.xml      |  2 +-
 library/multipicker/build.gradle                 |  2 ++
 library/multipicker/src/main/AndroidManifest.xml |  3 +--
 .../im/vector/lib/multipicker/ContactPicker.kt   |  2 ++
 library/ui-strings/build.gradle                  |  2 ++
 library/ui-strings/src/main/AndroidManifest.xml  |  2 +-
 library/ui-styles/build.gradle                   |  2 ++
 library/ui-styles/src/debug/AndroidManifest.xml  |  3 +--
 library/ui-styles/src/main/AndroidManifest.xml   |  5 ++---
 matrix-sdk-android-flow/build.gradle             |  2 ++
 .../src/main/AndroidManifest.xml                 |  5 +----
 matrix-sdk-android/build.gradle                  |  2 ++
 matrix-sdk-android/src/main/AndroidManifest.xml  |  3 +--
 .../sdk/internal/session/content/FileUploader.kt |  1 +
 vector-app/build.gradle                          |  1 +
 vector-app/src/main/AndroidManifest.xml          |  3 +--
 vector-config/build.gradle                       |  2 ++
 vector-config/src/main/AndroidManifest.xml       |  2 +-
 vector/build.gradle                              |  1 +
 .../app/features/RoomMemberListControllerTest.kt |  2 ++
 vector/src/main/AndroidManifest.xml              |  3 +--
 .../java/im/vector/app/core/files/FileSaver.kt   |  2 ++
 .../app/core/utils/ExternalApplicationsUtil.kt   |  2 ++
 .../app/features/crypto/keys/KeysExporter.kt     |  4 +++-
 .../app/features/crypto/keys/KeysExporterTest.kt |  7 +++++--
 35 files changed, 64 insertions(+), 33 deletions(-)

diff --git a/build.gradle b/build.gradle
index 1121ed5508..aea22ee184 100644
--- a/build.gradle
+++ b/build.gradle
@@ -342,17 +342,21 @@ subprojects { project ->
                 if (it instanceof com.android.build.gradle.LibraryExtension) {
                     libraryVariants.all { variant ->
                         def outputFolder = new File("build/generated/ksp/${variant.name}/kotlin")
-                        variant.addJavaSourceFoldersToModel(outputFolder)
-                        android.sourceSets.getAt(variant.name).java {
-                            srcDir(outputFolder)
+                        if (outputFolder.exists()) {
+                            variant.addJavaSourceFoldersToModel(outputFolder)
+                            android.sourceSets.getAt(variant.name).java {
+                                srcDir(outputFolder)
+                            }
                         }
                     }
                 } else if (it instanceof com.android.build.gradle.AppExtension) {
                     applicationVariants.all { variant ->
                         def outputFolder = new File("build/generated/ksp/${variant.name}/kotlin")
-                        variant.addJavaSourceFoldersToModel(outputFolder)
-                        android.sourceSets.getAt(variant.name).java {
-                            srcDir(outputFolder)
+                        if (outputFolder.exists()) {
+                            variant.addJavaSourceFoldersToModel(outputFolder)
+                            android.sourceSets.getAt(variant.name).java {
+                                srcDir(outputFolder)
+                            }
                         }
                     }
                 }
diff --git a/dependencies.gradle b/dependencies.gradle
index baa0994236..1c3c1e058a 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -7,7 +7,7 @@ ext.versions = [
         'targetCompat'      : JavaVersion.VERSION_11,
 ]
 
-def gradle = "7.2.2"
+def gradle = "7.3.1"
 // Ref: https://kotlinlang.org/releases.html
 def kotlin = "1.7.20"
 def kotlinCoroutines = "1.6.4"
diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle
index 109aee1c2c..68de2c1581 100644
--- a/dependencies_groups.gradle
+++ b/dependencies_groups.gradle
@@ -147,6 +147,7 @@ ext.groups = [
                         'io.netty',
                         'io.noties.markwon',
                         'io.opencensus',
+                        'io.perfmark',
                         'io.reactivex.rxjava2',
                         'io.realm',
                         'io.sentry',
diff --git a/library/attachment-viewer/build.gradle b/library/attachment-viewer/build.gradle
index 8bbafd3387..fc9495b113 100644
--- a/library/attachment-viewer/build.gradle
+++ b/library/attachment-viewer/build.gradle
@@ -18,6 +18,7 @@ apply plugin: 'com.android.library'
 apply plugin: 'kotlin-android'
 
 android {
+    namespace "im.vector.lib.attachmentviewer"
 
     compileSdk versions.compileSdk
 
diff --git a/library/attachment-viewer/src/main/AndroidManifest.xml b/library/attachment-viewer/src/main/AndroidManifest.xml
index 8970b47178..8072ee00db 100644
--- a/library/attachment-viewer/src/main/AndroidManifest.xml
+++ b/library/attachment-viewer/src/main/AndroidManifest.xml
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest package="im.vector.lib.attachmentviewer" />
+<manifest />
diff --git a/library/core-utils/build.gradle b/library/core-utils/build.gradle
index 0f7789a2a8..b985127ec6 100644
--- a/library/core-utils/build.gradle
+++ b/library/core-utils/build.gradle
@@ -20,6 +20,8 @@ plugins {
 }
 
 android {
+    namespace "im.vector.lib.core.utils"
+
     compileSdk versions.compileSdk
     defaultConfig {
         minSdk versions.minSdk
diff --git a/library/core-utils/src/main/AndroidManifest.xml b/library/core-utils/src/main/AndroidManifest.xml
index 20a9414519..8072ee00db 100644
--- a/library/core-utils/src/main/AndroidManifest.xml
+++ b/library/core-utils/src/main/AndroidManifest.xml
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest package="im.vector.lib.core.utils" />
\ No newline at end of file
+<manifest />
diff --git a/library/external/dialpad/build.gradle b/library/external/dialpad/build.gradle
index fade8ddf30..e6f249f535 100644
--- a/library/external/dialpad/build.gradle
+++ b/library/external/dialpad/build.gradle
@@ -2,6 +2,8 @@ apply plugin: 'com.android.library'
 apply plugin: 'kotlin-android'
 
 android {
+    namespace "com.android.dialer.dialpadview"
+
     compileSdk versions.compileSdk
 
     defaultConfig {
diff --git a/library/external/dialpad/src/main/AndroidManifest.xml b/library/external/dialpad/src/main/AndroidManifest.xml
index 1d412d0ae5..8072ee00db 100644
--- a/library/external/dialpad/src/main/AndroidManifest.xml
+++ b/library/external/dialpad/src/main/AndroidManifest.xml
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest package="com.android.dialer.dialpadview" />
+<manifest />
diff --git a/library/external/jsonviewer/build.gradle b/library/external/jsonviewer/build.gradle
index 50bb635e8e..a5d297b860 100644
--- a/library/external/jsonviewer/build.gradle
+++ b/library/external/jsonviewer/build.gradle
@@ -18,6 +18,8 @@ buildscript {
 }
 
 android {
+    namespace "org.billcarsonfr.jsonviewer"
+
     compileSdk versions.compileSdk
 
     defaultConfig {
diff --git a/library/external/jsonviewer/src/main/AndroidManifest.xml b/library/external/jsonviewer/src/main/AndroidManifest.xml
index 73322c2fdb..cc947c5679 100644
--- a/library/external/jsonviewer/src/main/AndroidManifest.xml
+++ b/library/external/jsonviewer/src/main/AndroidManifest.xml
@@ -1 +1 @@
-<manifest package="org.billcarsonfr.jsonviewer" />
+<manifest />
diff --git a/library/multipicker/build.gradle b/library/multipicker/build.gradle
index 09fc2a7b50..c77a86a764 100644
--- a/library/multipicker/build.gradle
+++ b/library/multipicker/build.gradle
@@ -19,6 +19,8 @@ apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-parcelize'
 
 android {
+    namespace "im.vector.lib.multipicker"
+
     compileSdk versions.compileSdk
 
     defaultConfig {
diff --git a/library/multipicker/src/main/AndroidManifest.xml b/library/multipicker/src/main/AndroidManifest.xml
index c02a22d1d9..2b4ef0e884 100644
--- a/library/multipicker/src/main/AndroidManifest.xml
+++ b/library/multipicker/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="im.vector.lib.multipicker">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
     <application>
         <provider
diff --git a/library/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt b/library/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt
index be83d2ea47..04dc8feb7b 100644
--- a/library/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt
+++ b/library/multipicker/src/main/java/im/vector/lib/multipicker/ContactPicker.kt
@@ -16,6 +16,7 @@
 
 package im.vector.lib.multipicker
 
+import android.annotation.SuppressLint
 import android.content.ContentResolver
 import android.content.Context
 import android.content.Intent
@@ -34,6 +35,7 @@ class ContactPicker : Picker<MultiPickerContactType>() {
      * Call this function from onActivityResult(int, int, Intent).
      * Returns selected contact or empty list if user did not select any contacts.
      */
+    @SuppressLint("Recycle")
     override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerContactType> {
         val contactList = mutableListOf<MultiPickerContactType>()
 
diff --git a/library/ui-strings/build.gradle b/library/ui-strings/build.gradle
index 6a31f24c9b..b6e6de5c22 100644
--- a/library/ui-strings/build.gradle
+++ b/library/ui-strings/build.gradle
@@ -5,6 +5,8 @@ plugins {
 }
 
 android {
+    namespace "im.vector.lib.strings"
+
     compileSdk versions.compileSdk
     defaultConfig {
         minSdk versions.minSdk
diff --git a/library/ui-strings/src/main/AndroidManifest.xml b/library/ui-strings/src/main/AndroidManifest.xml
index deff03ee0a..8072ee00db 100644
--- a/library/ui-strings/src/main/AndroidManifest.xml
+++ b/library/ui-strings/src/main/AndroidManifest.xml
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest package="im.vector.lib.strings" />
+<manifest />
diff --git a/library/ui-styles/build.gradle b/library/ui-styles/build.gradle
index ee5771d995..c805153e1d 100644
--- a/library/ui-styles/build.gradle
+++ b/library/ui-styles/build.gradle
@@ -21,6 +21,8 @@ plugins {
 
 android {
 
+    namespace "im.vector.lib.ui.styles"
+
     compileSdk versions.compileSdk
     defaultConfig {
         minSdk versions.minSdk
diff --git a/library/ui-styles/src/debug/AndroidManifest.xml b/library/ui-styles/src/debug/AndroidManifest.xml
index e32676136d..be7aeafb07 100644
--- a/library/ui-styles/src/debug/AndroidManifest.xml
+++ b/library/ui-styles/src/debug/AndroidManifest.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="im.vector.lib.ui.styles">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
     <application android:theme="@style/Theme.Vector.Light">
         <activity
diff --git a/library/ui-styles/src/main/AndroidManifest.xml b/library/ui-styles/src/main/AndroidManifest.xml
index 254827465d..6dc9cbfb8b 100644
--- a/library/ui-styles/src/main/AndroidManifest.xml
+++ b/library/ui-styles/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="im.vector.lib.ui.styles">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
     <application android:supportsRtl="true" />
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/matrix-sdk-android-flow/build.gradle b/matrix-sdk-android-flow/build.gradle
index fb69af2d82..0a29334ea8 100644
--- a/matrix-sdk-android-flow/build.gradle
+++ b/matrix-sdk-android-flow/build.gradle
@@ -5,6 +5,8 @@ plugins {
 }
 
 android {
+    namespace "org.matrix.android.sdk.flow"
+
     compileSdk versions.compileSdk
 
     defaultConfig {
diff --git a/matrix-sdk-android-flow/src/main/AndroidManifest.xml b/matrix-sdk-android-flow/src/main/AndroidManifest.xml
index 2392c0bfcb..b2d3ea1235 100644
--- a/matrix-sdk-android-flow/src/main/AndroidManifest.xml
+++ b/matrix-sdk-android-flow/src/main/AndroidManifest.xml
@@ -1,5 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.matrix.android.sdk.flow">
-
-</manifest>
\ No newline at end of file
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index ea2b5d6c47..4a6c0edf10 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -43,6 +43,8 @@ dokkaHtml {
 }
 
 android {
+    namespace "org.matrix.android.sdk"
+
     testOptions.unitTests.includeAndroidResources = true
 
     compileSdk versions.compileSdk
diff --git a/matrix-sdk-android/src/main/AndroidManifest.xml b/matrix-sdk-android/src/main/AndroidManifest.xml
index de0731422c..7f940d4e1c 100644
--- a/matrix-sdk-android/src/main/AndroidManifest.xml
+++ b/matrix-sdk-android/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="org.matrix.android.sdk">
+    xmlns:tools="http://schemas.android.com/tools">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt
index c023646c7f..eee55735e0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt
@@ -130,6 +130,7 @@ internal class FileUploader @Inject constructor(
             workingFile.outputStream().use {
                 inputStream.copyTo(it)
             }
+            inputStream.close()
             workingFile
         }
     }
diff --git a/vector-app/build.gradle b/vector-app/build.gradle
index eb19027880..eb6a9542ee 100644
--- a/vector-app/build.gradle
+++ b/vector-app/build.gradle
@@ -125,6 +125,7 @@ ext.abiVersionCodes = ["armeabi-v7a": 1, "arm64-v8a": 2, "x86": 3, "x86_64": 4].
 def buildNumber = System.env.BUILDKITE_BUILD_NUMBER as Integer ?: 0
 
 android {
+    namespace "im.vector.application"
     // Due to a bug introduced in Android gradle plugin 3.6.0, we have to specify the ndk version to use
     // Ref: https://issuetracker.google.com/issues/144111441
     ndkVersion "21.3.6528147"
diff --git a/vector-app/src/main/AndroidManifest.xml b/vector-app/src/main/AndroidManifest.xml
index 7a515449b4..661bd3f934 100644
--- a/vector-app/src/main/AndroidManifest.xml
+++ b/vector-app/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="im.vector.application">
+    xmlns:tools="http://schemas.android.com/tools">
 
     <application
         android:name="im.vector.app.VectorApplication"
diff --git a/vector-config/build.gradle b/vector-config/build.gradle
index 658452bbdd..771e51f357 100644
--- a/vector-config/build.gradle
+++ b/vector-config/build.gradle
@@ -4,6 +4,8 @@ plugins {
 }
 
 android {
+    namespace "im.vector.app.config"
+
     compileSdk versions.compileSdk
 
     defaultConfig {
diff --git a/vector-config/src/main/AndroidManifest.xml b/vector-config/src/main/AndroidManifest.xml
index 4d6cf1535f..8072ee00db 100644
--- a/vector-config/src/main/AndroidManifest.xml
+++ b/vector-config/src/main/AndroidManifest.xml
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest package="im.vector.app.config" />
+<manifest />
diff --git a/vector/build.gradle b/vector/build.gradle
index 048bb885bc..348eb18220 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -28,6 +28,7 @@ project.android.buildTypes.all { buildType ->
 initScreenshotTests(project)
 
 android {
+    namespace "im.vector.app"
     // Due to a bug introduced in Android gradle plugin 3.6.0, we have to specify the ndk version to use
     // Ref: https://issuetracker.google.com/issues/144111441
     ndkVersion "21.3.6528147"
diff --git a/vector/src/androidTest/java/im/vector/app/features/RoomMemberListControllerTest.kt b/vector/src/androidTest/java/im/vector/app/features/RoomMemberListControllerTest.kt
index 73174e4b34..527751aae2 100644
--- a/vector/src/androidTest/java/im/vector/app/features/RoomMemberListControllerTest.kt
+++ b/vector/src/androidTest/java/im/vector/app/features/RoomMemberListControllerTest.kt
@@ -25,6 +25,7 @@ import io.mockk.every
 import io.mockk.mockk
 import kotlinx.coroutines.test.runTest
 import org.amshove.kluent.shouldBeEqualTo
+import org.junit.Ignore
 import org.junit.Test
 import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
 import org.matrix.android.sdk.api.session.room.model.Membership
@@ -36,6 +37,7 @@ import kotlin.coroutines.suspendCoroutine
 class RoomMemberListControllerTest {
 
     @Test
+    @Ignore("Too flaky")
     fun testControllerUserVerificationLevel() = runTest {
         val roomListController = RoomMemberListController(
                 avatarRenderer = mockk {
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index ad15bf4829..6b95b99467 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="im.vector.app">
+    xmlns:tools="http://schemas.android.com/tools">
 
     <!-- Needed for VOIP call to detect and switch to headset-->
     <uses-permission
diff --git a/vector/src/main/java/im/vector/app/core/files/FileSaver.kt b/vector/src/main/java/im/vector/app/core/files/FileSaver.kt
index c595bb2693..8afcdac73b 100644
--- a/vector/src/main/java/im/vector/app/core/files/FileSaver.kt
+++ b/vector/src/main/java/im/vector/app/core/files/FileSaver.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.core.files
 
+import android.annotation.SuppressLint
 import android.app.DownloadManager
 import android.content.ContentValues
 import android.content.Context
@@ -52,6 +53,7 @@ fun writeToFile(data: ByteArray, file: File) {
     }
 }
 
+@SuppressLint("Recycle")
 fun addEntryToDownloadManager(
         context: Context,
         file: File,
diff --git a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt
index 915a8d4d0d..23641bd170 100644
--- a/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/ExternalApplicationsUtil.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.core.utils
 
+import android.annotation.SuppressLint
 import android.app.Activity
 import android.app.DownloadManager
 import android.content.ActivityNotFoundException
@@ -256,6 +257,7 @@ private fun appendTimeToFilename(name: String): String {
     return """${filename}_$dateExtension.$fileExtension"""
 }
 
+@SuppressLint("Recycle")
 suspend fun saveMedia(
         context: Context,
         file: File,
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt
index 4b3a657740..556d9bcec3 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt
@@ -47,10 +47,12 @@ class KeysExporter @Inject constructor(
         when {
             output == null -> throw IllegalStateException("Exported file not found")
             output.statSize != expectedSize -> {
-                throw UnexpectedExportKeysFileSizeException(
+                val exception = UnexpectedExportKeysFileSizeException(
                         expectedFileSize = expectedSize,
                         actualFileSize = output.statSize
                 )
+                output.close()
+                throw exception
             }
         }
     }
diff --git a/vector/src/test/java/im/vector/app/features/crypto/keys/KeysExporterTest.kt b/vector/src/test/java/im/vector/app/features/crypto/keys/KeysExporterTest.kt
index 3cd797a7b1..e4181f4e3f 100644
--- a/vector/src/test/java/im/vector/app/features/crypto/keys/KeysExporterTest.kt
+++ b/vector/src/test/java/im/vector/app/features/crypto/keys/KeysExporterTest.kt
@@ -17,12 +17,12 @@
 package im.vector.app.features.crypto.keys
 
 import android.net.Uri
-import android.os.ParcelFileDescriptor
 import im.vector.app.core.dispatchers.CoroutineDispatchers
 import im.vector.app.test.fakes.FakeContext
 import im.vector.app.test.fakes.FakeCryptoService
 import im.vector.app.test.fakes.FakeSession
 import io.mockk.every
+import io.mockk.justRun
 import io.mockk.mockk
 import io.mockk.verify
 import kotlinx.coroutines.Dispatchers
@@ -91,7 +91,10 @@ class KeysExporterTest {
 
     private fun givenFileDescriptorWithSize(size: Long) {
         context.givenFileDescriptor(A_URI, mode = "r") {
-            mockk<ParcelFileDescriptor>().also { every { it.statSize } returns size }
+            mockk {
+                every { statSize } returns size
+                justRun { close() }
+            }
         }
     }
 }

From 0cdc21649ec9d22220c61f37b82a693362ceb87f Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 17 Oct 2022 11:27:40 +0200
Subject: [PATCH 205/400] Fix unused import

---
 .../android/sdk/internal/session/widgets/helper/WidgetFactory.kt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
index 8ede63c365..a43c59a83b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt
@@ -20,7 +20,6 @@ import org.matrix.android.sdk.api.session.content.ContentUrlResolver
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.sender.SenderInfo
-import org.matrix.android.sdk.api.session.user.model.User
 import org.matrix.android.sdk.api.session.widgets.model.Widget
 import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
 import org.matrix.android.sdk.api.session.widgets.model.WidgetType

From b7570a3c5e781bc2f1d31aff07605ca6e7376bf4 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 17 Oct 2022 11:31:48 +0200
Subject: [PATCH 206/400] Fix compilation error

---
 .../android/sdk/internal/session/profile/GetProfileInfoTask.kt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
index 4a20c68caf..22bb3d37b0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/GetProfileInfoTask.kt
@@ -20,6 +20,7 @@ package org.matrix.android.sdk.internal.session.profile
 import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.api.session.user.model.User
 import org.matrix.android.sdk.api.util.JsonDict
+import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.session.user.UserEntityFactory
@@ -37,7 +38,7 @@ internal abstract class GetProfileInfoTask : Task<GetProfileInfoTask.Params, Jso
 internal class DefaultGetProfileInfoTask @Inject constructor(
         private val profileAPI: ProfileAPI,
         private val globalErrorReceiver: GlobalErrorReceiver,
-        private val monarchy: Monarchy,
+        @SessionDatabase private val monarchy: Monarchy,
 ) : GetProfileInfoTask() {
 
     override suspend fun execute(params: Params): JsonDict {

From 8547fee56216e5e2e468ff535d8e87f68377419b Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 17 Oct 2022 12:44:25 +0300
Subject: [PATCH 207/400] Enable qr code login by default.

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

diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index d41b5ac226..255ac6d188 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -59,7 +59,7 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun isLocationSharingEnabled() = Config.ENABLE_LOCATION_SHARING
     override fun forceUsageOfOpusEncoder(): Boolean = false
     override fun isNewAppLayoutFeatureEnabled(): Boolean = true
-    override fun isQrCodeLoginEnabled(): Boolean = false
+    override fun isQrCodeLoginEnabled(): Boolean = true
     override fun isQrCodeLoginForAllServers(): Boolean = false
     override fun isReciprocateQrCodeLogin(): Boolean = false
     override fun isVoiceBroadcastEnabled(): Boolean = false

From d979b50b2cd8b098b5bd3e353264e7e76be5dabe Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 10:52:24 +0100
Subject: [PATCH 208/400] Logging cleanup

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 22 ++++++++++++-------
 .../channels/ECDHRendezvousChannel.kt         | 16 ++++----------
 .../SimpleHttpRendezvousTransport.kt          | 11 +++-------
 3 files changed, 21 insertions(+), 28 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 6403d17031..b43b122cba 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -36,9 +36,14 @@ import org.matrix.android.sdk.api.util.MatrixJsonParser
 import timber.log.Timber
 
 internal enum class PayloadType(val value: String) {
-    @Json(name = "m.login.start") Start("m.login.start"),
-    @Json(name = "m.login.finish") Finish("m.login.finish"),
-    @Json(name = "m.login.progress") Progress("m.login.progress")
+    @Json(name = "m.login.start")
+    Start("m.login.start"),
+
+    @Json(name = "m.login.finish")
+    Finish("m.login.finish"),
+
+    @Json(name = "m.login.progress")
+    Progress("m.login.progress")
 }
 
 @JsonClass(generateAdapter = true)
@@ -150,12 +155,12 @@ class Rendezvous(
         }
 
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
-        val login_token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
+        val token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
 
-        Timber.tag(TAG).i("Got login_token: $login_token for $homeserver")
+        Timber.tag(TAG).i("Got login_token now attempting to sign in with $homeserver")
 
         val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver))
-        return authenticationService.loginUsingQrLoginToken(hsConfig, login_token)
+        return authenticationService.loginUsingQrLoginToken(hsConfig, token)
     }
 
     suspend fun completeVerificationOnNewDevice(session: Session) {
@@ -171,8 +176,8 @@ class Rendezvous(
         val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer")
-            return
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})")
+            throw RuntimeException("Key from verifying device doesn't match")
         }
 
         // set other device as verified
@@ -187,6 +192,7 @@ class Rendezvous(
                     crypto.crossSigningService().markMyMasterKeyAsTrusted()
                 } else {
                     Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                    throw RuntimeException("Master key from verifying device doesn't match")
                 }
             } ?: Timber.tag(TAG).i("No local master key")
         } ?: Timber.tag(TAG).i("No master key given by verifying device")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 1c8bca5d1c..4d5ed30ac5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTran
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
+import timber.log.Timber
 import java.security.SecureRandom
 import java.util.LinkedList
 import javax.crypto.Cipher
@@ -93,7 +94,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         val isInitiator = theirPublicKey == null
 
         if (isInitiator) {
-//            Timber.tag(TAG).i("Waiting for other device to send their public key")
+            Timber.tag(TAG).i("Waiting for other device to send their public key")
             val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
 
             if (res.key == null) {
@@ -105,7 +106,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
         } else {
             // send our public key unencrypted
-//            Timber.tag(TAG).i("Sending public key")
+            Timber.tag(TAG).i("Sending public key")
             send(
                     ECDHPayload(
                     algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
@@ -122,11 +123,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
         aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
 
-//        Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}")
-//        Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}")
-//        Timber.tag(TAG).i("AES info: $aesInfo")
-//        Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}")
-
         val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
         return getDecimalCodeRepresentation(rawChecksum)
     }
@@ -181,7 +177,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {
-//        Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}")
         val iv = ByteArray(16)
         SecureRandom().nextBytes(iv)
 
@@ -211,9 +206,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList())
         plainText.addAll(encryptCipher.doFinal().toList())
 
-        val plainTextBytes = plainText.toByteArray()
-
-//        Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}")
-        return plainTextBytes
+        return plainText.toByteArray()
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 475a4fbe6c..004cf38e24 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -67,8 +67,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         // TODO: properly determine endpoint
         val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
 
-//        Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri")
-
         val httpClient = okhttp3.OkHttpClient.Builder().build()
 
         val request = Request.Builder()
@@ -123,8 +121,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             val response = httpClient.newCall(request.build()).execute()
 
             try {
-//                Timber.tag(TAG).d("Received polling response: ${response.code} from $uri")
-
                 if (response.code == 404) {
                     cancel(RendezvousFailureReason.Unknown)
                     return null
@@ -140,9 +136,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                     response.header("etag")?.let {
                         etag = it
                     }
-                    val data = response.body?.bytes()
-//                    Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag")
-                    return data
+                    return response.body?.bytes()
                 }
 
                 done = false
@@ -159,7 +153,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         var mappedReason = reason
         Timber.tag(TAG).i("$expiresAt")
         if (mappedReason == RendezvousFailureReason.Unknown &&
-                expiresAt != null && Date() > expiresAt) {
+                expiresAt != null && Date() > expiresAt
+        ) {
             mappedReason = RendezvousFailureReason.Expired
         }
 

From ed6bc01bef48b4266dbe34e600f8038b0dcc6a3a Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 10:54:28 +0100
Subject: [PATCH 209/400] Resolve TODO

---
 .../api/rendezvous/transports/SimpleHttpRendezvousTransport.kt | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 004cf38e24..dde5edcb93 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -64,8 +64,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         }
 
         val method = if (uri != null) "PUT" else "POST"
-        // TODO: properly determine endpoint
-        val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev"
+        val uri = this.uri ?: throw RuntimeException("No rendezvous URI")
 
         val httpClient = okhttp3.OkHttpClient.Builder().build()
 

From 33be5c257dcaa8c674486a77c5851d1a6a626f7e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:24:48 +0100
Subject: [PATCH 210/400] Refactor into dedicated files and companion objects

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 39 +++---------
 .../channels/ECDHRendezvousChannel.kt         | 59 ++++++++++---------
 .../api/rendezvous/model/ECDHRendezvous.kt    |  7 ---
 .../rendezvous/model/ECDHRendezvousCode.kt    | 26 ++++++++
 .../sdk/api/rendezvous/model/Payload.kt       | 36 +++++++++++
 .../sdk/api/rendezvous/model/PayloadType.kt   | 30 ++++++++++
 .../SimpleHttpRendezvousTransportDetails.kt   | 25 ++++++++
 .../SimpleHttpRendezvousTransport.kt          | 13 ++--
 8 files changed, 159 insertions(+), 76 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index b43b122cba..4270d4a09c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -17,13 +17,13 @@
 package org.matrix.android.sdk.api.rendezvous
 
 import android.net.Uri
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.Payload
+import org.matrix.android.sdk.api.rendezvous.model.PayloadType
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
@@ -35,35 +35,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_S
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import timber.log.Timber
 
-internal enum class PayloadType(val value: String) {
-    @Json(name = "m.login.start")
-    Start("m.login.start"),
-
-    @Json(name = "m.login.finish")
-    Finish("m.login.finish"),
-
-    @Json(name = "m.login.progress")
-    Progress("m.login.progress")
-}
-
-@JsonClass(generateAdapter = true)
-internal data class Payload(
-        @Json val type: PayloadType,
-        @Json val intent: RendezvousIntent? = null,
-        @Json val outcome: String? = null,
-        @Json val protocols: List<String>? = null,
-        @Json val protocol: String? = null,
-        @Json val homeserver: String? = null,
-        @Json(name = "login_token") val loginToken: String? = null,
-        @Json(name = "device_id") val deviceId: String? = null,
-        @Json(name = "device_key") val deviceKey: String? = null,
-        @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null,
-        @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null,
-        @Json(name = "master_key") val masterKey: String? = null
-)
-
-private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
 /**
  * Implementation of MSC3906 to sign in + E2EE set up using a QR code.
  */
@@ -72,6 +43,8 @@ class Rendezvous(
         val theirIntent: RendezvousIntent,
 ) {
     companion object {
+        private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
         fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
             val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
 
@@ -176,7 +149,9 @@ class Rendezvous(
         val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})")
+            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${
+                verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
+            )
             throw RuntimeException("Key from verifying device doesn't match")
         }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 4d5ed30ac5..5dbac894ae 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
-import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
+import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
@@ -40,39 +40,40 @@ import javax.crypto.Cipher
 import javax.crypto.spec.IvParameterSpec
 import javax.crypto.spec.SecretKeySpec
 
-@JsonClass(generateAdapter = true)
-data class ECDHPayload(
-        @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
-        @Json val key: String? = null,
-        @Json val ciphertext: String? = null,
-        @Json val iv: String? = null
-)
-
-private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-    val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
-    val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
-    val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
-    val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
-    val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
-    // (B0 << 5 | B1 >> 3) + 1000
-    val first = (b0.shl(5) or b1.shr(3)) + 1000
-    // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
-    val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
-    // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
-    val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-    return "$first-$second-$third"
-}
-
-const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
-const val KEY_SPEC = "AES"
-
 /**
  *  Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903:
  *  https://github.com/matrix-org/matrix-spec-proposals/pull/3903
  */
 class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?) : RendezvousChannel {
+    companion object {
+        private const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
+        private const val KEY_SPEC = "AES"
+        private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+
+        private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+            val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
+            val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
+            val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
+            val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
+            val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
+            // (B0 << 5 | B1 >> 3) + 1000
+            val first = (b0.shl(5) or b1.shr(3)) + 1000
+            // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
+            val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
+            // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
+            val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
+            return "$first-$second-$third"
+        }
+    }
+
+    @JsonClass(generateAdapter = true)
+    internal data class ECDHPayload(
+            @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
+            @Json val key: String? = null,
+            @Json val ciphertext: String? = null,
+            @Json val iv: String? = null
+    )
+
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
index b203101b66..0840e1ca2e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
@@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
-import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvous(
@@ -26,9 +25,3 @@ data class ECDHRendezvous(
         @Json val algorithm: SecureRendezvousChannelAlgorithm,
         @Json val key: String
 )
-
-@JsonClass(generateAdapter = true)
-data class ECDHRendezvousCode(
-        @Json val intent: RendezvousIntent,
-        @Json val rendezvous: ECDHRendezvous
-)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
new file mode 100644
index 0000000000..410c5c1036
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.api.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class ECDHRendezvousCode(
+        @Json val intent: RendezvousIntent,
+        @Json val rendezvous: ECDHRendezvous
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
new file mode 100644
index 0000000000..5627452232
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.api.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+internal data class Payload(
+        @Json val type: PayloadType,
+        @Json val intent: RendezvousIntent? = null,
+        @Json val outcome: String? = null,
+        @Json val protocols: List<String>? = null,
+        @Json val protocol: String? = null,
+        @Json val homeserver: String? = null,
+        @Json(name = "login_token") val loginToken: String? = null,
+        @Json(name = "device_id") val deviceId: String? = null,
+        @Json(name = "device_key") val deviceKey: String? = null,
+        @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null,
+        @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null,
+        @Json(name = "master_key") val masterKey: String? = null
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
new file mode 100644
index 0000000000..9854e7c070
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.api.rendezvous.model
+
+import com.squareup.moshi.Json
+
+internal enum class PayloadType(val value: String) {
+    @Json(name = "m.login.start")
+    Start("m.login.start"),
+
+    @Json(name = "m.login.finish")
+    Finish("m.login.finish"),
+
+    @Json(name = "m.login.progress")
+    Progress("m.login.progress")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
new file mode 100644
index 0000000000..70a441d760
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.api.rendezvous.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class SimpleHttpRendezvousTransportDetails(
+        @Json val uri: String
+) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index dde5edcb93..03e8b0cda7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -26,22 +26,19 @@ import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportType
+import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.Date
 
-private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-@JsonClass(generateAdapter = true)
-data class SimpleHttpRendezvousTransportDetails(
-        @Json val uri: String
-) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)
-
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
  */
 class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
+    companion object {
+        private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
+    }
+
     override var ready = false
     private var cancelled = false
     private var uri: String?

From 3be4a0ca2137c339b66b9b5aa12bb0617c15d935 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:25:09 +0100
Subject: [PATCH 211/400] Remove unused val

---
 .../java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 81a00cf548..8b70f61e80 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -35,7 +35,6 @@ import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
-        private val applicationContext: Context,
         private val authenticationService: AuthenticationService,
         private val activeSessionHolder: ActiveSessionHolder,
         private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase

From 48de8f4e345105668298ec238bbad5600c057ab9 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:48:35 +0100
Subject: [PATCH 212/400] Fix bad merge

---
 .../session/homeserver/GetHomeServerCapabilitiesTask.kt         | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index d65e629b71..42bba5fe88 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -133,6 +133,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 homeServerCapabilitiesEntity.roomVersionsJson = capabilities?.roomVersions?.let {
                     MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it)
                 }
+                homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
+                        getVersionResult?.doesServerSupportThreads().orFalse()
             }
 
             if (getMediaConfigResult != null) {

From 506fa729ea8f9d77357cc0f1347d3227e6b51702 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 11:50:56 +0100
Subject: [PATCH 213/400] Cleanup

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 29 ++++++++++--------
 .../channels/ECDHRendezvousChannel.kt         |  1 +
 .../sdk/api/rendezvous/model/Outcome.kt       | 30 +++++++++++++++++++
 .../sdk/api/rendezvous/model/Payload.kt       |  6 ++--
 .../sdk/api/rendezvous/model/PayloadType.kt   |  6 ++--
 .../sdk/api/rendezvous/model/Protocol.kt      | 24 +++++++++++++++
 6 files changed, 78 insertions(+), 18 deletions(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 4270d4a09c..e467ff06e3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -22,8 +22,10 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel
 import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
+import org.matrix.android.sdk.api.rendezvous.model.Outcome
 import org.matrix.android.sdk.api.rendezvous.model.Payload
 import org.matrix.android.sdk.api.rendezvous.model.PayloadType
+import org.matrix.android.sdk.api.rendezvous.model.Protocol
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
@@ -68,7 +70,7 @@ class Rendezvous(
         Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
 
         if (incompatible) {
-            send(Payload(PayloadType.Finish, intent = ourIntent))
+            send(Payload(PayloadType.FINISH, intent = ourIntent))
             val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
                 RendezvousFailureReason.OtherDeviceNotSignedIn
             } else {
@@ -93,14 +95,14 @@ class Rendezvous(
         Timber.tag(TAG).i("Waiting for protocols")
         val protocolsResponse = receive()
 
-        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) {
-            send(Payload(PayloadType.Finish, outcome = "unsupported"))
+        if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) {
+            send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED))
             Timber.tag(TAG).i("No supported protocol")
             cancel(RendezvousFailureReason.Unknown)
             return null
         }
 
-        send(Payload(PayloadType.Progress, protocol = "login_token"))
+        send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN))
 
         return checksum
     }
@@ -110,21 +112,23 @@ class Rendezvous(
 
         val loginToken = receive()
 
-        if (loginToken?.type == PayloadType.Finish) {
+        if (loginToken?.type == PayloadType.FINISH) {
             when (loginToken.outcome) {
-                "declined" -> {
+                Outcome.DECLINED -> {
                     Timber.tag(TAG).i("Login declined by other device")
                     channel.cancel(RendezvousFailureReason.UserDeclined)
                     return null
                 }
-                "unsupported" -> {
+                Outcome.UNSUPPORTED -> {
                     Timber.tag(TAG).i("Not supported")
                     channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
                     return null
                 }
+                else -> {
+                    channel.cancel(RendezvousFailureReason.Unknown)
+                    return null
+                }
             }
-            channel.cancel(RendezvousFailureReason.Unknown)
-            return null
         }
 
         val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
@@ -141,7 +145,7 @@ class Rendezvous(
         val crypto = session.cryptoService()
         val deviceId = crypto.getMyDevice().deviceId
         val deviceKey = crypto.getMyDevice().fingerprint()
-        send(Payload(PayloadType.Progress, outcome = "success", deviceId = deviceId, deviceKey = deviceKey))
+        send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
 
         // await confirmation of verification
 
@@ -149,8 +153,9 @@ class Rendezvous(
         val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
         val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
         if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${
-                verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
+            Timber.tag(TAG).w(
+                    "Verifying device $verifyingDeviceId key doesn't match: ${
+                        verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
             )
             throw RuntimeException("Key from verifying device doesn't match")
         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 5dbac894ae..489d20e588 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -50,6 +50,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         private const val KEY_SPEC = "AES"
         private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
+        // n.b. we are only aver processing byte array that we have generated, so we can make assumptions about the length
         private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
             val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
             val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
new file mode 100644
index 0000000000..2dd6e7be28
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.api.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class Outcome(val value: String) {
+    @Json(name = "success")
+    SUCCESS("success"),
+
+    @Json(name = "declined")
+    DECLINED("declined"),
+
+    @Json(name = "unsupported")
+    UNSUPPORTED("unsupported")
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
index 5627452232..593177e625 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
@@ -23,9 +23,9 @@ import com.squareup.moshi.JsonClass
 internal data class Payload(
         @Json val type: PayloadType,
         @Json val intent: RendezvousIntent? = null,
-        @Json val outcome: String? = null,
-        @Json val protocols: List<String>? = null,
-        @Json val protocol: String? = null,
+        @Json val outcome: Outcome? = null,
+        @Json val protocols: List<Protocol>? = null,
+        @Json val protocol: Protocol? = null,
         @Json val homeserver: String? = null,
         @Json(name = "login_token") val loginToken: String? = null,
         @Json(name = "device_id") val deviceId: String? = null,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
index 9854e7c070..5ff4cd7cfa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -20,11 +20,11 @@ import com.squareup.moshi.Json
 
 internal enum class PayloadType(val value: String) {
     @Json(name = "m.login.start")
-    Start("m.login.start"),
+    START("m.login.start"),
 
     @Json(name = "m.login.finish")
-    Finish("m.login.finish"),
+    FINISH("m.login.finish"),
 
     @Json(name = "m.login.progress")
-    Progress("m.login.progress")
+    PROGRESS("m.login.progress")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
new file mode 100644
index 0000000000..18381984a5
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.api.rendezvous.model
+
+import com.squareup.moshi.Json
+
+enum class Protocol(val value: String) {
+    @Json(name = "login_token")
+    LOGIN_TOKEN("login_token")
+}

From 4306c57236dccd50531bf20e8732031a84d6bb0e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:01:12 +0100
Subject: [PATCH 214/400] Thread safe use of OlmSAS

---
 .../channels/ECDHRendezvousChannel.kt         | 69 ++++++++++---------
 1 file changed, 38 insertions(+), 31 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 489d20e588..ca7083b297 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -90,43 +90,45 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     override suspend fun connect(): String {
-        if (olmSAS == null) {
-            throw RuntimeException("Channel closed")
-        }
-        val isInitiator = theirPublicKey == null
+        olmSAS ?.let { olmSAS ->
+            val isInitiator = theirPublicKey == null
 
-        if (isInitiator) {
-            Timber.tag(TAG).i("Waiting for other device to send their public key")
-            val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
+            if (isInitiator) {
+                Timber.tag(TAG).i("Waiting for other device to send their public key")
+                val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
 
-            if (res.key == null) {
-                throw RendezvousError(
-                        "Unsupported algorithm: ${res.algorithm}",
-                        RendezvousFailureReason.UnsupportedAlgorithm,
+                if (res.key == null) {
+                    throw RendezvousError(
+                            "Unsupported algorithm: ${res.algorithm}",
+                            RendezvousFailureReason.UnsupportedAlgorithm,
+                    )
+                }
+                theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
+            } else {
+                // send our public key unencrypted
+                Timber.tag(TAG).i("Sending public key")
+                send(
+                        ECDHPayload(
+                                algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
+                                key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+                        )
                 )
             }
-            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
-        } else {
-            // send our public key unencrypted
-            Timber.tag(TAG).i("Sending public key")
-            send(
-                    ECDHPayload(
-                    algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
-                    key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-            )
-            )
-        }
 
-        olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+            synchronized(olmSAS) {
+                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
 
-        val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
-        val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
-        val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
+                val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
+                val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
+                val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
 
-        aesKey = olmSAS!!.generateShortCode(aesInfo, 32)
+                aesKey = olmSAS.generateShortCode(aesInfo, 32)
 
-        val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5)
-        return getDecimalCodeRepresentation(rawChecksum)
+                val rawChecksum = olmSAS.generateShortCode(aesInfo, 5)
+                return getDecimalCodeRepresentation(rawChecksum)
+            }
+        } ?: throw RuntimeException("Channel closed")
     }
 
     private suspend fun send(payload: ECDHPayload) {
@@ -174,8 +176,13 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     override suspend fun close() {
-        olmSAS?.releaseSas()
-        olmSAS = null
+        olmSAS ?.let {
+            synchronized(it) {
+                // this does a double release check already so we don't re-check ourselves
+                it.releaseSas()
+                olmSAS = null
+            }
+        }
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {

From fb86ab70a270e124b15b3e7af888381f32b78dd8 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:05:32 +0100
Subject: [PATCH 215/400] Comments and error mapping

---
 .../transports/SimpleHttpRendezvousTransport.kt          | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 03e8b0cda7..e899a64e99 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -99,9 +99,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
 
     override suspend fun receive(): ByteArray? {
         val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
-        var done = false
         val httpClient = okhttp3.OkHttpClient.Builder().build()
-        while (!done) {
+        while (true) {
             if (cancelled) {
                 return null
             }
@@ -117,8 +116,9 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             val response = httpClient.newCall(request.build()).execute()
 
             try {
+                // expired
                 if (response.code == 404) {
-                    cancel(RendezvousFailureReason.Unknown)
+                    cancel(RendezvousFailureReason.Expired)
                     return null
                 }
 
@@ -135,7 +135,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                     return response.body?.bytes()
                 }
 
-                done = false
+                // sleep for a second before polling again
+                // we rely on the server expiring the channel rather than checking it ourselves
                 delay(1000)
             } finally {
                 response.close()

From 91bb86d3c60844cc21c2d23e272c3798a15a3df5 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 17 Oct 2022 14:16:29 +0300
Subject: [PATCH 216/400] Code review fixes.

---
 .../src/main/res/values/strings.xml           |  2 +-
 .../GetHomeServerCapabilitiesTask.kt          |  5 +--
 .../features/login/qr/QrCodeLoginAction.kt    |  2 +-
 .../features/login/qr/QrCodeLoginActivity.kt  |  4 +-
 .../app/features/login/qr/QrCodeLoginArgs.kt  |  2 +-
 .../login/qr/QrCodeLoginHeaderView.kt         | 16 +++----
 .../qr/QrCodeLoginInstructionsFragment.kt     | 42 +++++++++----------
 .../login/qr/QrCodeLoginInstructionsView.kt   | 21 +++++-----
 .../login/qr/QrCodeLoginShowQrCodeFragment.kt | 20 ++++-----
 .../login/qr/QrCodeLoginStatusFragment.kt     | 24 +++++------
 .../features/login/qr/QrCodeLoginViewModel.kt | 30 ++++---------
 .../onboarding/OnboardingViewModel.kt         | 20 +++++----
 .../ftueauth/FtueAuthCombinedLoginFragment.kt |  6 +--
 .../v2/VectorSettingsDevicesFragment.kt       | 29 ++++++-------
 .../layout/fragment_ftue_combined_login.xml   |  4 +-
 .../view_qr_code_login_instructions.xml       | 22 ----------
 16 files changed, 102 insertions(+), 147 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index a13db6a6db..1c3daf9c3d 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3385,7 +3385,7 @@
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
-    <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
+    <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code in this device\'</string>
     <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Start at the sign in screen</string>
     <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Select \'Sign in with QR code\'</string>
     <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Start at the sign in screen</string>
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index d65e629b71..2c3cb440b6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -20,7 +20,6 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.api.MatrixPatterns.getServerName
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
-import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.internal.auth.version.Versions
@@ -144,8 +143,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 homeServerCapabilitiesEntity.lastVersionIdentityServerSupported = getVersionResult.isLoginAndRegistrationSupportedBySdk()
                 homeServerCapabilitiesEntity.canControlLogoutDevices = getVersionResult.doesServerSupportLogoutDevices()
                 homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
-                        getVersionResult.doesServerSupportThreads().orFalse()
-                homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult.doesServerSupportQrCodeLogin().orFalse()
+                        getVersionResult.doesServerSupportThreads()
+                homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult.doesServerSupportQrCodeLogin()
             }
 
             if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
index d946f744cf..8854d0720f 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
@@ -20,6 +20,6 @@ import im.vector.app.core.platform.VectorViewModelAction
 
 sealed class QrCodeLoginAction : VectorViewModelAction {
     data class OnQrCodeScanned(val qrCode: String) : QrCodeLoginAction()
-    object QrCodeViewStarted : QrCodeLoginAction()
+    object GenerateQrCode : QrCodeLoginAction()
     object ShowQrCode : QrCodeLoginAction()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 29190a70cb..f5fd17c0c8 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -26,7 +26,6 @@ import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 import im.vector.lib.core.utils.compat.getParcelableCompat
-import org.matrix.android.sdk.api.extensions.orFalse
 import timber.log.Timber
 
 @AndroidEntryPoint
@@ -45,7 +44,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
                     showInstructionsFragment(qrCodeLoginArgs)
                 }
                 QrCodeLoginType.LINK_A_DEVICE -> {
-                    if (qrCodeLoginArgs.showQrCodeByDefault.orFalse()) {
+                    if (qrCodeLoginArgs.showQrCodeImmediately) {
                         handleNavigateToShowQrCodeScreen()
                     } else {
                         showInstructionsFragment(qrCodeLoginArgs)
@@ -54,6 +53,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
                 null -> {
                     Timber.i("QrCodeLoginArgs is null. This is not expected.")
                     finish()
+                    return
                 }
             }
         }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
index c7b681c488..6c23d07c0f 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt
@@ -22,5 +22,5 @@ import kotlinx.parcelize.Parcelize
 @Parcelize
 data class QrCodeLoginArgs(
         val loginType: QrCodeLoginType,
-        val showQrCodeByDefault: Boolean,
+        val showQrCodeImmediately: Boolean,
 ) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
index f14d711d3c..03478d2f50 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt
@@ -24,6 +24,7 @@ import android.view.LayoutInflater
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.content.res.use
 import im.vector.app.R
+import im.vector.app.core.extensions.setTextOrHide
 import im.vector.app.databinding.ViewQrCodeLoginHeaderBinding
 
 class QrCodeLoginHeaderView @JvmOverloads constructor(
@@ -52,27 +53,26 @@ class QrCodeLoginHeaderView @JvmOverloads constructor(
 
     private fun setTitle(typedArray: TypedArray) {
         val title = typedArray.getString(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderTitle)
-        binding.qrCodeLoginHeaderTitleTextView.text = title
+        setTitle(title)
     }
 
     private fun setDescription(typedArray: TypedArray) {
         val description = typedArray.getString(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderDescription)
-        binding.qrCodeLoginHeaderDescriptionTextView.text = description
+        setDescription(description)
     }
 
     private fun setImage(typedArray: TypedArray) {
         val imageResource = typedArray.getResourceId(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderImageResource, 0)
         val backgroundTint = typedArray.getColor(R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderImageBackgroundTint, 0)
-        binding.qrCodeLoginHeaderImageView.setImageResource(imageResource)
-        binding.qrCodeLoginHeaderImageView.backgroundTintList = ColorStateList.valueOf(backgroundTint)
+        setImage(imageResource, backgroundTint)
     }
 
-    fun setTitle(title: String) {
-        binding.qrCodeLoginHeaderTitleTextView.text = title
+    fun setTitle(title: String?) {
+        binding.qrCodeLoginHeaderTitleTextView.setTextOrHide(title)
     }
 
-    fun setDescription(description: String) {
-        binding.qrCodeLoginHeaderDescriptionTextView.text = description
+    fun setDescription(description: String?) {
+        binding.qrCodeLoginHeaderDescriptionTextView.setTextOrHide(description)
     }
 
     fun setImage(imageResource: Int, backgroundTintColor: Int) {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index ae3ba9574b..efd23f2530 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -22,6 +22,7 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import com.airbnb.mvrx.activityViewModel
+import com.airbnb.mvrx.withState
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.extensions.registerStartForActivityResult
@@ -41,32 +42,10 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-        observeViewState()
         initScanQrCodeButton()
         initShowQrCodeButton()
     }
 
-    private fun observeViewState() {
-        viewModel.onEach {
-            if (it.loginType == QrCodeLoginType.LOGIN) {
-                views.qrCodeLoginInstructionsView.setInstructions(
-                        listOf(
-                                getString(R.string.qr_code_login_new_device_instruction_1),
-                                getString(R.string.qr_code_login_new_device_instruction_2),
-                                getString(R.string.qr_code_login_new_device_instruction_3),
-                        )
-                )
-            } else {
-                views.qrCodeLoginInstructionsView.setInstructions(
-                        listOf(
-                                getString(R.string.qr_code_login_link_a_device_scan_qr_code_instruction_1),
-                                getString(R.string.qr_code_login_link_a_device_scan_qr_code_instruction_2),
-                        )
-                )
-            }
-        }
-    }
-
     private fun initShowQrCodeButton() {
         views.qrCodeLoginInstructionsShowQrCodeButton.debouncedClicks {
             viewModel.handle(QrCodeLoginAction.ShowQrCode)
@@ -99,4 +78,23 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
     private fun onQrCodeScannerFailed() {
         Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
     }
+
+    override fun invalidate() = withState(viewModel) { state ->
+        if (state.loginType == QrCodeLoginType.LOGIN) {
+            views.qrCodeLoginInstructionsView.setInstructions(
+                    listOf(
+                            getString(R.string.qr_code_login_new_device_instruction_1),
+                            getString(R.string.qr_code_login_new_device_instruction_2),
+                            getString(R.string.qr_code_login_new_device_instruction_3),
+                    )
+            )
+        } else {
+            views.qrCodeLoginInstructionsView.setInstructions(
+                    listOf(
+                            getString(R.string.qr_code_login_link_a_device_scan_qr_code_instruction_1),
+                            getString(R.string.qr_code_login_link_a_device_scan_qr_code_instruction_2),
+                    )
+            )
+        }
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
index 7ef1706b37..ed5c4de175 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt
@@ -54,18 +54,19 @@ class QrCodeLoginInstructionsView @JvmOverloads constructor(
         val instruction1 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction1)
         val instruction2 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction2)
         val instruction3 = typedArray.getString(R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction3)
-        binding.instructions1Layout.isVisible = instruction1 != null
-        binding.instructions2Layout.isVisible = instruction2 != null
-        binding.instructions3Layout.isVisible = instruction3 != null
-        binding.instruction1TextView.text = instruction1
-        binding.instruction2TextView.text = instruction2
-        binding.instruction3TextView.text = instruction3
+        setInstructions(
+                listOf(
+                        instruction1,
+                        instruction2,
+                        instruction3,
+                )
+        )
     }
 
-    fun setInstructions(instructions: List<String>) {
-        setInstruction(binding.instructions1Layout, binding.instruction1TextView, instructions.getOrNull(0))
-        setInstruction(binding.instructions2Layout, binding.instruction2TextView, instructions.getOrNull(1))
-        setInstruction(binding.instructions3Layout, binding.instruction3TextView, instructions.getOrNull(2))
+    fun setInstructions(instructions: List<String?>?) {
+        setInstruction(binding.instructions1Layout, binding.instruction1TextView, instructions?.getOrNull(0))
+        setInstruction(binding.instructions2Layout, binding.instruction2TextView, instructions?.getOrNull(1))
+        setInstruction(binding.instructions3Layout, binding.instruction3TextView, instructions?.getOrNull(2))
     }
 
     private fun setInstruction(instructionLayout: LinearLayout, instructionTextView: TextView, instruction: String?) {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
index efc2c9dd08..d31f531a49 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt
@@ -21,6 +21,7 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import com.airbnb.mvrx.activityViewModel
+import com.airbnb.mvrx.withState
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
@@ -38,8 +39,7 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         initCancelButton()
-        observeViewState()
-        viewModel.handle(QrCodeLoginAction.QrCodeViewStarted)
+        viewModel.handle(QrCodeLoginAction.GenerateQrCode)
     }
 
     private fun initCancelButton() {
@@ -48,15 +48,6 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
         }
     }
 
-    private fun observeViewState() {
-        viewModel.onEach {
-            setInstructions(it.loginType)
-            it.generatedQrCodeData?.let { qrCodeData ->
-                showQrCode(qrCodeData)
-            }
-        }
-    }
-
     private fun setInstructions(loginType: QrCodeLoginType) {
         if (loginType == QrCodeLoginType.LOGIN) {
             views.qrCodeLoginShowQrCodeHeaderView.setDescription(getString(R.string.qr_code_login_header_show_qr_code_new_device_description))
@@ -81,4 +72,11 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
     private fun showQrCode(qrCodeData: String) {
         views.qrCodeLoginSHowQrCodeImageView.setData(qrCodeData)
     }
+
+    override fun invalidate() = withState(viewModel) { state ->
+        state.generatedQrCodeData?.let { qrCodeData ->
+            showQrCode(qrCodeData)
+        }
+        setInstructions(state.loginType)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 405e37af15..a9c589e469 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -22,6 +22,7 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.core.view.isVisible
 import com.airbnb.mvrx.activityViewModel
+import com.airbnb.mvrx.withState
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.platform.VectorBaseFragment
@@ -40,7 +41,6 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         initCancelButton()
-        observeViewState()
     }
 
     private fun initCancelButton() {
@@ -49,18 +49,6 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         }
     }
 
-    private fun observeViewState() {
-        viewModel.onEach {
-            when (it.connectionStatus) {
-                is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(it.connectionStatus, it.loginType)
-                QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice()
-                QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn()
-                is QrCodeLoginConnectionStatus.Failed -> handleFailed(it.connectionStatus)
-                null -> { /* NOOP */ }
-            }
-        }
-    }
-
     private fun handleFailed(connectionStatus: QrCodeLoginConnectionStatus.Failed) {
         views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = false
         views.qrCodeLoginStatusLoadingLayout.isVisible = false
@@ -128,4 +116,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
                 backgroundTintColor = ThemeUtils.getColor(requireContext(), R.attr.colorPrimary)
         )
     }
+
+    override fun invalidate() = withState(viewModel) { state ->
+        when (state.connectionStatus) {
+            is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(state.connectionStatus, state.loginType)
+            QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice()
+            QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn()
+            is QrCodeLoginConnectionStatus.Failed -> handleFailed(state.connectionStatus)
+            null -> { /* NOOP */ }
+        }
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index da3348653c..e979ffa63c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -23,8 +23,7 @@ import dagger.assisted.AssistedInject
 import im.vector.app.core.di.MavericksAssistedViewModelFactory
 import im.vector.app.core.di.hiltMavericksViewModelFactory
 import im.vector.app.core.platform.VectorViewModel
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
+import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
@@ -40,7 +39,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     override fun handle(action: QrCodeLoginAction) {
         when (action) {
             is QrCodeLoginAction.OnQrCodeScanned -> handleOnQrCodeScanned(action)
-            QrCodeLoginAction.QrCodeViewStarted -> handleQrCodeViewStarted()
+            QrCodeLoginAction.GenerateQrCode -> handleQrCodeViewStarted()
             QrCodeLoginAction.ShowQrCode -> handleShowQrCode()
         }
     }
@@ -67,18 +66,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             }
             _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
         }
-
-        // TODO. UI test purpose. Fixme remove!
-        viewModelScope.launch {
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.TIMEOUT, true)
-            delay(3000)
-            onConnectionEstablished("1234-ABCD-5678-EFGH")
-            delay(3000)
-            onSigningIn()
-            delay(3000)
-            onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false)
-        }
     }
 
     private fun onFailed(errorType: QrCodeLoginErrorType, canTryAgain: Boolean) {
@@ -106,17 +93,14 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         }
     }
 
-    /**
-     * TODO. UI test purpose. Fixme accordingly.
-     */
+    // TODO. Implement in the logic related PR.
     private fun isValidQrCode(qrCode: String): Boolean {
-        return qrCode.startsWith("http")
+        Timber.d("isValidQrCode: $qrCode")
+        return false
     }
 
-    /**
-     * TODO. UI test purpose. Fixme accordingly.
-     */
+    // TODO. Implement in the logic related PR.
     private fun generateQrCodeData(): String {
-        return "https://element.io"
+        return "TODO"
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index fb8c29f39f..46b14c6d5f 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -118,7 +118,7 @@ class OnboardingViewModel @AssistedInject constructor(
         }
     }
 
-    private fun observeQrCodeLoginCapability(homeServerUrl: String) = viewModelScope.launch {
+    private fun checkQrCodeLoginCapability(homeServerUrl: String) {
         if (!vectorFeatures.isQrCodeLoginEnabled()) {
             setState {
                 copy(
@@ -133,13 +133,15 @@ class OnboardingViewModel @AssistedInject constructor(
                 )
             }
         } else {
-            // check if selected server supports MSC3882 first
-            homeServerConnectionConfigFactory.create(homeServerUrl)?.let {
-                val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
-                setState {
-                    copy(
-                            canLoginWithQrCode = canLoginWithQrCode
-                    )
+            viewModelScope.launch {
+                // check if selected server supports MSC3882 first
+                homeServerConnectionConfigFactory.create(homeServerUrl)?.let {
+                    val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
+                    setState {
+                        copy(
+                                canLoginWithQrCode = canLoginWithQrCode
+                        )
+                    }
                 }
             }
         }
@@ -707,7 +709,7 @@ class OnboardingViewModel @AssistedInject constructor(
             _viewEvents.post(OnboardingViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
         } else {
             startAuthenticationFlow(action, homeServerConnectionConfig, serverTypeOverride, postAction)
-            observeQrCodeLoginCapability(homeServerConnectionConfig.homeServerUri.toString())
+            checkQrCodeLoginCapability(homeServerConnectionConfig.homeServerUri.toString())
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
index 4d32964b7d..aad54877c9 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt
@@ -81,20 +81,18 @@ class FtueAuthCombinedLoginFragment :
     }
 
     private fun configureQrCodeLoginButtonVisibility(canLoginWithQrCode: Boolean) {
+        views.loginWithQrCode.isVisible = canLoginWithQrCode
         if (canLoginWithQrCode) {
-            views.loginWithQrCode.isVisible = true
             views.loginWithQrCode.debouncedClicks {
                 navigator
                         .openLoginWithQrCode(
                                 requireActivity(),
                                 QrCodeLoginArgs(
                                         loginType = QrCodeLoginType.LOGIN,
-                                        showQrCodeByDefault = false,
+                                        showQrCodeImmediately = false,
                                 )
                         )
             }
-        } else {
-            views.loginWithQrCode.isVisible = false
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
index d4ee1d92eb..080405c2ce 100644
--- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt
@@ -170,28 +170,25 @@ class VectorSettingsDevicesFragment :
         views.deviceListHeaderShowQrCodeButton.isVisible = true
 
         views.deviceListHeaderScanQrCodeButton.debouncedClicks {
-            navigator
-                    .openLoginWithQrCode(
-                            requireActivity(),
-                            QrCodeLoginArgs(
-                                    loginType = QrCodeLoginType.LINK_A_DEVICE,
-                                    showQrCodeByDefault = false,
-                            )
-                    )
+            navigateToQrCodeScreen(showQrCodeImmediately = false)
         }
 
         views.deviceListHeaderShowQrCodeButton.debouncedClicks {
-            navigator
-                    .openLoginWithQrCode(
-                            requireActivity(),
-                            QrCodeLoginArgs(
-                                    loginType = QrCodeLoginType.LINK_A_DEVICE,
-                                    showQrCodeByDefault = true,
-                            )
-                    )
+            navigateToQrCodeScreen(showQrCodeImmediately = true)
         }
     }
 
+    private fun navigateToQrCodeScreen(showQrCodeImmediately: Boolean) {
+        navigator
+                .openLoginWithQrCode(
+                        requireActivity(),
+                        QrCodeLoginArgs(
+                                loginType = QrCodeLoginType.LINK_A_DEVICE,
+                                showQrCodeImmediately = showQrCodeImmediately,
+                        )
+                )
+    }
+
     override fun onDestroyView() {
         cleanUpLearnMoreButtonsListeners()
         super.onDestroyView()
diff --git a/vector/src/main/res/layout/fragment_ftue_combined_login.xml b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
index 17fe895cf2..7eff92f4f9 100644
--- a/vector/src/main/res/layout/fragment_ftue_combined_login.xml
+++ b/vector/src/main/res/layout/fragment_ftue_combined_login.xml
@@ -251,10 +251,12 @@
             android:layout_height="60dp"
             android:layout_marginTop="12dp"
             android:text="@string/login_scan_qr_code"
+            android:visibility="gone"
             app:drawableLeftCompat="@drawable/ic_qr_code"
             app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
             app:layout_constraintStart_toStartOf="@id/loginGutterStart"
-            app:layout_constraintTop_toBottomOf="@id/ssoButtonsHeader" />
+            app:layout_constraintTop_toBottomOf="@id/ssoButtonsHeader"
+            tools:visibility="visible" />
 
         <im.vector.app.features.login.SocialLoginButtonsView
             android:id="@+id/ssoButtons"
diff --git a/vector/src/main/res/layout/view_qr_code_login_instructions.xml b/vector/src/main/res/layout/view_qr_code_login_instructions.xml
index 6ff4b306a3..fee3e2c5d6 100644
--- a/vector/src/main/res/layout/view_qr_code_login_instructions.xml
+++ b/vector/src/main/res/layout/view_qr_code_login_instructions.xml
@@ -98,26 +98,4 @@
             tools:text="@string/qr_code_login_new_device_instruction_3" />
     </LinearLayout>
 
-    <LinearLayout
-        android:id="@+id/instructions4Layout"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="12dp"
-        android:orientation="horizontal"
-        android:visibility="gone"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/instructions3Layout"
-        tools:visibility="visible">
-
-        <TextView
-            style="@style/TextAppearance.Vector.Caption"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="@drawable/circle_qr_code_login_instruction_with_border"
-            android:padding="6dp"
-            android:text="@string/four"
-            android:textColor="?colorPrimary" />
-    </LinearLayout>
-
 </merge>

From 1976451c8164ee15b88186ae1fc6291513d158ec Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:23:14 +0100
Subject: [PATCH 217/400] Lint

---
 .../matrix/android/sdk/api/rendezvous/RendezvousChannel.kt    | 2 +-
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt    | 4 ----
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt   | 1 -
 3 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 9d2843ce66..31014e392d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -20,7 +20,7 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 
 /**
- * Representation of a rendezvous channel such as that described by MSC3903
+ * Representation of a rendezvous channel such as that described by MSC3903.
  */
 interface RendezvousChannel {
     var transport: RendezvousTransport
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index e899a64e99..3e9b11a68b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -16,8 +16,6 @@
 
 package org.matrix.android.sdk.api.rendezvous.transports
 
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
 import kotlinx.coroutines.delay
 import okhttp3.MediaType
 import okhttp3.Request
@@ -142,8 +140,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
                 response.close()
             }
         }
-
-        return null
     }
 
     override suspend fun cancel(reason: RendezvousFailureReason) {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 8b70f61e80..36e88b284c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -16,7 +16,6 @@
 
 package im.vector.app.features.login.qr
 
-import android.content.Context
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory

From eb30ef166acff6fca015b321c71ce1b10a811c49 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 12:32:40 +0100
Subject: [PATCH 218/400] Improve 404 handling

---
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt   | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 3e9b11a68b..ca2a3425cd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -75,7 +75,9 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         val response = httpClient.newCall(request.build()).execute()
 
         if (response.code == 404) {
+            // we set to unknown and the cancel method will rewrite the reason to expired if applicable
             cancel(RendezvousFailureReason.Unknown)
+            return
         }
         etag = response.header("etag")
 
@@ -116,7 +118,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             try {
                 // expired
                 if (response.code == 404) {
-                    cancel(RendezvousFailureReason.Expired)
+                    // we set to unknown and the cancel method will rewrite the reason to expired if applicable
+                    cancel(RendezvousFailureReason.Unknown)
                     return null
                 }
 

From d3a24fec9b391913ee898781325e687ef1005410 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 17 Oct 2022 15:35:21 +0300
Subject: [PATCH 219/400] Lint fix.

---
 library/ui-strings/src/main/res/values/strings.xml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 1c3daf9c3d..dbab79e1be 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3369,8 +3369,7 @@
     <string name="one">1</string>
     <string name="two">2</string>
     <string name="three">3</string>
-    <string name="four">4</string>
-   
+
     <!-- QR Code Login -->
     <string name="qr_code_login_header_scan_qr_code_title">Scan QR code</string>
     <string name="qr_code_login_header_scan_qr_code_description">Use the camera on this device to scan the QR code shown on your other device:</string>

From d616251f260515548f8b699514247b6e0a8ee9bc Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 13:41:27 +0100
Subject: [PATCH 220/400] Fix merge

---
 .../session/homeserver/GetHomeServerCapabilitiesTask.kt         | 2 --
 1 file changed, 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 9147c4e1cd..2c3cb440b6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -132,8 +132,6 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 homeServerCapabilitiesEntity.roomVersionsJson = capabilities?.roomVersions?.let {
                     MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it)
                 }
-                homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
-                        getVersionResult?.doesServerSupportThreads().orFalse()
             }
 
             if (getMediaConfigResult != null) {

From 52147512185dfe6f73a81897919fd718548ddcc2 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 17 Oct 2022 15:00:39 +0200
Subject: [PATCH 221/400] Let the doctor be less strict and just warn. Keep the
 useful log "Is CI build: $isCiBuild".

---
 tools/gradle/doctor.gradle | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/gradle/doctor.gradle b/tools/gradle/doctor.gradle
index c77d2eb338..705bb3e250 100644
--- a/tools/gradle/doctor.gradle
+++ b/tools/gradle/doctor.gradle
@@ -54,7 +54,7 @@ doctor {
     /**
      * Warn when not using parallel GC. Parallel GC is faster for build type tasks and is no longer the default in Java 9+.
      */
-    warnWhenNotUsingParallelGC = !isCiBuild
+    warnWhenNotUsingParallelGC = true
     /**
      * Throws an error when the `Delete` or `clean` task has dependencies.
      * If a clean task depends on other tasks, clean can be reordered and made to run after the tasks that would produce
@@ -82,7 +82,7 @@ doctor {
         /**
          * Fail on any `JAVA_HOME` issues.
          */
-        failOnError.set(!isCiBuild)
+        failOnError.set(false)
         /**
          * Extra message text, if any, to show with the Gradle Doctor message. This is useful if you have a wiki page or
          * other instructions that you want to link for developers on your team if they encounter an issue.

From b5eb15c7e3907030842f2389d48262e0b111a7e2 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 23 Sep 2022 15:54:30 +0200
Subject: [PATCH 222/400] Avoid using ActiveSessionHolder in a Fragment. Move
 the userId to `state.personalizationState`

---
 .../vector/app/features/onboarding/OnboardingViewModel.kt  | 1 +
 .../vector/app/features/onboarding/OnboardingViewState.kt  | 3 ++-
 .../ftueauth/FtueAuthChooseProfilePictureFragment.kt       | 7 ++-----
 .../app/features/onboarding/OnboardingViewModelTest.kt     | 5 +++--
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 9bb52fb1a5..93e2dfd154 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -641,6 +641,7 @@ class OnboardingViewModel @AssistedInject constructor(
                 val homeServerCapabilities = session.homeServerCapabilitiesService().getHomeServerCapabilities()
                 val capabilityOverrides = vectorOverrides.forceHomeserverCapabilities?.firstOrNull()
                 state.personalizationState.copy(
+                        userId = session.myUserId,
                         displayName = state.registrationState.selectedMatrixId?.let { MatrixPatterns.extractUserNameFromId(it) },
                         supportsChangingDisplayName = capabilityOverrides?.canChangeDisplayName ?: homeServerCapabilities.canChangeDisplayName,
                         supportsChangingProfilePicture = capabilityOverrides?.canChangeAvatar ?: homeServerCapabilities.canChangeAvatar
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
index 99678ea5c1..b078f41ae2 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
@@ -78,10 +78,11 @@ data class SelectedHomeserverState(
 
 @Parcelize
 data class PersonalizationState(
+        val userId: String = "",
         val supportsChangingDisplayName: Boolean = false,
         val supportsChangingProfilePicture: Boolean = false,
         val displayName: String? = null,
-        val selectedPictureUri: Uri? = null
+        val selectedPictureUri: Uri? = null,
 ) : Parcelable {
 
     fun supportsPersonalization() = supportsChangingDisplayName || supportsChangingProfilePicture
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt
index 92d0aa2a0f..5450c74095 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthChooseProfilePictureFragment.kt
@@ -26,7 +26,6 @@ import androidx.core.view.isInvisible
 import com.airbnb.mvrx.withState
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
 import im.vector.app.core.dialogs.GalleryOrCameraDialogHelperFactory
 import im.vector.app.databinding.FragmentFtueProfilePictureBinding
@@ -42,7 +41,6 @@ class FtueAuthChooseProfilePictureFragment :
         AbstractFtueAuthFragment<FragmentFtueProfilePictureBinding>(),
         GalleryOrCameraDialogHelper.Listener {
 
-    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
     @Inject lateinit var galleryOrCameraDialogHelperFactory: GalleryOrCameraDialogHelperFactory
     @Inject lateinit var avatarRenderer: AvatarRenderer
 
@@ -85,10 +83,9 @@ class FtueAuthChooseProfilePictureFragment :
         views.profilePictureSubmit.isEnabled = hasSetPicture
         views.changeProfilePictureIcon.setImageResource(if (hasSetPicture) R.drawable.ic_edit else R.drawable.ic_camera_plain)
 
-        val session = activeSessionHolder.getActiveSession()
         val matrixItem = MatrixItem.UserItem(
-                id = session.myUserId,
-                displayName = state.personalizationState.displayName ?: ""
+                id = state.personalizationState.userId,
+                displayName = state.personalizationState.displayName.orEmpty()
         )
         avatarRenderer.render(matrixItem, localUri = state.personalizationState.selectedPictureUri, imageView = views.profilePictureView)
     }
diff --git a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
index 82adc70fe3..865f1c79d6 100644
--- a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
@@ -687,7 +687,7 @@ class OnboardingViewModelTest {
                 .assertStatesChanges(
                         initialState,
                         { copy(isLoading = true) },
-                        { copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState(A_USERNAME)) }
+                        { copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState(A_MATRIX_ID, A_USERNAME)) }
                 )
                 .assertEvents(OnboardingViewEvents.OnAccountCreated)
                 .finish()
@@ -1196,7 +1196,8 @@ class OnboardingViewModelTest {
     }
 }
 
-private fun HomeServerCapabilities.toPersonalisationState(displayName: String? = null) = PersonalizationState(
+private fun HomeServerCapabilities.toPersonalisationState(userId: String, displayName: String? = null) = PersonalizationState(
+        userId = userId,
         supportsChangingDisplayName = canChangeDisplayName,
         supportsChangingProfilePicture = canChangeAvatar,
         displayName = displayName,

From ae802dea315b594f9c02209b4bd681cf5a5635c8 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 23 Sep 2022 15:56:55 +0200
Subject: [PATCH 223/400] Avoid using ActiveSessionHolder in a Fragment. Use
 the userId from `state.personalizationState`

---
 .../ftueauth/FtueAuthAccountCreatedFragment.kt         | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthAccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthAccountCreatedFragment.kt
index a53ca52e85..2089dc5ad0 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthAccountCreatedFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthAccountCreatedFragment.kt
@@ -26,21 +26,17 @@ import androidx.core.view.isVisible
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.core.animations.play
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.utils.isAnimationEnabled
 import im.vector.app.core.utils.styleMatchingText
 import im.vector.app.databinding.FragmentFtueAccountCreatedBinding
 import im.vector.app.features.onboarding.OnboardingAction
 import im.vector.app.features.onboarding.OnboardingViewEvents
 import im.vector.app.features.onboarding.OnboardingViewState
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class FtueAuthAccountCreatedFragment :
         AbstractFtueAuthFragment<FragmentFtueAccountCreatedBinding>() {
 
-    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
-
     private var hasPlayedConfetti = false
 
     override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueAccountCreatedBinding {
@@ -53,15 +49,15 @@ class FtueAuthAccountCreatedFragment :
     }
 
     private fun setupViews() {
-        val userId = activeSessionHolder.getActiveSession().myUserId
-        val subtitle = getString(R.string.ftue_account_created_subtitle, userId).toSpannable().styleMatchingText(userId, Typeface.BOLD)
-        views.accountCreatedSubtitle.text = subtitle
         views.accountCreatedPersonalize.debouncedClicks { viewModel.handle(OnboardingAction.PersonalizeProfile) }
         views.accountCreatedTakeMeHome.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome)) }
         views.accountCreatedTakeMeHomeCta.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome)) }
     }
 
     override fun updateWithState(state: OnboardingViewState) {
+        val userId = state.personalizationState.userId
+        val subtitle = getString(R.string.ftue_account_created_subtitle, userId).toSpannable().styleMatchingText(userId, Typeface.BOLD)
+        views.accountCreatedSubtitle.text = subtitle
         val canPersonalize = state.personalizationState.supportsPersonalization()
         views.personalizeButtonGroup.isVisible = canPersonalize
         views.takeMeHomeButtonGroup.isVisible = !canPersonalize

From f95d21ef1754ac1e471082244319c64c9b054b27 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 23 Sep 2022 16:32:50 +0200
Subject: [PATCH 224/400] Inject member in VectorBaseActivity instead of using
 SingletonEntryPoint

---
 .../app/core/platform/VectorBaseActivity.kt   | 28 ++++++-------------
 1 file changed, 8 insertions(+), 20 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index 7e61958565..413249e79c 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -105,7 +105,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
 
     protected var analyticsScreenName: MobileScreen.ScreenName? = null
 
-    protected lateinit var analyticsTracker: AnalyticsTracker
+    @Inject lateinit var analyticsTracker: AnalyticsTracker
 
     /* ==========================================================================================
      * View
@@ -149,26 +149,22 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
      * ========================================================================================== */
 
     private lateinit var configurationViewModel: ConfigurationViewModel
-    private lateinit var sessionListener: SessionListener
-    protected lateinit var bugReporter: BugReporter
-    private lateinit var pinLocker: PinLocker
 
+    @Inject lateinit var sessionListener: SessionListener
+    @Inject lateinit var bugReporter: BugReporter
+    @Inject lateinit var pinLocker: PinLocker
     @Inject lateinit var rageShake: RageShake
     @Inject lateinit var buildMeta: BuildMeta
     @Inject lateinit var fontScalePreferences: FontScalePreferences
     @Inject lateinit var vectorLocale: VectorLocaleProvider
+    @Inject lateinit var vectorFeatures: VectorFeatures
+    @Inject lateinit var navigator: Navigator
+    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
+    @Inject lateinit var vectorPreferences: VectorPreferences
 
     // For debug only
     @Inject lateinit var debugReceiver: DebugReceiver
 
-    @Inject
-    lateinit var vectorFeatures: VectorFeatures
-
-    lateinit var navigator: Navigator
-        private set
-
-    private lateinit var activeSessionHolder: ActiveSessionHolder
-    private lateinit var vectorPreferences: VectorPreferences
 
     // Filter for multiple invalid token error
     private var mainActivityStarted = false
@@ -205,7 +201,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
     @CallSuper
     override fun onCreate(savedInstanceState: Bundle?) {
         Timber.i("onCreate Activity ${javaClass.simpleName}")
-        val singletonEntryPoint = singletonEntryPoint()
         val activityEntryPoint = EntryPointAccessors.fromActivity(this, ActivityEntryPoint::class.java)
         ThemeUtils.setActivityTheme(this, getOtherThemes())
         viewModelFactory = activityEntryPoint.viewModelFactory()
@@ -213,12 +208,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
         addOnMultiWindowModeChangedListener(onMultiWindowModeChangedListener)
         setupMenu()
         configurationViewModel = viewModelProvider.get(ConfigurationViewModel::class.java)
-        bugReporter = singletonEntryPoint.bugReporter()
-        pinLocker = singletonEntryPoint.pinLocker()
-        analyticsTracker = singletonEntryPoint.analyticsTracker()
-        navigator = singletonEntryPoint.navigator()
-        activeSessionHolder = singletonEntryPoint.activeSessionHolder()
-        vectorPreferences = singletonEntryPoint.vectorPreferences()
         configurationViewModel.activityRestarter.observe(this) {
             if (!it.hasBeenHandled) {
                 // Recreate the Activity because configuration has changed
@@ -230,7 +219,6 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
                 navigator.openPinCode(this, pinStartForActivityResult, PinMode.AUTH)
             }
         }
-        sessionListener = singletonEntryPoint.sessionListener()
         sessionListener.globalErrorLiveData.observeEvent(this) {
             handleGlobalError(it)
         }

From cbd0972eca266dbe8decd38221af196fffc7adcb Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 23 Sep 2022 16:16:37 +0200
Subject: [PATCH 225/400] Reuse injected members in parent activity.

---
 .../vector/app/features/debug/DebugMenuActivity.kt  |  4 ----
 .../java/im/vector/app/features/MainActivity.kt     | 13 +++++--------
 .../keysbackup/restore/KeysBackupRestoreActivity.kt |  4 ----
 .../keysbackup/setup/KeysBackupSetupActivity.kt     |  2 --
 .../im/vector/app/features/home/HomeActivity.kt     |  4 ----
 .../vector/app/features/link/LinkHandlerActivity.kt | 10 ++++------
 .../app/features/share/IncomingShareActivity.kt     |  4 ----
 .../app/features/webview/VectorWebViewActivity.kt   |  3 ---
 .../vector/app/features/widgets/WidgetActivity.kt   |  4 ----
 9 files changed, 9 insertions(+), 39 deletions(-)

diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
index 005e9c499b..e74caac299 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
@@ -26,7 +26,6 @@ import androidx.core.app.Person
 import androidx.core.content.getSystemService
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.core.time.Clock
@@ -59,9 +58,6 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
 
     override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater)
 
-    @Inject
-    lateinit var activeSessionHolder: ActiveSessionHolder
-
     @Inject
     lateinit var clock: Clock
 
diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt
index 0040962b73..1040727722 100644
--- a/vector/src/main/java/im/vector/app/features/MainActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt
@@ -131,13 +131,10 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
     private lateinit var args: MainActivityArgs
 
     @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
-    @Inject lateinit var sessionHolder: ActiveSessionHolder
     @Inject lateinit var errorFormatter: ErrorFormatter
-    @Inject lateinit var vectorPreferences: VectorPreferences
     @Inject lateinit var uiStateRepository: UiStateRepository
     @Inject lateinit var shortcutsHandler: ShortcutsHandler
     @Inject lateinit var pinCodeHelper: PinCodeHelper
-    @Inject lateinit var pinLocker: PinLocker
     @Inject lateinit var popupAlertManager: PopupAlertManager
     @Inject lateinit var vectorAnalytics: VectorAnalytics
     @Inject lateinit var lockScreenKeyRepository: LockScreenKeyRepository
@@ -232,7 +229,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
     }
 
     private fun doCleanUp() {
-        val session = sessionHolder.getSafeActiveSession()
+        val session = activeSessionHolder.getSafeActiveSession()
         if (session == null) {
             startNextActivityAndFinish()
             return
@@ -244,7 +241,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
                 lifecycleScope.launch {
                     // Just do the local cleanup
                     Timber.w("Account deactivated, start app")
-                    sessionHolder.clearActiveSession()
+                    activeSessionHolder.clearActiveSession()
                     doLocalCleanup(clearPreferences = true, onboardingStore)
                     startNextActivityAndFinish()
                 }
@@ -258,7 +255,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
                         return@launch
                     }
                     Timber.w("SIGN_OUT: success, start app")
-                    sessionHolder.clearActiveSession()
+                    activeSessionHolder.clearActiveSession()
                     doLocalCleanup(clearPreferences = true, onboardingStore)
                     startNextActivityAndFinish()
                 }
@@ -330,10 +327,10 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
             args.isUserLoggedOut ->
                 // the homeserver has invalidated the token (password changed, device deleted, other security reasons)
                 SignedOutActivity.newIntent(this)
-            sessionHolder.hasActiveSession() ->
+            activeSessionHolder.hasActiveSession() ->
                 // We have a session.
                 // Check it can be opened
-                if (sessionHolder.getActiveSession().isOpenable) {
+                if (activeSessionHolder.getActiveSession().isOpenable) {
                     HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true)
                 } else {
                     // The token is still invalid
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
index f7964cf0ed..4adccf3953 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreActivity.kt
@@ -22,7 +22,6 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.extensions.observeEvent
 import im.vector.app.core.extensions.registerStartForActivityResult
@@ -32,7 +31,6 @@ import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
 import im.vector.app.features.workers.signout.ServerBackupStatusAction
 import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
 import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class KeysBackupRestoreActivity : SimpleFragmentActivity() {
@@ -56,8 +54,6 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
         super.onBackPressed()
     }
 
-    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
-
     override fun initUiAndData() {
         super.initUiAndData()
         viewModel = viewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
index 8238da8245..4473d54765 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupActivity.kt
@@ -25,7 +25,6 @@ import androidx.lifecycle.lifecycleScope
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.dialogs.ExportKeysDialog
 import im.vector.app.core.extensions.observeEvent
 import im.vector.app.core.extensions.queryExportKeys
@@ -45,7 +44,6 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
     private lateinit var viewModel: KeysBackupSetupSharedViewModel
 
     @Inject lateinit var keysExporter: KeysExporter
-    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
 
     private val session by lazy {
         activeSessionHolder.getActiveSession()
diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
index 552b78bbb8..2df94fecad 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt
@@ -37,7 +37,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
 import im.vector.app.SpaceStateHandler
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.extensions.hideKeyboard
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.extensions.replaceFragment
@@ -77,7 +76,6 @@ import im.vector.app.features.popup.PopupAlertManager
 import im.vector.app.features.popup.VerificationVectorAlert
 import im.vector.app.features.rageshake.ReportType
 import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
-import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.VectorSettingsActivity
 import im.vector.app.features.spaces.SpaceCreationActivity
 import im.vector.app.features.spaces.SpacePreviewActivity
@@ -129,11 +127,9 @@ class HomeActivity :
 
     private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel()
 
-    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
     @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
     @Inject lateinit var pushersManager: PushersManager
     @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
-    @Inject lateinit var vectorPreferences: VectorPreferences
     @Inject lateinit var popupAlertManager: PopupAlertManager
     @Inject lateinit var shortcutsHandler: ShortcutsHandler
     @Inject lateinit var permalinkHandler: PermalinkHandler
diff --git a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
index 0bdec53f60..2526dc6ed7 100644
--- a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
@@ -24,7 +24,6 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseActivity
@@ -46,7 +45,6 @@ import javax.inject.Inject
 @AndroidEntryPoint
 class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
 
-    @Inject lateinit var sessionHolder: ActiveSessionHolder
     @Inject lateinit var errorFormatter: ErrorFormatter
     @Inject lateinit var permalinkHandler: PermalinkHandler
 
@@ -103,7 +101,7 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
     }
 
     private fun handleConfigUrl(uri: Uri) {
-        if (sessionHolder.hasActiveSession()) {
+        if (activeSessionHolder.hasActiveSession()) {
             displayAlreadyLoginPopup(uri)
         } else {
             // user is not yet logged in, this is the nominal case
@@ -114,7 +112,7 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
     private fun handleSupportedHostUrl() {
         // If we are not logged in, open login screen.
         // In the future, we might want to relaunch the process after login.
-        if (!sessionHolder.hasActiveSession()) {
+        if (!activeSessionHolder.hasActiveSession()) {
             startLoginActivity()
             return
         }
@@ -152,7 +150,7 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
     }
 
     private fun safeSignout(uri: Uri) {
-        val session = sessionHolder.getSafeActiveSession()
+        val session = activeSessionHolder.getSafeActiveSession()
         if (session == null) {
             // Should not happen
             startLoginActivity(uri)
@@ -161,7 +159,7 @@ class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
                 try {
                     session.signOutService().signOut(true)
                     Timber.d("## displayAlreadyLoginPopup(): logout succeeded")
-                    sessionHolder.clearActiveSession()
+                    activeSessionHolder.clearActiveSession()
                     startLoginActivity(uri)
                 } catch (failure: Throwable) {
                     displayError(failure)
diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt
index 3d603e3f6a..a78e6c95c0 100644
--- a/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareActivity.kt
@@ -20,22 +20,18 @@ import android.content.Intent
 import android.os.Bundle
 import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleBinding
 import im.vector.app.features.MainActivity
 import im.vector.app.features.start.StartAppViewModel
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class IncomingShareActivity : VectorBaseActivity<ActivitySimpleBinding>() {
 
     private val startAppViewModel: StartAppViewModel by viewModel()
 
-    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
-
     private val launcher = registerStartForActivityResult {
         if (it.resultCode == RESULT_OK) {
             handleAppStarted()
diff --git a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
index c97f87972e..6f60ed9958 100644
--- a/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/webview/VectorWebViewActivity.kt
@@ -21,12 +21,10 @@ import android.content.Intent
 import android.webkit.WebChromeClient
 import android.webkit.WebView
 import dagger.hilt.android.AndroidEntryPoint
-import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityVectorWebViewBinding
 import im.vector.lib.core.utils.compat.getSerializableCompat
 import org.matrix.android.sdk.api.session.Session
-import javax.inject.Inject
 
 /**
  * This class is responsible for managing a WebView
@@ -39,7 +37,6 @@ class VectorWebViewActivity : VectorBaseActivity<ActivityVectorWebViewBinding>()
 
     override fun getBinding() = ActivityVectorWebViewBinding.inflate(layoutInflater)
 
-    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
     val session: Session by lazy {
         activeSessionHolder.getActiveSession()
     }
diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt
index 586e4dcc6e..eeb3959ef8 100644
--- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt
@@ -39,7 +39,6 @@ import im.vector.app.R
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityWidgetBinding
-import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
 import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewEvents
 import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel
@@ -48,7 +47,6 @@ import im.vector.lib.core.utils.compat.getSerializableCompat
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.events.model.Content
 import java.io.Serializable
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class WidgetActivity : VectorBaseActivity<ActivityWidgetBinding>() {
@@ -83,8 +81,6 @@ class WidgetActivity : VectorBaseActivity<ActivityWidgetBinding>() {
     private val viewModel: WidgetViewModel by viewModel()
     private val permissionViewModel: RoomWidgetPermissionViewModel by viewModel()
 
-    @Inject lateinit var vectorPreferences: VectorPreferences
-
     override fun getBinding() = ActivityWidgetBinding.inflate(layoutInflater)
 
     override fun getTitleRes() = R.string.room_widget_activity_title

From b3068c017da17d07f040c8060f9e3d8b369917db Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 23 Sep 2022 16:20:20 +0200
Subject: [PATCH 226/400] Inject ErrorFormatter in the parent Activity (often
 used)

---
 .../java/im/vector/app/core/platform/VectorBaseActivity.kt  | 3 ++-
 vector/src/main/java/im/vector/app/features/MainActivity.kt | 5 -----
 .../im/vector/app/features/call/dialpad/PstnDialActivity.kt | 2 --
 .../app/features/call/transfer/CallTransferActivity.kt      | 4 ----
 .../app/features/createdirect/CreateDirectRoomActivity.kt   | 3 ---
 .../features/crypto/quads/SharedSecureStorageActivity.kt    | 3 ---
 .../vector/app/features/invite/InviteUsersToRoomActivity.kt | 3 ---
 .../java/im/vector/app/features/link/LinkHandlerActivity.kt | 2 --
 .../roomprofile/settings/joinrule/RoomJoinRuleActivity.kt   | 5 -----
 .../vector/app/features/signout/soft/SoftLogoutActivity.kt  | 2 --
 .../app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt | 6 +-----
 .../im/vector/app/features/terms/ReviewTermsActivity.kt     | 4 ----
 12 files changed, 3 insertions(+), 39 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
index 413249e79c..4e5116eda9 100644
--- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt
@@ -56,6 +56,7 @@ import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.core.di.ActivityEntryPoint
 import im.vector.app.core.dialogs.DialogLocker
 import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
+import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.error.fatalError
 import im.vector.app.core.extensions.observeEvent
 import im.vector.app.core.extensions.observeNotNull
@@ -161,11 +162,11 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
     @Inject lateinit var navigator: Navigator
     @Inject lateinit var activeSessionHolder: ActiveSessionHolder
     @Inject lateinit var vectorPreferences: VectorPreferences
+    @Inject lateinit var errorFormatter: ErrorFormatter
 
     // For debug only
     @Inject lateinit var debugReceiver: DebugReceiver
 
-
     // Filter for multiple invalid token error
     private var mainActivityStarted = false
 
diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt
index 1040727722..c197cfccf3 100644
--- a/vector/src/main/java/im/vector/app/features/MainActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt
@@ -30,8 +30,6 @@ import com.bumptech.glide.Glide
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.startSyncing
 import im.vector.app.core.extensions.vectorStore
 import im.vector.app.core.platform.VectorBaseActivity
@@ -42,13 +40,11 @@ import im.vector.app.features.analytics.plan.ViewRoom
 import im.vector.app.features.home.HomeActivity
 import im.vector.app.features.home.ShortcutsHandler
 import im.vector.app.features.notifications.NotificationDrawerManager
-import im.vector.app.features.pin.PinLocker
 import im.vector.app.features.pin.UnlockedActivity
 import im.vector.app.features.pin.lockscreen.crypto.LockScreenKeyRepository
 import im.vector.app.features.pin.lockscreen.pincode.PinCodeHelper
 import im.vector.app.features.popup.PopupAlertManager
 import im.vector.app.features.session.VectorSessionStore
-import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.signout.hard.SignedOutActivity
 import im.vector.app.features.start.StartAppAction
 import im.vector.app.features.start.StartAppAndroidService
@@ -131,7 +127,6 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
     private lateinit var args: MainActivityArgs
 
     @Inject lateinit var notificationDrawerManager: NotificationDrawerManager
-    @Inject lateinit var errorFormatter: ErrorFormatter
     @Inject lateinit var uiStateRepository: UiStateRepository
     @Inject lateinit var shortcutsHandler: ShortcutsHandler
     @Inject lateinit var pinCodeHelper: PinCodeHelper
diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/PstnDialActivity.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/PstnDialActivity.kt
index 251195ddc4..1a7571598e 100644
--- a/vector/src/main/java/im/vector/app/features/call/dialpad/PstnDialActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/dialpad/PstnDialActivity.kt
@@ -23,7 +23,6 @@ import androidx.lifecycle.lifecycleScope
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 import im.vector.app.features.call.webrtc.WebRtcCallManager
@@ -39,7 +38,6 @@ class PstnDialActivity : SimpleFragmentActivity() {
     @Inject lateinit var callManager: WebRtcCallManager
     @Inject lateinit var directRoomHelper: DirectRoomHelper
     @Inject lateinit var session: Session
-    @Inject lateinit var errorFormatter: ErrorFormatter
 
     private var progress: AppCompatDialog? = null
 
diff --git a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt
index 25bfb6c0e9..ae168c5f92 100644
--- a/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/call/transfer/CallTransferActivity.kt
@@ -25,12 +25,10 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.tabs.TabLayoutMediator
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivityCallTransferBinding
 import im.vector.lib.core.utils.compat.getParcelableCompat
 import kotlinx.parcelize.Parcelize
-import javax.inject.Inject
 
 @Parcelize
 data class CallTransferArgs(val callId: String) : Parcelable
@@ -40,8 +38,6 @@ private const val USER_LIST_FRAGMENT_TAG = "USER_LIST_FRAGMENT_TAG"
 @AndroidEntryPoint
 class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
 
-    @Inject lateinit var errorFormatter: ErrorFormatter
-
     private lateinit var sectionsPagerAdapter: CallTransferPagerAdapter
 
     private val callTransferViewModel: CallTransferViewModel by viewModel()
diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt
index fd28235a51..acaf24dca7 100644
--- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt
@@ -32,7 +32,6 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
@@ -58,7 +57,6 @@ import kotlinx.coroutines.flow.onEach
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
 import java.net.HttpURLConnection
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class CreateDirectRoomActivity : SimpleFragmentActivity() {
@@ -67,7 +65,6 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
     private val qrViewModel: QrCodeScannerViewModel by viewModel()
 
     private lateinit var sharedActionViewModel: UserListSharedActionViewModel
-    @Inject lateinit var errorFormatter: ErrorFormatter
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt
index b39992256d..d393636a8e 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageActivity.kt
@@ -30,13 +30,11 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
 import im.vector.app.features.crypto.recover.SetupMode
 import kotlinx.parcelize.Parcelize
-import javax.inject.Inject
 import kotlin.reflect.KClass
 
 @AndroidEntryPoint
@@ -54,7 +52,6 @@ class SharedSecureStorageActivity :
     ) : Parcelable
 
     private val viewModel: SharedSecureStorageViewModel by viewModel()
-    @Inject lateinit var errorFormatter: ErrorFormatter
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt
index 86f061849b..7f514d2ad2 100644
--- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomActivity.kt
@@ -27,7 +27,6 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.addFragmentToBackstack
 import im.vector.app.core.platform.SimpleFragmentActivity
@@ -47,7 +46,6 @@ import kotlinx.coroutines.flow.onEach
 import kotlinx.parcelize.Parcelize
 import org.matrix.android.sdk.api.failure.Failure
 import java.net.HttpURLConnection
-import javax.inject.Inject
 
 @Parcelize
 data class InviteUsersToRoomArgs(val roomId: String) : Parcelable
@@ -57,7 +55,6 @@ class InviteUsersToRoomActivity : SimpleFragmentActivity() {
 
     private val viewModel: InviteUsersToRoomViewModel by viewModel()
     private lateinit var sharedActionViewModel: UserListSharedActionViewModel
-    @Inject lateinit var errorFormatter: ErrorFormatter
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
diff --git a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
index 2526dc6ed7..1ca67e1fb7 100644
--- a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
@@ -24,7 +24,6 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.registerStartForActivityResult
 import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.core.utils.toast
@@ -45,7 +44,6 @@ import javax.inject.Inject
 @AndroidEntryPoint
 class LinkHandlerActivity : VectorBaseActivity<ActivityProgressBinding>() {
 
-    @Inject lateinit var errorFormatter: ErrorFormatter
     @Inject lateinit var permalinkHandler: PermalinkHandler
 
     private val startAppViewModel: StartAppViewModel by viewModel()
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt
index 205fb86377..818300ac72 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleActivity.kt
@@ -29,7 +29,6 @@ import com.airbnb.mvrx.viewModel
 import com.airbnb.mvrx.withState
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.addFragment
 import im.vector.app.core.extensions.commitTransaction
 import im.vector.app.core.extensions.toMvRxBundle
@@ -44,7 +43,6 @@ import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRul
 import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedState
 import im.vector.app.features.roomprofile.settings.joinrule.advanced.RoomJoinRuleChooseRestrictedViewModel
 import im.vector.lib.core.utils.compat.getParcelableCompat
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class RoomJoinRuleActivity : VectorBaseActivity<ActivitySimpleBinding>() {
@@ -53,9 +51,6 @@ class RoomJoinRuleActivity : VectorBaseActivity<ActivitySimpleBinding>() {
 
     private lateinit var roomProfileArgs: RoomProfileArgs
 
-    @Inject
-    lateinit var errorFormatter: ErrorFormatter
-
     val viewModel: RoomJoinRuleChooseRestrictedViewModel by viewModel()
 
     override fun initUiAndData() {
diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt
index 729bd2541e..15a86c869b 100644
--- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt
@@ -25,7 +25,6 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.features.MainActivity
 import im.vector.app.features.MainActivityArgs
@@ -45,7 +44,6 @@ class SoftLogoutActivity : LoginActivity() {
     private val softLogoutViewModel: SoftLogoutViewModel by viewModel()
 
     @Inject lateinit var session: Session
-    @Inject lateinit var errorFormatter: ErrorFormatter
 
     override fun initUiAndData() {
         super.initUiAndData()
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
index 80959c61a1..6f9aef401d 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
@@ -29,7 +29,6 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.hideKeyboard
 import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.extensions.setTextOrHide
@@ -37,16 +36,13 @@ import im.vector.app.core.platform.VectorBaseActivity
 import im.vector.app.databinding.ActivitySimpleLoadingBinding
 import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs
 import im.vector.lib.core.utils.compat.getParcelableExtraCompat
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class SpaceLeaveAdvancedActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() {
 
     override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater)
 
-    val leaveViewModel: SpaceLeaveAdvancedViewModel by viewModel()
-
-    @Inject lateinit var errorFormatter: ErrorFormatter
+    private val leaveViewModel: SpaceLeaveAdvancedViewModel by viewModel()
 
     override fun showWaitingView(text: String?) {
         hideKeyboard()
diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt
index d261a8d160..6e34113012 100644
--- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsActivity.kt
@@ -22,18 +22,14 @@ import com.airbnb.mvrx.viewModel
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.R
-import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 import im.vector.lib.core.utils.compat.getParcelableExtraCompat
 import org.matrix.android.sdk.api.session.terms.TermsService
-import javax.inject.Inject
 
 @AndroidEntryPoint
 class ReviewTermsActivity : SimpleFragmentActivity() {
 
-    @Inject lateinit var errorFormatter: ErrorFormatter
-
     private val reviewTermsViewModel: ReviewTermsViewModel by viewModel()
 
     override fun initUiAndData() {

From aa806ed2c61a7fd8cf4dcac36921cb61615d6730 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 23 Sep 2022 16:25:48 +0200
Subject: [PATCH 227/400] More cleanup on @Inject members

---
 .../vector/app/features/debug/DebugMenuActivity.kt  |  3 +--
 .../features/home/room/threads/ThreadsActivity.kt   |  3 +--
 .../media/VectorAttachmentViewerActivity.kt         | 13 ++++---------
 .../app/features/roomprofile/RoomProfileActivity.kt |  3 +--
 4 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
index e74caac299..f431192efd 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt
@@ -58,8 +58,7 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
 
     override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater)
 
-    @Inject
-    lateinit var clock: Clock
+    @Inject lateinit var clock: Clock
 
     private lateinit var buffer: ByteArray
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt
index 0ee0beb2e6..b3f2ef1f75 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/threads/ThreadsActivity.kt
@@ -40,8 +40,7 @@ import javax.inject.Inject
 @AndroidEntryPoint
 class ThreadsActivity : VectorBaseActivity<ActivityThreadsBinding>() {
 
-    @Inject
-    lateinit var avatarRenderer: AvatarRenderer
+    @Inject lateinit var avatarRenderer: AvatarRenderer
 
 //    private val roomThreadDetailFragment: RoomThreadDetailFragment?
 //        get() {
diff --git a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
index d523d99d76..089fdcebd4 100644
--- a/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VectorAttachmentViewerActivity.kt
@@ -69,14 +69,9 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
             val sharedTransitionName: String?
     ) : Parcelable
 
-    @Inject
-    lateinit var sessionHolder: ActiveSessionHolder
-
-    @Inject
-    lateinit var dataSourceFactory: AttachmentProviderFactory
-
-    @Inject
-    lateinit var imageContentRenderer: ImageContentRenderer
+    @Inject lateinit var activeSessionHolder: ActiveSessionHolder
+    @Inject lateinit var dataSourceFactory: AttachmentProviderFactory
+    @Inject lateinit var imageContentRenderer: ImageContentRenderer
 
     private val viewModel: VectorAttachmentViewerViewModel by viewModel()
     private val errorFormatter by lazy(LazyThreadSafetyMode.NONE) { singletonEntryPoint().errorFormatter() }
@@ -128,7 +123,7 @@ class VectorAttachmentViewerActivity : AttachmentViewerActivity(), AttachmentInt
             }
         }
 
-        val session = sessionHolder.getSafeActiveSession() ?: return Unit.also { finish() }
+        val session = activeSessionHolder.getSafeActiveSession() ?: return Unit.also { finish() }
 
         val room = args.roomId?.let { session.getRoom(it) }
 
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
index 2bb3e26384..526d676dee 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt
@@ -69,8 +69,7 @@ class RoomProfileActivity :
 
     private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel()
 
-    @Inject
-    lateinit var roomDetailPendingActionStore: RoomDetailPendingActionStore
+    @Inject lateinit var roomDetailPendingActionStore: RoomDetailPendingActionStore
 
     override fun getBinding(): ActivitySimpleBinding {
         return ActivitySimpleBinding.inflate(layoutInflater)

From cfca776d01954dc248f547b41bbd6b0400beb34c Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 23 Sep 2022 16:44:39 +0200
Subject: [PATCH 228/400] Create PermalinkUseCase to avoid injecting the
 Session in the View.

---
 .../app/features/home/HomeDrawerFragment.kt   |  4 +-
 .../features/home/NewHomeDetailFragment.kt    |  2 -
 .../home/room/detail/TimelineFragment.kt      |  8 ++--
 .../features/permalink/PermalinkUseCase.kt    | 39 +++++++++++++++++++
 .../roomdirectory/PublicRoomsFragment.kt      |  6 +--
 5 files changed, 50 insertions(+), 9 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/permalink/PermalinkUseCase.kt

diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
index 106fbc7281..b75d5c2e99 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
@@ -32,6 +32,7 @@ import im.vector.app.core.resources.BuildMeta
 import im.vector.app.core.utils.startSharePlainTextIntent
 import im.vector.app.databinding.FragmentHomeDrawerBinding
 import im.vector.app.features.analytics.plan.MobileScreen
+import im.vector.app.features.permalink.PermalinkUseCase
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.VectorSettingsActivity
 import im.vector.app.features.spaces.SpaceListFragment
@@ -49,6 +50,7 @@ class HomeDrawerFragment :
     @Inject lateinit var vectorPreferences: VectorPreferences
     @Inject lateinit var avatarRenderer: AvatarRenderer
     @Inject lateinit var buildMeta: BuildMeta
+    @Inject lateinit var permalinkUseCase: PermalinkUseCase
 
     private lateinit var sharedActionViewModel: HomeSharedActionViewModel
 
@@ -101,7 +103,7 @@ class HomeDrawerFragment :
         }
 
         views.homeDrawerInviteFriendButton.debouncedClicks {
-            session.permalinkService().createPermalink(sharedActionViewModel.session.myUserId)?.let { permalink ->
+            permalinkUseCase.createPermalinkOfCurrentUser()?.let { permalink ->
                 analyticsTracker.screen(MobileScreen(screenName = MobileScreen.ScreenName.InviteFriends))
                 val text = getString(R.string.invite_friends_text, permalink)
 
diff --git a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
index 66bb9ef876..5956646eab 100644
--- a/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/NewHomeDetailFragment.kt
@@ -61,7 +61,6 @@ import im.vector.app.features.workers.signout.ServerBackupStatusAction
 import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import javax.inject.Inject
@@ -80,7 +79,6 @@ class NewHomeDetailFragment :
     @Inject lateinit var callManager: WebRtcCallManager
     @Inject lateinit var vectorPreferences: VectorPreferences
     @Inject lateinit var spaceStateHandler: SpaceStateHandler
-    @Inject lateinit var session: Session
     @Inject lateinit var buildMeta: BuildMeta
 
     private val viewModel: HomeDetailViewModel by fragmentViewModel()
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index ad8b2f28f3..b17cefa6b7 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -169,6 +169,7 @@ import im.vector.app.features.notifications.NotificationDrawerManager
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.permalink.NavigationInterceptor
 import im.vector.app.features.permalink.PermalinkHandler
+import im.vector.app.features.permalink.PermalinkUseCase
 import im.vector.app.features.poll.PollMode
 import im.vector.app.features.reactions.EmojiReactionPickerActivity
 import im.vector.app.features.roomprofile.RoomProfileActivity
@@ -247,6 +248,7 @@ class TimelineFragment :
     @Inject lateinit var clock: Clock
     @Inject lateinit var vectorFeatures: VectorFeatures
     @Inject lateinit var galleryOrCameraDialogHelperFactory: GalleryOrCameraDialogHelperFactory
+    @Inject lateinit var permalinkUseCase: PermalinkUseCase
 
     companion object {
         const val MAX_TYPING_MESSAGE_USERS_COUNT = 4
@@ -867,7 +869,7 @@ class TimelineFragment :
             }
             R.id.menu_thread_timeline_copy_link -> {
                 getRootThreadEventId()?.let {
-                    val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, it)
+                    val permalink = permalinkUseCase.createPermalink(timelineArgs.roomId, it)
                     copyToClipboard(requireContext(), permalink, false)
                     showSnackWithMessage(getString(R.string.copied_to_clipboard))
                 }
@@ -879,7 +881,7 @@ class TimelineFragment :
             }
             R.id.menu_thread_timeline_share -> {
                 getRootThreadEventId()?.let {
-                    val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, it)
+                    val permalink = permalinkUseCase.createPermalink(timelineArgs.roomId, it)
                     shareText(requireContext(), permalink)
                 }
                 true
@@ -1788,7 +1790,7 @@ class TimelineFragment :
                 }
             }
             is EventSharedAction.CopyPermalink -> {
-                val permalink = session.permalinkService().createPermalink(timelineArgs.roomId, action.eventId)
+                val permalink = permalinkUseCase.createPermalink(timelineArgs.roomId, action.eventId)
                 copyToClipboard(requireContext(), permalink, false)
                 showSnackWithMessage(getString(R.string.copied_to_clipboard))
             }
diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkUseCase.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkUseCase.kt
new file mode 100644
index 0000000000..d9c629d5d0
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkUseCase.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.permalink
+
+import org.matrix.android.sdk.api.session.Session
+import javax.inject.Inject
+
+/**
+ * Contains synchronous methods to create permalinks from the Session.
+ */
+class PermalinkUseCase @Inject constructor(
+        private val session: Session,
+) {
+    fun createPermalinkOfCurrentUser(): String? {
+        return createPermalink(session.myUserId)
+    }
+
+    fun createPermalink(id: String): String? {
+        return session.permalinkService().createPermalink(id)
+    }
+
+    fun createPermalink(roomId: String, eventId: String): String {
+        return session.permalinkService().createPermalink(roomId, eventId)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
index 7b5cc20910..89ee7434df 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
@@ -38,11 +38,11 @@ import im.vector.app.databinding.FragmentPublicRoomsBinding
 import im.vector.app.features.analytics.plan.ViewRoom
 import im.vector.app.features.permalink.NavigationInterceptor
 import im.vector.app.features.permalink.PermalinkHandler
+import im.vector.app.features.permalink.PermalinkUseCase
 import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
 import reactivecircus.flowbinding.appcompat.queryTextChanges
 import timber.log.Timber
@@ -60,7 +60,7 @@ class PublicRoomsFragment :
 
     @Inject lateinit var publicRoomsController: PublicRoomsController
     @Inject lateinit var permalinkHandler: PermalinkHandler
-    @Inject lateinit var session: Session
+    @Inject lateinit var permalinkUseCase: PermalinkUseCase
 
     private val viewModel: RoomDirectoryViewModel by activityViewModel()
     private lateinit var sharedActionViewModel: RoomDirectorySharedActionViewModel
@@ -128,7 +128,7 @@ class PublicRoomsFragment :
 
     override fun onUnknownRoomClicked(roomIdOrAlias: String) {
         viewLifecycleOwner.lifecycleScope.launch {
-            val permalink = session.permalinkService().createPermalink(roomIdOrAlias)
+            val permalink = permalinkUseCase.createPermalink(roomIdOrAlias)
             val isHandled = permalinkHandler
                     .launch(requireActivity(), permalink, object : NavigationInterceptor {
                         override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean {

From 37f34dbdfd63434c9912e3dcc8f4ba2ea9de9391 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 26 Sep 2022 16:41:19 +0200
Subject: [PATCH 229/400] Expect the userId from the fakeSession

---
 .../vector/app/features/onboarding/OnboardingViewModelTest.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
index 865f1c79d6..718f1ec7a9 100644
--- a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
+++ b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt
@@ -687,7 +687,7 @@ class OnboardingViewModelTest {
                 .assertStatesChanges(
                         initialState,
                         { copy(isLoading = true) },
-                        { copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState(A_MATRIX_ID, A_USERNAME)) }
+                        { copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState("@fake:server.fake", A_USERNAME)) }
                 )
                 .assertEvents(OnboardingViewEvents.OnAccountCreated)
                 .finish()

From 3bc3da1073b1c592755fa8af9365c470ca2ead33 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 4 Oct 2022 16:49:03 +0200
Subject: [PATCH 230/400] Rename `PermalinkUseCase` to `PermalinkFactory`

---
 .../im/vector/app/features/home/HomeDrawerFragment.kt  |  6 +++---
 .../app/features/home/room/detail/TimelineFragment.kt  | 10 +++++-----
 .../{PermalinkUseCase.kt => PermalinkFactory.kt}       |  2 +-
 .../app/features/roomdirectory/PublicRoomsFragment.kt  |  6 +++---
 4 files changed, 12 insertions(+), 12 deletions(-)
 rename vector/src/main/java/im/vector/app/features/permalink/{PermalinkUseCase.kt => PermalinkFactory.kt} (96%)

diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
index b75d5c2e99..22d9709229 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt
@@ -32,7 +32,7 @@ import im.vector.app.core.resources.BuildMeta
 import im.vector.app.core.utils.startSharePlainTextIntent
 import im.vector.app.databinding.FragmentHomeDrawerBinding
 import im.vector.app.features.analytics.plan.MobileScreen
-import im.vector.app.features.permalink.PermalinkUseCase
+import im.vector.app.features.permalink.PermalinkFactory
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.settings.VectorSettingsActivity
 import im.vector.app.features.spaces.SpaceListFragment
@@ -50,7 +50,7 @@ class HomeDrawerFragment :
     @Inject lateinit var vectorPreferences: VectorPreferences
     @Inject lateinit var avatarRenderer: AvatarRenderer
     @Inject lateinit var buildMeta: BuildMeta
-    @Inject lateinit var permalinkUseCase: PermalinkUseCase
+    @Inject lateinit var permalinkFactory: PermalinkFactory
 
     private lateinit var sharedActionViewModel: HomeSharedActionViewModel
 
@@ -103,7 +103,7 @@ class HomeDrawerFragment :
         }
 
         views.homeDrawerInviteFriendButton.debouncedClicks {
-            permalinkUseCase.createPermalinkOfCurrentUser()?.let { permalink ->
+            permalinkFactory.createPermalinkOfCurrentUser()?.let { permalink ->
                 analyticsTracker.screen(MobileScreen(screenName = MobileScreen.ScreenName.InviteFriends))
                 val text = getString(R.string.invite_friends_text, permalink)
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index b17cefa6b7..59af00fac3 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -169,7 +169,7 @@ import im.vector.app.features.notifications.NotificationDrawerManager
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.permalink.NavigationInterceptor
 import im.vector.app.features.permalink.PermalinkHandler
-import im.vector.app.features.permalink.PermalinkUseCase
+import im.vector.app.features.permalink.PermalinkFactory
 import im.vector.app.features.poll.PollMode
 import im.vector.app.features.reactions.EmojiReactionPickerActivity
 import im.vector.app.features.roomprofile.RoomProfileActivity
@@ -248,7 +248,7 @@ class TimelineFragment :
     @Inject lateinit var clock: Clock
     @Inject lateinit var vectorFeatures: VectorFeatures
     @Inject lateinit var galleryOrCameraDialogHelperFactory: GalleryOrCameraDialogHelperFactory
-    @Inject lateinit var permalinkUseCase: PermalinkUseCase
+    @Inject lateinit var permalinkFactory: PermalinkFactory
 
     companion object {
         const val MAX_TYPING_MESSAGE_USERS_COUNT = 4
@@ -869,7 +869,7 @@ class TimelineFragment :
             }
             R.id.menu_thread_timeline_copy_link -> {
                 getRootThreadEventId()?.let {
-                    val permalink = permalinkUseCase.createPermalink(timelineArgs.roomId, it)
+                    val permalink = permalinkFactory.createPermalink(timelineArgs.roomId, it)
                     copyToClipboard(requireContext(), permalink, false)
                     showSnackWithMessage(getString(R.string.copied_to_clipboard))
                 }
@@ -881,7 +881,7 @@ class TimelineFragment :
             }
             R.id.menu_thread_timeline_share -> {
                 getRootThreadEventId()?.let {
-                    val permalink = permalinkUseCase.createPermalink(timelineArgs.roomId, it)
+                    val permalink = permalinkFactory.createPermalink(timelineArgs.roomId, it)
                     shareText(requireContext(), permalink)
                 }
                 true
@@ -1790,7 +1790,7 @@ class TimelineFragment :
                 }
             }
             is EventSharedAction.CopyPermalink -> {
-                val permalink = permalinkUseCase.createPermalink(timelineArgs.roomId, action.eventId)
+                val permalink = permalinkFactory.createPermalink(timelineArgs.roomId, action.eventId)
                 copyToClipboard(requireContext(), permalink, false)
                 showSnackWithMessage(getString(R.string.copied_to_clipboard))
             }
diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkUseCase.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkFactory.kt
similarity index 96%
rename from vector/src/main/java/im/vector/app/features/permalink/PermalinkUseCase.kt
rename to vector/src/main/java/im/vector/app/features/permalink/PermalinkFactory.kt
index d9c629d5d0..378ffc4569 100644
--- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkFactory.kt
@@ -22,7 +22,7 @@ import javax.inject.Inject
 /**
  * Contains synchronous methods to create permalinks from the Session.
  */
-class PermalinkUseCase @Inject constructor(
+class PermalinkFactory @Inject constructor(
         private val session: Session,
 ) {
     fun createPermalinkOfCurrentUser(): String? {
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
index 89ee7434df..c7d799346b 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
@@ -38,7 +38,7 @@ import im.vector.app.databinding.FragmentPublicRoomsBinding
 import im.vector.app.features.analytics.plan.ViewRoom
 import im.vector.app.features.permalink.NavigationInterceptor
 import im.vector.app.features.permalink.PermalinkHandler
-import im.vector.app.features.permalink.PermalinkUseCase
+import im.vector.app.features.permalink.PermalinkFactory
 import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -60,7 +60,7 @@ class PublicRoomsFragment :
 
     @Inject lateinit var publicRoomsController: PublicRoomsController
     @Inject lateinit var permalinkHandler: PermalinkHandler
-    @Inject lateinit var permalinkUseCase: PermalinkUseCase
+    @Inject lateinit var permalinkFactory: PermalinkFactory
 
     private val viewModel: RoomDirectoryViewModel by activityViewModel()
     private lateinit var sharedActionViewModel: RoomDirectorySharedActionViewModel
@@ -128,7 +128,7 @@ class PublicRoomsFragment :
 
     override fun onUnknownRoomClicked(roomIdOrAlias: String) {
         viewLifecycleOwner.lifecycleScope.launch {
-            val permalink = permalinkUseCase.createPermalink(roomIdOrAlias)
+            val permalink = permalinkFactory.createPermalink(roomIdOrAlias)
             val isHandled = permalinkHandler
                     .launch(requireActivity(), permalink, object : NavigationInterceptor {
                         override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?, rootThreadEventId: String?): Boolean {

From 822f06fef4c6ca24d5e7c5f4f62273529c119610 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Fri, 7 Oct 2022 14:57:06 +0200
Subject: [PATCH 231/400] Fix ktlint issue

---
 .../im/vector/app/features/home/room/detail/TimelineFragment.kt | 2 +-
 .../im/vector/app/features/roomdirectory/PublicRoomsFragment.kt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index 59af00fac3..9d50cdb070 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -168,8 +168,8 @@ import im.vector.app.features.media.VideoContentRenderer
 import im.vector.app.features.notifications.NotificationDrawerManager
 import im.vector.app.features.notifications.NotificationUtils
 import im.vector.app.features.permalink.NavigationInterceptor
-import im.vector.app.features.permalink.PermalinkHandler
 import im.vector.app.features.permalink.PermalinkFactory
+import im.vector.app.features.permalink.PermalinkHandler
 import im.vector.app.features.poll.PollMode
 import im.vector.app.features.reactions.EmojiReactionPickerActivity
 import im.vector.app.features.roomprofile.RoomProfileActivity
diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
index c7d799346b..e541e7f879 100644
--- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt
@@ -37,8 +37,8 @@ import im.vector.app.core.utils.toast
 import im.vector.app.databinding.FragmentPublicRoomsBinding
 import im.vector.app.features.analytics.plan.ViewRoom
 import im.vector.app.features.permalink.NavigationInterceptor
-import im.vector.app.features.permalink.PermalinkHandler
 import im.vector.app.features.permalink.PermalinkFactory
+import im.vector.app.features.permalink.PermalinkHandler
 import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach

From fa1d2bd8ab9cef1015cd83afbb2b42c246f15e0c Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 17 Oct 2022 15:12:41 +0200
Subject: [PATCH 232/400] Fix compilation issue after rebase.

---
 .../im/vector/app/features/settings/VectorSettingsActivity.kt   | 2 --
 1 file changed, 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
index dcf8e7b3ae..4a9db49c67 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt
@@ -63,8 +63,6 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
 
     @Inject lateinit var session: Session
 
-    @Inject lateinit var vectorPreferences: VectorPreferences
-
     override fun initUiAndData() {
         setupToolbar(views.settingsToolbar)
                 .allowBack()

From 3864d937d93a89628e146c77c565959925f71b65 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Mon, 17 Oct 2022 15:52:55 +0200
Subject: [PATCH 233/400] Set warnWhenNotUsingParallelGC to false, because it
 fails the build.

---
 tools/gradle/doctor.gradle | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tools/gradle/doctor.gradle b/tools/gradle/doctor.gradle
index 705bb3e250..7a7adad062 100644
--- a/tools/gradle/doctor.gradle
+++ b/tools/gradle/doctor.gradle
@@ -54,7 +54,8 @@ doctor {
     /**
      * Warn when not using parallel GC. Parallel GC is faster for build type tasks and is no longer the default in Java 9+.
      */
-    warnWhenNotUsingParallelGC = true
+    // Note: Actually, if set to true, it fails the build. See https://lightrun.com/answers/runningcode-gradle-doctor-warnwhennotusingparallelgc-fails-the-build-warn-is-a-confusing-keyword-here
+    warnWhenNotUsingParallelGC = false
     /**
      * Throws an error when the `Delete` or `clean` task has dependencies.
      * If a clean task depends on other tasks, clean can be reordered and made to run after the tasks that would produce

From 3adc1268a9a161f528c8039e784400c970d5e412 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 17 Oct 2022 13:59:14 +0000
Subject: [PATCH 234/400] Bump stem-plugin from 2.2.2 to 2.2.3

Bumps stem-plugin from 2.2.2 to 2.2.3.

---
updated-dependencies:
- dependency-name: com.likethesalad.android:stem-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index aea22ee184..d0f093a451 100644
--- a/build.gradle
+++ b/build.gradle
@@ -28,7 +28,7 @@ buildscript {
         classpath 'com.google.gms:google-services:4.3.14'
         classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513'
         classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
-        classpath "com.likethesalad.android:stem-plugin:2.2.2"
+        classpath "com.likethesalad.android:stem-plugin:2.2.3"
         classpath 'org.owasp:dependency-check-gradle:7.2.1'
         classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20"
         classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"

From e01ee619d3bb59d5bec0c1d0ee1bef9829584f40 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:02:25 +0100
Subject: [PATCH 235/400] Refactor error handling and report E2EE errors

---
 .../src/main/res/values/strings.xml           |   6 +
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 140 ++++++++++--------
 .../sdk/api/rendezvous/RendezvousChannel.kt   |  12 +-
 .../api/rendezvous/RendezvousFailureReason.kt |  25 ++--
 .../sdk/api/rendezvous/RendezvousTransport.kt |   7 +-
 .../channels/ECDHRendezvousChannel.kt         |  27 +---
 .../sdk/api/rendezvous/model/Outcome.kt       |   8 +-
 .../api/rendezvous/model/RendezvousError.kt   |   2 +-
 .../SimpleHttpRendezvousTransport.kt          |  57 ++++---
 .../login/qr/QrCodeLoginStatusFragment.kt     |   6 +
 .../features/login/qr/QrCodeLoginViewModel.kt |  27 +++-
 11 files changed, 171 insertions(+), 146 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 7d0173f341..e28ebb31c6 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3383,6 +3383,12 @@
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
     <string name="qr_code_login_header_failed_other_description">The request failed.</string>
+    <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your intent connection(s); Your device(s);</string>
+    <string name="qr_code_login_header_failed_other_device_already_signed_in_description">The other device is already signed in.</string>
+    <string name="qr_code_login_header_failed_other_device_not_signed_in_description">The other device must be signed in.</string>
+    <string name="qr_code_login_header_failed_invalid_qr_code_description">The QR code scanned is invalid.</string>
+    <string name="qr_code_login_header_failed_user_cancelled_description">The sign in was cancelled on the other device.</string>
+    <string name="qr_code_login_header_failed_homeserver_is_not_supported_description">The homeserver doesn\'t support sign in with QR code.</string>
     <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
     <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
     <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code in this device\'</string>
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index e467ff06e3..9ad889fca0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.rendezvous.model.Outcome
 import org.matrix.android.sdk.api.rendezvous.model.Payload
 import org.matrix.android.sdk.api.rendezvous.model.PayloadType
 import org.matrix.android.sdk.api.rendezvous.model.Protocol
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport
 import org.matrix.android.sdk.api.session.Session
@@ -47,10 +48,16 @@ class Rendezvous(
     companion object {
         private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
-        fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous {
-            val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code")
+        @Throws(RendezvousError::class)
+        fun buildChannelFromCode(code: String): Rendezvous {
+            val parsed = try {
+                // we rely on moshi validating the code and throwing exception if invalid JSON or doesn't
+                MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code)
+            } catch (a: Throwable) {
+                throw RendezvousError("Invalid code", RendezvousFailureReason.InvalidCode)
+            } ?: throw RendezvousError("Invalid code", RendezvousFailureReason.InvalidCode)
 
-            val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri)
+            val transport = SimpleHttpRendezvousTransport(parsed.rendezvous.transport.uri)
 
             return Rendezvous(
                     ECDHRendezvousChannel(transport, parsed.rendezvous.key),
@@ -64,32 +71,30 @@ class Rendezvous(
     // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
     val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE
 
-    private suspend fun areIntentsIncompatible(): Boolean {
+    @Throws(RendezvousError::class)
+    private suspend fun checkCompatibility() {
         val incompatible = theirIntent == ourIntent
 
         Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible")
 
         if (incompatible) {
+            // inform the other side
             send(Payload(PayloadType.FINISH, intent = ourIntent))
-            val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
-                RendezvousFailureReason.OtherDeviceNotSignedIn
+            if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
+                throw RendezvousError("The other device isn't signed in", RendezvousFailureReason.OtherDeviceNotSignedIn)
             } else {
-                RendezvousFailureReason.OtherDeviceAlreadySignedIn
+                throw RendezvousError("The other device is already signed in", RendezvousFailureReason.OtherDeviceAlreadySignedIn)
             }
-            channel.cancel(reason)
         }
-
-        return incompatible
     }
 
+    @Throws(RendezvousError::class)
     suspend fun startAfterScanningCode(): String? {
         val checksum = channel.connect()
 
         Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
 
-        if (areIntentsIncompatible()) {
-            return null
-        }
+        checkCompatibility()
 
         // get protocols
         Timber.tag(TAG).i("Waiting for protocols")
@@ -97,9 +102,7 @@ class Rendezvous(
 
         if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) {
             send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED))
-            Timber.tag(TAG).i("No supported protocol")
-            cancel(RendezvousFailureReason.Unknown)
-            return null
+            throw RendezvousError("Unsupported protocols", RendezvousFailureReason.UnsupportedHomeserver)
         }
 
         send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN))
@@ -107,6 +110,7 @@ class Rendezvous(
         return checksum
     }
 
+    @Throws(RendezvousError::class)
     suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
         Timber.tag(TAG).i("Waiting for login_token")
 
@@ -115,24 +119,19 @@ class Rendezvous(
         if (loginToken?.type == PayloadType.FINISH) {
             when (loginToken.outcome) {
                 Outcome.DECLINED -> {
-                    Timber.tag(TAG).i("Login declined by other device")
-                    channel.cancel(RendezvousFailureReason.UserDeclined)
-                    return null
+                    throw RendezvousError("Login declined by other device", RendezvousFailureReason.UserDeclined)
                 }
                 Outcome.UNSUPPORTED -> {
-                    Timber.tag(TAG).i("Not supported")
-                    channel.cancel(RendezvousFailureReason.HomeserverLacksSupport)
-                    return null
+                    throw RendezvousError("Homeserver lacks support", RendezvousFailureReason.UnsupportedHomeserver)
                 }
                 else -> {
-                    channel.cancel(RendezvousFailureReason.Unknown)
-                    return null
+                    throw RendezvousError("Unknown error", RendezvousFailureReason.Unknown)
                 }
             }
         }
 
-        val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned")
-        val token = loginToken.loginToken ?: throw RuntimeException("No login token returned")
+        val homeserver = loginToken?.homeserver ?: throw RendezvousError("No homeserver returned", RendezvousFailureReason.ProtocolError)
+        val token = loginToken.loginToken ?: throw RendezvousError("No login token returned", RendezvousFailureReason.ProtocolError)
 
         Timber.tag(TAG).i("Got login_token now attempting to sign in with $homeserver")
 
@@ -140,6 +139,7 @@ class Rendezvous(
         return authenticationService.loginUsingQrLoginToken(hsConfig, token)
     }
 
+    @Throws(RendezvousError::class)
     suspend fun completeVerificationOnNewDevice(session: Session) {
         val userId = session.myUserId
         val crypto = session.cryptoService()
@@ -148,59 +148,77 @@ class Rendezvous(
         send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey))
 
         // await confirmation of verification
-
         val verificationResponse = receive()
-        val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned")
-        val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
-        if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
-            Timber.tag(TAG).w(
-                    "Verifying device $verifyingDeviceId key doesn't match: ${
-                        verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})"
-            )
-            throw RuntimeException("Key from verifying device doesn't match")
-        }
+        if (verificationResponse?.outcome == Outcome.VERIFIED) {
+            val verifyingDeviceId = verificationResponse.verifyingDeviceId
+                    ?: throw RendezvousError("No verifying device id returned", RendezvousFailureReason.ProtocolError)
+            val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId)
+            if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) {
+                Timber.tag(TAG).w(
+                        "Verifying device $verifyingDeviceId key doesn't match: ${
+                            verifyingDeviceFromServer?.fingerprint()
+                        } vs ${verificationResponse.verifyingDeviceKey})"
+                )
+                // inform the other side
+                send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
+                throw RendezvousError("Key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
+            }
 
-        // set other device as verified
-        Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
-        crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+            verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice ->
+                // check master key againt what the homeserver told us
+                crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
+                    if (localMasterKey.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
+                        Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                        // inform the other side
+                        send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
+                        throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
+                    }
+                    // set other device as verified
+                    Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
+                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
-        verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice ->
-            // set master key as trusted
-            crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
-                if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) {
                     Timber.tag(TAG).i("Setting master key as trusted")
                     crypto.crossSigningService().markMyMasterKeyAsTrusted()
-                } else {
-                    Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
-                    throw RuntimeException("Master key from verifying device doesn't match")
-                }
-            } ?: Timber.tag(TAG).i("No local master key")
-        } ?: Timber.tag(TAG).i("No master key given by verifying device")
+                } ?: Timber.tag(TAG).w("No local master key so not verifying")
+            } ?: run {
+                // set other device as verified anyway
+                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
+                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
 
-        // request secrets from the verifying device
-        Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+                Timber.tag(TAG).i("No master key given by verifying device")
+            }
 
-        session.sharedSecretStorageService().let {
-            it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
-            it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
-            it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
-            it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+            // request secrets from the verifying device
+            Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId")
+
+            session.sharedSecretStorageService().let {
+                it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId)
+                it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+                it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId)
+                it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId)
+            }
+        } else {
+            Timber.tag(TAG).i("Not doing verification")
         }
     }
 
+    @Throws(RendezvousError::class)
     private suspend fun receive(): Payload? {
         val data = channel.receive() ?: return null
-        return adapter.fromJson(data.toString(Charsets.UTF_8))
+        val payload = try {
+            adapter.fromJson(data.toString(Charsets.UTF_8))
+        } catch (e: Exception) {
+            Timber.tag(TAG).w(e, "Failed to parse payload")
+            throw RendezvousError("Invalid payload received", RendezvousFailureReason.Unknown)
+        }
+
+        return payload
     }
 
     private suspend fun send(payload: Payload) {
         channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8))
     }
 
-    suspend fun cancel(reason: RendezvousFailureReason) {
-        channel.cancel(reason)
-    }
-
     suspend fun close() {
         channel.close()
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 31014e392d..be79569164 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -16,8 +16,7 @@
 
 package org.matrix.android.sdk.api.rendezvous
 
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 
 /**
  * Representation of a rendezvous channel such as that described by MSC3903.
@@ -28,26 +27,25 @@ interface RendezvousChannel {
     /**
      * @returns the checksum/confirmation digits to be shown to the user
      */
+    @Throws(RendezvousError::class)
     suspend fun connect(): String
 
     /**
      * Send a payload via the channel.
      * @param data payload to send
      */
+    @Throws(RendezvousError::class)
     suspend fun send(data: ByteArray)
 
     /**
      * Receive a payload from the channel.
      * @returns the received payload
      */
+    @Throws(RendezvousError::class)
     suspend fun receive(): ByteArray?
 
     /**
-     * @returns a representation of the channel that can be encoded in a QR or similar
+     * @returns closes the channel and cleans up
      */
     suspend fun close()
-
-    // In future we probably want this to be a more generic RendezvousCode but it is suffice for now
-    suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode
-    suspend fun cancel(reason: RendezvousFailureReason)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
index a607dc7f38..18e625d825 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt
@@ -16,16 +16,17 @@
 
 package org.matrix.android.sdk.api.rendezvous
 
-enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) {
-    UserDeclined("user_declined"),
-    OtherDeviceNotSignedIn("other_device_not_signed_in"),
-    OtherDeviceAlreadySignedIn("other_device_already_signed_in"),
-    Unknown("unknown"),
-    Expired("expired"),
-    UserCancelled("user_cancelled"),
-    InvalidCode("invalid_code"),
-    UnsupportedAlgorithm("unsupported_algorithm", false),
-    DataMismatch("data_mismatch"),
-    UnsupportedTransport("unsupported_transport", false),
-    HomeserverLacksSupport("homeserver_lacks_support", false)
+enum class RendezvousFailureReason(val canRetry: Boolean = true) {
+    UserDeclined,
+    OtherDeviceNotSignedIn,
+    OtherDeviceAlreadySignedIn,
+    Unknown,
+    Expired,
+    UserCancelled,
+    InvalidCode,
+    UnsupportedAlgorithm(false),
+    UnsupportedTransport(false),
+    UnsupportedHomeserver(false),
+    ProtocolError,
+    E2EESecurityIssue(false)
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index de0aed7efc..5daf906930 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -17,13 +17,16 @@
 package org.matrix.android.sdk.api.rendezvous
 
 import okhttp3.MediaType
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
     var ready: Boolean
-    var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?
+    @Throws(RendezvousError::class)
     suspend fun details(): RendezvousTransportDetails
+    @Throws(RendezvousError::class)
     suspend fun send(contentType: MediaType, data: ByteArray)
+    @Throws(RendezvousError::class)
     suspend fun receive(): ByteArray?
-    suspend fun cancel(reason: RendezvousFailureReason)
+    suspend fun close()
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index ca7083b297..9a5c5e865a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -89,13 +89,14 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP)
     }
 
+    @Throws(RendezvousError::class)
     override suspend fun connect(): String {
         olmSAS ?.let { olmSAS ->
             val isInitiator = theirPublicKey == null
 
             if (isInitiator) {
                 Timber.tag(TAG).i("Waiting for other device to send their public key")
-                val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device")
+                val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError)
 
                 if (res.key == null) {
                     throw RendezvousError(
@@ -137,7 +138,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     override suspend fun send(data: ByteArray) {
         if (aesKey == null) {
-            throw RuntimeException("Shared secret not established")
+            throw IllegalStateException("Shared secret not established")
         }
         send(encrypt(data))
     }
@@ -150,31 +151,12 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     override suspend fun receive(): ByteArray? {
         if (aesKey == null) {
-            throw RuntimeException("Shared secret not established")
+            throw IllegalStateException("Shared secret not established")
         }
         val payload = receiveAsPayload() ?: return null
         return decrypt(payload)
     }
 
-    override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode {
-        return ECDHRendezvousCode(
-                intent,
-                rendezvous = ECDHRendezvous(
-                        transport.details() as SimpleHttpRendezvousTransportDetails,
-                        SecureRendezvousChannelAlgorithm.ECDH_V1,
-                        key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-                )
-        )
-    }
-
-    override suspend fun cancel(reason: RendezvousFailureReason) {
-        try {
-            transport.cancel(reason)
-        } finally {
-            close()
-        }
-    }
-
     override suspend fun close() {
         olmSAS ?.let {
             synchronized(it) {
@@ -183,6 +165,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
                 olmSAS = null
             }
         }
+        transport.close()
     }
 
     private fun encrypt(plainText: ByteArray): ECDHPayload {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
index 2dd6e7be28..2ef24e9cb7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -26,5 +26,11 @@ enum class Outcome(val value: String) {
     DECLINED("declined"),
 
     @Json(name = "unsupported")
-    UNSUPPORTED("unsupported")
+    UNSUPPORTED("unsupported"),
+
+    @Json(name = "verified")
+    VERIFIED("verified"),
+
+    @Json(name = "e2ee_security_error")
+    E2EE_SECURITY_ERROR("e2ee_security_error")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
index fec55ffb67..c52b11a322 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt
@@ -18,4 +18,4 @@ package org.matrix.android.sdk.api.rendezvous.model
 
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 
-class RendezvousError(val description: String, val reason: RendezvousFailureReason) : RuntimeException(description)
+class RendezvousError(val description: String, val reason: RendezvousFailureReason) : Exception(description)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index ca2a3425cd..fa0e5d8e2a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -23,6 +23,7 @@ import okhttp3.RequestBody.Companion.toRequestBody
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import timber.log.Timber
@@ -32,7 +33,7 @@ import java.util.Date
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
  */
-class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport {
+class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTransport {
     companion object {
         private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value
     }
@@ -55,7 +56,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
 
     override suspend fun send(contentType: MediaType, data: ByteArray) {
         if (cancelled) {
-            return
+            throw IllegalStateException("Rendezvous cancelled")
         }
 
         val method = if (uri != null) "PUT" else "POST"
@@ -75,9 +76,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         val response = httpClient.newCall(request.build()).execute()
 
         if (response.code == 404) {
-            // we set to unknown and the cancel method will rewrite the reason to expired if applicable
-            cancel(RendezvousFailureReason.Unknown)
-            return
+            throw get404Error()
         }
         etag = response.header("etag")
 
@@ -98,12 +97,12 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
     }
 
     override suspend fun receive(): ByteArray? {
+        if (cancelled) {
+            throw IllegalStateException("Rendezvous cancelled")
+        }
         val uri = uri ?: throw IllegalStateException("Rendezvous not set up")
         val httpClient = okhttp3.OkHttpClient.Builder().build()
         while (true) {
-            if (cancelled) {
-                return null
-            }
             Timber.tag(TAG).i("Polling: $uri after etag $etag")
             val request = Request.Builder()
                     .url(uri)
@@ -118,9 +117,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
             try {
                 // expired
                 if (response.code == 404) {
-                    // we set to unknown and the cancel method will rewrite the reason to expired if applicable
-                    cancel(RendezvousFailureReason.Unknown)
-                    return null
+                    throw get404Error()
                 }
 
                 // rely on server expiring the channel rather than checking ourselves
@@ -145,31 +142,27 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo
         }
     }
 
-    override suspend fun cancel(reason: RendezvousFailureReason) {
-        var mappedReason = reason
-        Timber.tag(TAG).i("$expiresAt")
-        if (mappedReason == RendezvousFailureReason.Unknown &&
-                expiresAt != null && Date() > expiresAt
-        ) {
-            mappedReason = RendezvousFailureReason.Expired
-        }
+    private fun get404Error(): RendezvousError {
+        return if (expiresAt != null && Date() > expiresAt)
+            RendezvousError("Expired", RendezvousFailureReason.Expired)
+        else
+            RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown)
+    }
 
+    override suspend fun close() {
         cancelled = true
         ready = false
-        onCancelled ?.let { it(mappedReason) }
 
-        if (mappedReason == RendezvousFailureReason.UserDeclined) {
-            uri ?.let {
-                try {
-                    val httpClient = okhttp3.OkHttpClient.Builder().build()
-                    val request = Request.Builder()
-                            .url(it)
-                            .delete()
-                            .build()
-                    httpClient.newCall(request).execute()
-                } catch (e: Exception) {
-                    Timber.tag(TAG).w(e, "Failed to delete channel")
-                }
+        uri ?.let {
+            try {
+                val httpClient = okhttp3.OkHttpClient.Builder().build()
+                val request = Request.Builder()
+                        .url(it)
+                        .delete()
+                        .build()
+                httpClient.newCall(request).execute()
+            } catch (e: Throwable) {
+                Timber.tag(TAG).w(e, "Failed to delete channel")
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 0691e2367e..617d620c27 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -70,8 +70,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         return when (reason) {
             RendezvousFailureReason.UnsupportedAlgorithm,
             RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)
+            RendezvousFailureReason.UnsupportedHomeserver -> getString(R.string.qr_code_login_header_failed_homeserver_is_not_supported_description)
             RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description)
             RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description)
+            RendezvousFailureReason.E2EESecurityIssue -> getString(R.string.qr_code_login_header_failed_e2ee_security_issue_description)
+            RendezvousFailureReason.OtherDeviceAlreadySignedIn -> getString(R.string.qr_code_login_header_failed_other_device_already_signed_in_description)
+            RendezvousFailureReason.OtherDeviceNotSignedIn -> getString(R.string.qr_code_login_header_failed_other_device_not_signed_in_description)
+            RendezvousFailureReason.InvalidCode -> getString(R.string.qr_code_login_header_failed_invalid_qr_code_description)
+            RendezvousFailureReason.UserCancelled -> getString(R.string.qr_code_login_header_failed_user_cancelled_description)
             else -> getString(R.string.qr_code_login_header_failed_other_description)
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 9d4e5e9870..7f95fad485 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -30,6 +30,7 @@ import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.auth.AuthenticationService
 import org.matrix.android.sdk.api.rendezvous.Rendezvous
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
+import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import timber.log.Timber
 
 class QrCodeLoginViewModel @AssistedInject constructor(
@@ -38,14 +39,15 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         private val activeSessionHolder: ActiveSessionHolder,
         private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
-    val TAG: String = QrCodeLoginViewModel::class.java.simpleName
 
     @AssistedFactory
     interface Factory : MavericksAssistedViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> {
         override fun create(initialState: QrCodeLoginViewState): QrCodeLoginViewModel
     }
 
-    companion object : MavericksViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> by hiltMavericksViewModelFactory()
+    companion object : MavericksViewModelFactory<QrCodeLoginViewModel, QrCodeLoginViewState> by hiltMavericksViewModelFactory() {
+        val TAG: String = QrCodeLoginViewModel::class.java.simpleName
+    }
 
     override fun handle(action: QrCodeLoginAction) {
         when (action) {
@@ -71,9 +73,14 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
         Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
 
-        val rendezvous = Rendezvous.buildChannelFromCode(action.qrCode) { reason ->
-            Timber.tag(TAG).d("Rendezvous cancelled: $reason")
-            onFailed(reason)
+        val rendezvous = try { Rendezvous.buildChannelFromCode(action.qrCode) } catch (t: Throwable) {
+            Timber.tag(TAG).e(t, "Error occurred during sign in")
+            if (t is RendezvousError) {
+                onFailed(t.reason)
+            } else {
+                onFailed(RendezvousFailureReason.Unknown)
+            }
+            return
         }
 
         setState {
@@ -103,9 +110,13 @@ class QrCodeLoginViewModel @AssistedInject constructor(
                         _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
                     }
                 }
-            } catch (failure: Throwable) {
-                Timber.tag(TAG).e(failure, "Error occurred during sign in")
-                onFailed(RendezvousFailureReason.Unknown)
+            } catch (t: Throwable) {
+                Timber.tag(TAG).e(t, "Error occurred during sign in")
+                if (t is RendezvousError) {
+                    onFailed(t.reason)
+                } else {
+                    onFailed(RendezvousFailureReason.Unknown)
+                }
             }
         }
     }

From 29065b819f6be245a9e78ca3ba7666766d0ce28a Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:03:47 +0100
Subject: [PATCH 236/400] Remove unused class

---
 .../rendezvous/model/EmbeddedRendezvous.kt    | 26 -------------------
 1 file changed, 26 deletions(-)
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
deleted file mode 100644
index 785ce1fed7..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt
+++ /dev/null
@@ -1,26 +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.api.rendezvous.model
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-
-@JsonClass(generateAdapter = true)
-open class EmbeddedRendezvous(
-        @Json(name = "transport") val transport: RendezvousTransportDetails,
-        @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm
-)

From 76a11d97dc61a42a1deac01f20e9f22c95ac0c3d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= <jorgem@element.io>
Date: Mon, 17 Oct 2022 17:05:36 +0200
Subject: [PATCH 237/400] Bump WYSIWYG lib to 0.2.1

---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index d60283e825..547b76aa89 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -101,7 +101,7 @@ ext.libs = [
         ],
         element     : [
                 'opusencoder'             : "io.element.android:opusencoder:1.1.0",
-                'wysiwyg'                 : "io.element.android:wysiwyg:0.1.0"
+                'wysiwyg'                 : "io.element.android:wysiwyg:0.2.1"
         ],
         squareup    : [
                 'moshi'                  : "com.squareup.moshi:moshi:$moshi",

From e877feed6eedc22792a24188e3fa37099d5f1f93 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:06:08 +0100
Subject: [PATCH 238/400] Add @JsonClass to all enums

---
 .../java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt | 2 ++
 .../org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt  | 2 ++
 .../org/matrix/android/sdk/api/rendezvous/model/Protocol.kt     | 2 ++
 .../matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt | 2 ++
 .../android/sdk/api/rendezvous/model/RendezvousTransportType.kt | 2 ++
 .../api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt    | 2 ++
 6 files changed, 12 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
index 2ef24e9cb7..a0b2cb31b4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class Outcome(val value: String) {
     @Json(name = "success")
     SUCCESS("success"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
index 5ff4cd7cfa..9e0dbd9562 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 internal enum class PayloadType(val value: String) {
     @Json(name = "m.login.start")
     START("m.login.start"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
index 18381984a5..3442d4abf4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class Protocol(val value: String) {
     @Json(name = "login_token")
     LOGIN_TOKEN("login_token")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
index 1c070599b0..8ce6430212 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class RendezvousIntent {
     @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
     @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index 9c3e44f25b..2814cad77c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class RendezvousTransportType(val value: String) {
     @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index 9a9db58a41..d9f73a0b99 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -17,7 +17,9 @@
 package org.matrix.android.sdk.api.rendezvous.model
 
 import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
 
+@JsonClass(generateAdapter = true)
 enum class SecureRendezvousChannelAlgorithm(val value: String) {
     @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
 }

From 7793667970ae4b000d7808ce353d03cdaf6c8d62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= <jorgem@element.io>
Date: Mon, 17 Oct 2022 17:12:16 +0200
Subject: [PATCH 239/400] Update changelog

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

diff --git a/changelog.d/7384.misc b/changelog.d/7384.misc
new file mode 100644
index 0000000000..3994dc0fa1
--- /dev/null
+++ b/changelog.d/7384.misc
@@ -0,0 +1 @@
+Update WYSIWYG library to v0.2.1.

From 623277e31ff94a4deac05b3bc297b95f52c85441 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:13:49 +0100
Subject: [PATCH 240/400] Lint

---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt | 4 ----
 .../transports/SimpleHttpRendezvousTransport.kt          | 9 +++++----
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 9a5c5e865a..2f368d6520 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -24,12 +24,8 @@ import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
 import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason
 import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous
-import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
-import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
-import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails
 import org.matrix.android.sdk.api.util.MatrixJsonParser
 import org.matrix.android.sdk.internal.extensions.toUnsignedInt
 import org.matrix.olm.OlmSAS
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index fa0e5d8e2a..50cebae12d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -143,10 +143,11 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
     }
 
     private fun get404Error(): RendezvousError {
-        return if (expiresAt != null && Date() > expiresAt)
-            RendezvousError("Expired", RendezvousFailureReason.Expired)
-        else
-            RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown)
+        if (expiresAt != null && Date() > expiresAt) {
+            return RendezvousError("Expired", RendezvousFailureReason.Expired)
+        }
+
+        return RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown)
     }
 
     override suspend fun close() {

From 3d37e0b2a557cf121efe01935a5a2980c9177ff2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:19:03 +0100
Subject: [PATCH 241/400] Fix enum JsonClass generateAdapter = false

---
 .../java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt | 2 +-
 .../org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt  | 2 +-
 .../org/matrix/android/sdk/api/rendezvous/model/Protocol.kt     | 2 +-
 .../matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt | 2 +-
 .../android/sdk/api/rendezvous/model/RendezvousTransportType.kt | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
index a0b2cb31b4..0ebd1f88b3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class Outcome(val value: String) {
     @Json(name = "success")
     SUCCESS("success"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
index 9e0dbd9562..33beb1f525 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 internal enum class PayloadType(val value: String) {
     @Json(name = "m.login.start")
     START("m.login.start"),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
index 3442d4abf4..7c020da641 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class Protocol(val value: String) {
     @Json(name = "login_token")
     LOGIN_TOKEN("login_token")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
index 8ce6430212..65037e1252 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class RendezvousIntent {
     @Json(name = "login.start") LOGIN_ON_NEW_DEVICE,
     @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index 2814cad77c..aa578e3660 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class RendezvousTransportType(val value: String) {
     @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
 }

From 552fb9de9a30d8295256329f0c63fc525d1031cb Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:24:01 +0100
Subject: [PATCH 242/400] Improved comment around QR generation

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt  | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 7f95fad485..69deb44c70 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -147,9 +147,10 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     /**
-     * TODO. UI test purpose. Fixme accordingly.
+     * QR code generation is not currently supported and this is a placeholder for future
+     * functionality.
      */
     private fun generateQrCodeData(): String {
-        return "TODO"
+        return "NOT SUPPORTED"
     }
 }

From b2dc0b33b5fb07bf47a5cc697589479d138e5515 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Mon, 17 Oct 2022 18:32:35 +0300
Subject: [PATCH 243/400] Implement try again button action.

---
 .../GetHomeServerCapabilitiesTask.kt          |  1 +
 .../features/login/qr/QrCodeLoginAction.kt    |  1 +
 .../features/login/qr/QrCodeLoginActivity.kt  | 44 +++++++++++--------
 .../login/qr/QrCodeLoginStatusFragment.kt     |  7 +++
 .../login/qr/QrCodeLoginViewEvents.kt         |  1 +
 .../features/login/qr/QrCodeLoginViewModel.kt |  5 +++
 6 files changed, 41 insertions(+), 18 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 2c3cb440b6..4b56d8e756 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -20,6 +20,7 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.api.MatrixPatterns.getServerName
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
+import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.internal.auth.version.Versions
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
index 8854d0720f..5ea46d3dcd 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt
@@ -22,4 +22,5 @@ sealed class QrCodeLoginAction : VectorViewModelAction {
     data class OnQrCodeScanned(val qrCode: String) : QrCodeLoginAction()
     object GenerateQrCode : QrCodeLoginAction()
     object ShowQrCode : QrCodeLoginAction()
+    object TryAgain : QrCodeLoginAction()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index 2f30261890..c9b8ae0080 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -38,30 +38,33 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
         super.onCreate(savedInstanceState)
         views.toolbar.visibility = View.GONE
 
-        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG)
         if (isFirstCreation()) {
-            when (qrCodeLoginArgs?.loginType) {
-                QrCodeLoginType.LOGIN -> {
-                    showInstructionsFragment(qrCodeLoginArgs)
-                }
-                QrCodeLoginType.LINK_A_DEVICE -> {
-                    if (qrCodeLoginArgs.showQrCodeImmediately) {
-                        handleNavigateToShowQrCodeScreen()
-                    } else {
-                        showInstructionsFragment(qrCodeLoginArgs)
-                    }
-                }
-                null -> {
-                    Timber.i("QrCodeLoginArgs is null. This is not expected.")
-                    finish()
-                    return
-                }
-            }
+           navigateToInitialFragment()
         }
 
         observeViewEvents()
     }
 
+    private fun navigateToInitialFragment() {
+        val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG)
+        when (qrCodeLoginArgs?.loginType) {
+            QrCodeLoginType.LOGIN -> {
+                showInstructionsFragment(qrCodeLoginArgs)
+            }
+            QrCodeLoginType.LINK_A_DEVICE -> {
+                if (qrCodeLoginArgs.showQrCodeImmediately) {
+                    handleNavigateToShowQrCodeScreen()
+                } else {
+                    showInstructionsFragment(qrCodeLoginArgs)
+                }
+            }
+            null -> {
+                Timber.i("QrCodeLoginArgs is null. This is not expected.")
+                finish()
+            }
+        }
+    }
+
     private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) {
         addFragment(
                 views.container,
@@ -77,10 +80,15 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
                 QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen()
                 QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen()
                 QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen()
+                QrCodeLoginViewEvents.NavigateToInitialScreen -> handleNavigateToInitialScreen()
             }
         }
     }
 
+    private fun handleNavigateToInitialScreen() {
+        navigateToInitialFragment()
+    }
+
     private fun handleNavigateToShowQrCodeScreen() {
         addFragment(
                 views.container,
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 617d620c27..097c956f2c 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -42,6 +42,13 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         initCancelButton()
+        initTryAgainButton()
+    }
+
+    private fun initTryAgainButton() {
+        views.qrCodeLoginStatusTryAgainButton.debouncedClicks {
+            viewModel.handle(QrCodeLoginAction.TryAgain)
+        }
     }
 
     private fun initCancelButton() {
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
index 0f282fee38..e20ea6b2e8 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt
@@ -22,4 +22,5 @@ sealed class QrCodeLoginViewEvents : VectorViewEvents {
     object NavigateToStatusScreen : QrCodeLoginViewEvents()
     object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents()
     object NavigateToHomeScreen : QrCodeLoginViewEvents()
+    object NavigateToInitialScreen : QrCodeLoginViewEvents()
 }
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 69deb44c70..ed2fb6e0f5 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -54,9 +54,14 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             is QrCodeLoginAction.OnQrCodeScanned -> handleOnQrCodeScanned(action)
             QrCodeLoginAction.GenerateQrCode -> handleQrCodeViewStarted()
             QrCodeLoginAction.ShowQrCode -> handleShowQrCode()
+            QrCodeLoginAction.TryAgain -> handleTryAgain()
         }
     }
 
+    private fun handleTryAgain() {
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToInitialScreen)
+    }
+
     private fun handleShowQrCode() {
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToShowQrCodeScreen)
     }

From 1863e4c3efe867faa0e30d001fadf33885fbe08d Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 16:34:51 +0100
Subject: [PATCH 244/400] Use unstable prefixes

---
 .../org/matrix/android/sdk/api/rendezvous/model/Protocol.kt   | 4 ++--
 .../sdk/api/rendezvous/model/RendezvousTransportType.kt       | 3 ++-
 .../api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt  | 3 ++-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
index 7c020da641..6fce2fa11c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt
@@ -21,6 +21,6 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = false)
 enum class Protocol(val value: String) {
-    @Json(name = "login_token")
-    LOGIN_TOKEN("login_token")
+    @Json(name = "org.matrix.msc3906.login_token")
+    LOGIN_TOKEN("org.matrix.msc3906.login_token")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
index aa578e3660..6fca7efa71 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt
@@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = false)
 enum class RendezvousTransportType(val value: String) {
-    @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1")
+    @Json(name = "org.matrix.msc3886.http.v1")
+    MSC3886_SIMPLE_HTTP_V1("org.matrix.msc3886.http.v1")
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index d9f73a0b99..ab58179f5a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 enum class SecureRendezvousChannelAlgorithm(val value: String) {
-    @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256")
+    @Json(name = "org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")
+    ECDH_V1("org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")
 }

From f686299d344739a5e4c07a62887356fb7d83af6f Mon Sep 17 00:00:00 2001
From: waclaw66 <waclaw66@seznam.cz>
Date: Mon, 17 Oct 2022 06:59:54 +0000
Subject: [PATCH 245/400] Translated using Weblate (Czech)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/
---
 .../src/main/res/values-cs/strings.xml        | 42 ++++++++++++++++---
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-cs/strings.xml b/library/ui-strings/src/main/res/values-cs/strings.xml
index ffa7123dae..bc88530c3d 100644
--- a/library/ui-strings/src/main/res/values-cs/strings.xml
+++ b/library/ui-strings/src/main/res/values-cs/strings.xml
@@ -962,10 +962,10 @@
     <string name="settings_push_rules">Push pravidla</string>
     <string name="settings_push_rules_no_rules">Žádná push pravidla nejsou definována</string>
     <string name="settings_push_gateway_no_pushers">Žádné push brány nejsou registrovány</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_id">ID aplikace:</string>
+    <string name="push_gateway_item_push_key">Klíč push:</string>
+    <string name="push_gateway_item_app_display_name">Zobrazovaný název aplikace:</string>
+    <string name="push_gateway_item_device_name">Název relace:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Formát:</string>
     <string name="preference_voice_and_video">Hlas a video</string>
@@ -1136,7 +1136,7 @@
 \n
 \nZastavit proces změny hesla\?</string>
     <string name="login_set_email_title">Nastavit emailovou adresu</string>
-    <string name="login_set_email_notice">Nastavte emailovou adresu pro obnovu svého účtu. Později můžete volitelně dovolit lidem, které znáte, aby Vás podle emailu nalezli.</string>
+    <string name="login_set_email_notice">Nastavte e-mailovou adresu pro obnovení účtu. Později můžete volitelně povolit svým známým, aby vás podle této adresy nalezli.</string>
     <string name="login_set_email_mandatory_hint">Email</string>
     <string name="login_set_email_optional_hint">Email (volitelné)</string>
     <string name="login_set_email_submit">Dále</string>
@@ -2796,4 +2796,34 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ V této místnosti jsou neověřená zařízení, která nebudou schopna dešifrovat odeslané zprávy.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Nikdy neodesílat šifrované zprávy do neověřených relací v této místnosti.</string>
     <string name="action_got_it">Rozumím</string>
-</resources>
+    <string name="rich_text_editor_format_underline">Použít podtržení</string>
+    <string name="rich_text_editor_format_strikethrough">Použít přeškrtnutí</string>
+    <string name="rich_text_editor_format_bold">Použít tučný text</string>
+    <string name="rich_text_editor_format_italic">Použít kurzívu</string>
+    <string name="labs_enable_client_info_recording_summary">Zaznamenávat název, verzi a url pro snadnější rozpoznání relací ve správci relací.</string>
+    <string name="labs_enable_client_info_recording_title">Povolit záznamenávání informací o klientu</string>
+    <string name="labs_enable_session_manager_summary">Získejte lepší přehled a kontrolu nad všemi relacemi.</string>
+    <string name="labs_enable_session_manager_title">Použít nový správce relací</string>
+    <string name="device_manager_session_details_device_operating_system">Operační systém</string>
+    <string name="device_manager_session_details_device_model">Model</string>
+    <string name="device_manager_session_details_device_browser">Prohlížeč</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Verze</string>
+    <string name="device_manager_session_details_application_name">Název</string>
+    <string name="device_manager_session_details_application">Aplikace</string>
+    <string name="device_manager_push_notifications_description">Přijímat push oznámení v této relaci.</string>
+    <string name="device_manager_push_notifications_title">Push oznámení</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Ověřením aktuální relace zjistíte stav ověření této relace.</string>
+    <string name="device_manager_verification_status_unknown">Neznámý stav ověření</string>
+    <string name="push_gateway_item_enabled">Zapnuto:</string>
+    <string name="push_gateway_item_device_id">ID relace:</string>
+    <string name="error_check_network">Něco se pokazilo. Zkontrolujte prosím síťové připojení a zkuste to znovu.</string>
+    <string name="grant_permission">Udělit oprávnění</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} potřebuje oprávnění k zobrazování oznámení.
+\nUdělte prosím toto oprávnění.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} potřebuje oprávnění k zobrazování oznámení. Oznámení mohou zobrazovat vaše zprávy, pozvánky atd.
+\n
+\nPro zobrazování oznámení povolte přístup na dalších vyskakovacích oknech.</string>
+    <string name="labs_enable_rich_text_editor_summary">Vyzkoušejte rozšířený textový editor (textový režim již brzy)</string>
+    <string name="labs_enable_rich_text_editor_title">Povolit rozšířený textový editor</string>
+</resources>
\ No newline at end of file

From cfd750e5d28165f259bf64f0d49a4b243abaabf7 Mon Sep 17 00:00:00 2001
From: Johannes Marbach <n0-0ne+github@mailbox.org>
Date: Mon, 17 Oct 2022 06:42:51 +0000
Subject: [PATCH 246/400] Translated using Weblate (German)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 library/ui-strings/src/main/res/values-de/strings.xml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index da59f03e18..6c292329ef 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -2741,4 +2741,6 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Es befinden sich nicht verifizierte Geräte in diesem Raum. Sie werden deine Nachrichten nicht entschlüsseln können.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Niemals verschlüsselte Nachrichten zu unverifizierten Sitzungen in diesem Raum senden.</string>
     <string name="action_got_it">Verstanden</string>
-</resources>
+    <string name="labs_enable_rich_text_editor_summary">Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus)</string>
+    <string name="labs_enable_rich_text_editor_title">Aktiviere Rich-Text-Editor</string>
+</resources>
\ No newline at end of file

From edf3f2cd46f41099e5d9e2f4ca85191cf9dfc7d6 Mon Sep 17 00:00:00 2001
From: jucktnich <humidor.tandem0y@icloud.com>
Date: Sun, 16 Oct 2022 21:46:00 +0000
Subject: [PATCH 247/400] Translated using Weblate (German)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 library/ui-strings/src/main/res/values-de/strings.xml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index 6c292329ef..882f04ed24 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -2743,4 +2743,5 @@
     <string name="action_got_it">Verstanden</string>
     <string name="labs_enable_rich_text_editor_summary">Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus)</string>
     <string name="labs_enable_rich_text_editor_title">Aktiviere Rich-Text-Editor</string>
+    <string name="device_manager_session_details_device_browser">Browser</string>
 </resources>
\ No newline at end of file

From 579eedd050222801893f26feaf8a11f52f58740b Mon Sep 17 00:00:00 2001
From: Vri <element@vrifox.cc>
Date: Sun, 16 Oct 2022 12:50:08 +0000
Subject: [PATCH 248/400] Translated using Weblate (German)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 .../src/main/res/values-de/strings.xml        | 57 +++++++++++++------
 1 file changed, 39 insertions(+), 18 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index 882f04ed24..545bb3f45d 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -708,10 +708,10 @@
     <string name="unexpected_error">Unerwarteter Fehler</string>
     <string name="keys_backup_setup_skip_title">Bist du sicher\?</string>
     <string name="keys_backup_restore_key_enter_hint">Wiederherstellungsschlüssel eingeben</string>
-    <string name="keys_backup_restoring_waiting_message">Stelle Backup wieder her:</string>
+    <string name="keys_backup_restoring_waiting_message">Stelle Sicherung wieder her:</string>
     <string name="keys_backup_unlock_button">Historie entschlüsseln</string>
     <string name="keys_backup_settings_restore_backup_button">Von Sicherung wiederherstellen</string>
-    <string name="keys_backup_settings_delete_backup_button">Sicherung löschen</string>
+    <string name="keys_backup_settings_delete_backup_button">Lösche Sicherung</string>
     <string name="keys_backup_settings_deleting_backup">Lösche Sicherung …</string>
     <string name="keys_backup_settings_delete_confirm_title">Lösche Sicherung</string>
     <string name="settings_notification_by_event">Präferenz der Benachrichtigungen nach Ereignis</string>
@@ -722,7 +722,7 @@
     <string name="settings_troubleshoot_test_fcm_failed_account_missing">[%1$s]
 \nDieser Fehler ist außerhalb von ${app_name} passiert. Es gibt kein Google-Konto auf dem Gerät. Bitte füge ein Google-Konto hinzu.</string>
     <string name="settings_cryptography_manage_keys">Verwaltung der Kryptoschlüssel</string>
-    <string name="encryption_settings_manage_message_recovery_summary">Schlüssel-Sicherung verwalten</string>
+    <string name="encryption_settings_manage_message_recovery_summary">Schlüsselsicherung verwalten</string>
     <string name="keys_backup_setup_step1_description">Nachrichten in verschlüsselten Räumen sind mit Ende-zu-Ende-Verschlüsselung gesichert. Nur du und der Empfänger haben die Schlüssel um diese Nachrichten zu lesen.
 \n
 \nSichere deine Schlüssel, um sie nicht zu verlieren.</string>
@@ -763,7 +763,7 @@
     <string name="sign_out_bottom_sheet_warning_backup_not_active">Schlüsselsicherung sollte bei allen Sitzungen aktiviert sein, um den Verlust verschlüsselter Nachrichten zu verhindern.</string>
     <string name="sign_out_bottom_sheet_dont_want_secure_messages">Ich möchte meine verschlüsselten Nachrichten nicht</string>
     <string name="sign_out_bottom_sheet_backing_up_keys">Sichere Schlüssel …</string>
-    <string name="are_you_sure">Sicher\?</string>
+    <string name="are_you_sure">Bist du sicher\?</string>
     <string name="backup">Sicherung</string>
     <string name="sign_out_bottom_sheet_will_lose_secure_messages">Alle verschlüsselten Nachrichten gehen verloren, wenn Du dich abmeldest ohne die Schlüssel gesichert zu haben.</string>
     <string name="action_sign_out_confirmation_simple">Bist du sicher, dass du dich abmelden möchtest\?</string>
@@ -783,7 +783,7 @@
     <string name="keys_backup_setup_step3_text_line1">Deine Schlüssel wurden gesichert.</string>
     <string name="keys_backup_setup_step3_text_line2">Dein Wiederherstellungsschlüssel ist ein Sicherungsnetz - du kannst es benutzen um den Zugriff auf deine verschlüsselten Nachrichten wiederherzustellen, falls du deine Passphrase vergisst.
 \nVerwahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie einem Passwortmanager (oder Safe)</string>
-    <string name="keys_backup_setup_step3_text_line2_no_passphrase">Bewahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort auf, wie z.B. einem Passwortmanager (oder Tresor) auf</string>
+    <string name="keys_backup_setup_step3_text_line2_no_passphrase">Bewahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie einem Passwortmanager (oder Safe) auf</string>
     <string name="keys_backup_setup_step3_button_title_no_passphrase">Ich habe eine Kopie angefertigt</string>
     <string name="keys_backup_setup_step3_share_recovery_file">Teilen</string>
     <string name="keys_backup_banner_recover_line1">Verliere nie wieder verschlüsselte Nachrichten</string>
@@ -1495,11 +1495,11 @@
     <string name="room_participants_ban_reason">Grund für den Bann</string>
     <string name="room_participants_unban_title">Bann des Benutzers aufheben</string>
     <string name="room_participants_unban_prompt_msg">Das Aufheben des Bannes wird dem Benutzer erlauben dem Raum wieder beizutreten.</string>
-    <string name="settings_secure_backup_section_title">Sicheres Backup</string>
-    <string name="settings_secure_backup_setup">Backup einrichten</string>
-    <string name="settings_secure_backup_reset">Backup zurücksetzen</string>
+    <string name="settings_secure_backup_section_title">Verschlüsselte Sicherung</string>
+    <string name="settings_secure_backup_setup">Sicherung einrichten</string>
+    <string name="settings_secure_backup_reset">Sicherung zurücksetzen</string>
     <string name="settings_secure_backup_enter_to_setup">Auf diesem Gerät einrichten</string>
-    <string name="settings_secure_backup_section_info">Verlust verschlüsselter Nachrichten und Daten verhindern, indem die Schlüssel für die Entschlüsselung auf dem Server gesichert werden.</string>
+    <string name="settings_secure_backup_section_info">Verhindere, den Zugriff auf verschlüsselte Nachrichten und Daten zu verlieren, indem du die Verschlüsselungs-Schlüssel auf deinem Server sicherst.</string>
     <string name="reset_secure_backup_title">Generiere einen neuen Sicherheitsschlüssel oder setze eine neue Sicherheitspassphrase für dein existierendes Backup.</string>
     <string name="reset_secure_backup_warning">Dieses wird deinen aktuellen Schlüssel oder deine aktuelle Phrase ersetzen.</string>
     <string name="disabled_integration_dialog_title">Integrationen sind deaktiviert</string>
@@ -1512,9 +1512,9 @@
     <string name="active_widget_view_action">ANSICHT</string>
     <string name="active_widgets_title">Aktive Widgets</string>
     <string name="recovery_key_export_saved">Der Sicherheitsschlüssel ist gespeichert worden.</string>
-    <string name="secure_backup_banner_setup_line1">Backup</string>
+    <string name="secure_backup_banner_setup_line1">Verschlüsselte Sicherung</string>
     <string name="secure_backup_banner_setup_line2">Absicherung gegen den Verlust verschlüsselter Nachrichten</string>
-    <string name="secure_backup_setup">Richte Backup ein</string>
+    <string name="secure_backup_setup">Sicherung einrichten</string>
     <string name="event_redacted">Nachricht entfernt</string>
     <string name="settings_show_redacted">Gelöschte Nachrichten zeigen</string>
     <string name="settings_show_redacted_summary">Zeigt einen Platzhalter für gelöschte Nachrichten an</string>
@@ -1534,7 +1534,7 @@
     <string name="login_server_url_form_common_notice">Gib die Adresse des Servers ein, den du benutzen möchtest</string>
     <string name="login_connect_using_matrix_id_submit">Einloggen mit Matrix-ID</string>
     <string name="login_signin_matrix_id_title">Einloggen mit Matrix-ID</string>
-    <string name="login_signin_matrix_id_notice">Wenn du einen Account auf einem Homeserver eingerichtet hast, benutze deine Matrix-ID (z.B. @benutzer:domain.com) und Passwort.</string>
+    <string name="login_signin_matrix_id_notice">Falls du ein Konto auf einem Heim-Server eingerichtet hast, verwende nachstehend deine Matrix-ID (z. B. @benutzer:domain.com) und dein Passwort.</string>
     <string name="login_signin_matrix_id_hint">Matrix-ID</string>
     <string name="login_signin_matrix_id_password_notice">Wenn du dein Passwort nicht weißt, gehe zurück um es zurücksetzen zu lassen.</string>
     <string name="login_signin_matrix_id_error_invalid_matrix_id">Dies ist keine gültige Benutzerkennung. Erwartetes Format: \'@benutzer:homeserver.org\'</string>
@@ -1547,7 +1547,7 @@
     <string name="bootstrap_info_text_2">Gib eine Sicherheitsphrase ein, die nur du kennst. Diese wird benutzt um deine Daten auf dem Server geheim zu halten.</string>
     <string name="bootstrap_cancel_text">Wenn du jetzt abbrichst und den Zugriff zu deinen Sitzungen verlierst, kannst du verschlüsselte Nachrichten und Daten verlieren.
 \n
-\nDu kannst auch ein Backup einrichten und deine Schlüssel in den Einstellungen verwalten.</string>
+\nDu kannst auch eine Sicherung einrichten und deine Schlüssel in den Einstellungen verwalten.</string>
     <string name="room_created_summary_item_by_you">Du hast den Raum erstellt und konfiguriert.</string>
     <string name="auth_invalid_login_deactivated_account">Dieser Account ist deaktiviert worden.</string>
     <string name="error_saving_media_file">Konnte Mediendatei nicht speichern</string>
@@ -1575,14 +1575,14 @@
     <string name="a11y_unmute_microphone">Aktiviere Mikrophon</string>
     <string name="a11y_stop_camera">Stoppe Kamera</string>
     <string name="a11y_start_camera">Starte Kamera</string>
-    <string name="bottom_sheet_setup_secure_backup_title">Backup</string>
-    <string name="bottom_sheet_setup_secure_backup_subtitle">Verlust verschlüsselter Nachrichten und Daten verhindern, indem die Schlüssel für die Entschlüsselung am Server gesichert werden.</string>
+    <string name="bottom_sheet_setup_secure_backup_title">Verschlüsselte Sicherung</string>
+    <string name="bottom_sheet_setup_secure_backup_subtitle">Verhindere, den Zugriff auf verschlüsselte Nachrichten und Daten zu verlieren, indem du die Verschlüsselungs-Schlüssel auf deinem Server sicherst.</string>
     <string name="bottom_sheet_setup_secure_backup_security_key_title">Sicherheitsschlüssel benutzen</string>
-    <string name="bottom_sheet_setup_secure_backup_security_key_subtitle">Generiere einen Sicherheitsschlüssel, welcher z.B. in einem Passwortmanager oder in einem Tresor sicher aufbewahrt werden sollte.</string>
+    <string name="bottom_sheet_setup_secure_backup_security_key_subtitle">Generiere einen Sicherheitsschlüssel, den du in einem Passwort-Manager oder Tresor sicher aufbewahren solltest.</string>
     <string name="bottom_sheet_setup_secure_backup_security_phrase_title">Eine Sicherheitsphrase benutzen</string>
     <string name="bottom_sheet_setup_secure_backup_security_phrase_subtitle">Gib eine geheime Phrase ein, die nur du kennst und generiere einen Schlüssel als Backup.</string>
     <string name="bottom_sheet_save_your_recovery_key_title">Speichere deinen Sicherheitsschlüssel</string>
-    <string name="bottom_sheet_save_your_recovery_key_content">Bewahre deinen Sicherheitsschlüssel irgendwo sicher auf, wie z.B. in einem Passwortmanager oder in einem Tresor.</string>
+    <string name="bottom_sheet_save_your_recovery_key_content">Bewahre deinen Sicherheitsschlüssel in einem Passwort-Manager oder Tresor sicher auf.</string>
     <string name="set_a_security_phrase_title">Sicherheitsphrase setzen</string>
     <string name="set_a_security_phrase_notice">Gib eine Sicherheitsphrase ein, welche nur du kennst und deine Daten auf dem Server geheim halten soll.</string>
     <string name="set_a_security_phrase_hint">Sicherheitsphrase</string>
@@ -1810,7 +1810,7 @@
     <string name="room_alias_local_address_empty">Dieser Raum hat keine lokalen Adressen</string>
     <string name="room_alias_local_address_subtitle">Füge Adressen für diesen Raum hinzu, damit andere Nutzer ihn auf %1$s finden können</string>
     <string name="room_alias_local_address_title">Lokale Adresse</string>
-    <string name="room_alias_address_hint">Neue öffentliche Adresse (z.B. #alias:server)</string>
+    <string name="room_alias_address_hint">Neue öffentliche Adresse (z. B. #alias:server)</string>
     <string name="room_alias_address_empty">Noch keine weiteren öffentlichen Adressen vorhanden.</string>
     <string name="room_alias_address_empty_can_add">Noch keine weiteren öffentlichen Adressen vorhanden, füge unten eine hinzu.</string>
     <string name="room_alias_delete_confirmation">Die Adresse \"%1$s\" löschen\?</string>
@@ -2744,4 +2744,25 @@
     <string name="labs_enable_rich_text_editor_summary">Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus)</string>
     <string name="labs_enable_rich_text_editor_title">Aktiviere Rich-Text-Editor</string>
     <string name="device_manager_session_details_device_browser">Browser</string>
+    <string name="rich_text_editor_format_strikethrough">Durchgestrichen formatieren</string>
+    <string name="rich_text_editor_format_italic">Kursiv formatieren</string>
+    <string name="rich_text_editor_format_bold">Fett formatieren</string>
+    <string name="rich_text_editor_format_underline">Unterstrichen formatieren</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} benötigt die Berechtigung zur Anzeige von Benachrichtigungen.
+\nBitte gewähre diese Berechtigung.</string>
+    <string name="labs_enable_client_info_recording_summary">Bezeichnung, Version und URL der Anwendung registrieren, damit diese Sitzung in der Sitzungsverwaltung besser erkennbar ist.</string>
+    <string name="labs_enable_client_info_recording_title">Anwendungsinformationen erfassen</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="labs_enable_session_manager_summary">Bessere Übersicht und Kontrolle über all deine Sitzungen.</string>
+    <string name="labs_enable_session_manager_title">Aktiviere neue Sitzungsverwaltung</string>
+    <string name="device_manager_session_details_device_operating_system">Betriebssystem</string>
+    <string name="device_manager_session_details_device_model">Modell</string>
+    <string name="device_manager_session_details_application_version">Version</string>
+    <string name="device_manager_session_details_application_name">Name</string>
+    <string name="device_manager_session_details_application">Anwendung</string>
+    <string name="device_manager_push_notifications_description">Erhalte Push-Benachrichtigungen in dieser Sitzung.</string>
+    <string name="device_manager_push_notifications_title">Push-Benachrichtigungen</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Verifiziere deine aktuelle Sitzung, um den Verifizierungsstatus dieser Sitzung anzuzeigen.</string>
+    <string name="device_manager_verification_status_unknown">Unbekannter Verifizierungsstatus</string>
+    <string name="push_gateway_item_device_id">Sitzungs-ID:</string>
 </resources>
\ No newline at end of file

From 9cac1c8cfb66061ded81d87e3d8adbe37f0f4cb3 Mon Sep 17 00:00:00 2001
From: Christoph Klassen <christoph.klassen@intevation.de>
Date: Sat, 15 Oct 2022 12:21:02 +0000
Subject: [PATCH 249/400] Translated using Weblate (German)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 library/ui-strings/src/main/res/values-de/strings.xml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index 545bb3f45d..a8e24369b5 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -2765,4 +2765,10 @@
     <string name="device_manager_verification_status_detail_other_session_unknown">Verifiziere deine aktuelle Sitzung, um den Verifizierungsstatus dieser Sitzung anzuzeigen.</string>
     <string name="device_manager_verification_status_unknown">Unbekannter Verifizierungsstatus</string>
     <string name="push_gateway_item_device_id">Sitzungs-ID:</string>
+    <string name="push_gateway_item_enabled">Aktiviert:</string>
+    <string name="error_check_network">Etwas ist schiefgelaufen. Bitte überprüfe deine Internetverbindung und versuche es erneut.</string>
+    <string name="grant_permission">Berechtigung geben</string>
+    <string name="permissions_rationale_msg_notification">${app_name} braucht die Berechtigung, um Benachrichtigungen anzuzeigen. Benachrichtigungen können deine Nachrichten, Einladungen etc. anzeigen.
+\n
+\nBitte erlaube den Zugriff im nächsten Dialog, damit Benachrichtigungen angezeigt werden können.</string>
 </resources>
\ No newline at end of file

From 491b5cb537da3fde90fbbe735fc6b5f0c9029f78 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com>
Date: Sun, 16 Oct 2022 08:20:54 +0000
Subject: [PATCH 250/400] Translated using Weblate (Estonian)

Currently translated at 98.6% (2437 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/et/
---
 .../ui-strings/src/main/res/values-et/strings.xml    | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-et/strings.xml b/library/ui-strings/src/main/res/values-et/strings.xml
index d0f3b540e2..ecb7b9757c 100644
--- a/library/ui-strings/src/main/res/values-et/strings.xml
+++ b/library/ui-strings/src/main/res/values-et/strings.xml
@@ -1158,10 +1158,10 @@
     <string name="settings_push_rules">Tõuketeavituste reeglid</string>
     <string name="settings_push_rules_no_rules">Tõuketeavituste reegleid pole kirjeldatud</string>
     <string name="settings_push_gateway_no_pushers">Tõuketeavituste võrguväravaid pole registreeritud</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_id">Rakenduse ID:</string>
+    <string name="push_gateway_item_push_key">Tõuketeenuse võti:</string>
+    <string name="push_gateway_item_app_display_name">Rakenduse kuvatav nimi:</string>
+    <string name="push_gateway_item_device_name">Sessiooni nimi:</string>
     <string name="push_gateway_item_url">URL:</string>
     <string name="push_gateway_item_format">Vorming:</string>
     <string name="preference_voice_and_video">Heli ja video</string>
@@ -2733,4 +2733,6 @@
     <string name="command_description_devtools">Ava arendaja töövahendite vaade</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Ära iialgi saada selles jututoas krüptitud sõnumeid verifitseerimata sessioonidesse.</string>
     <string name="action_got_it">Selge lugu</string>
-</resources>
+    <string name="labs_enable_rich_text_editor_summary">Proovi vormindatud teksti alusel töötavat tekstitoimetit (varsti lisandub ka vormindamata teksti režiim)</string>
+    <string name="labs_enable_rich_text_editor_title">Võta kasutusele vormindatud teksti pruukiv tekstitoimeti</string>
+</resources>
\ No newline at end of file

From f51b20481de61d06aa506ff80f9d61fee6ca3386 Mon Sep 17 00:00:00 2001
From: Danial Behzadi <dani.behzi@ubuntu.com>
Date: Fri, 14 Oct 2022 20:12:02 +0000
Subject: [PATCH 251/400] Translated using Weblate (Persian)

Currently translated at 98.7% (2438 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/fa/
---
 .../src/main/res/values-fa/strings.xml           | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-fa/strings.xml b/library/ui-strings/src/main/res/values-fa/strings.xml
index 47cade0bf8..dbf3658887 100644
--- a/library/ui-strings/src/main/res/values-fa/strings.xml
+++ b/library/ui-strings/src/main/res/values-fa/strings.xml
@@ -1492,10 +1492,10 @@
     <string name="settings_troubleshoot_test_token_registration_quick_fix">ثبت ژتون</string>
     <string name="push_gateway_item_format">فرمت:</string>
     <string name="push_gateway_item_url">آدرس:</string>
-    <string name="push_gateway_item_device_name">نام نشست:</string>
-    <string name="push_gateway_item_app_display_name">نام برنامه:</string>
-    <string name="push_gateway_item_push_key">کلید push:</string>
-    <string name="push_gateway_item_app_id">شناسه برنامه:</string>
+    <string name="push_gateway_item_device_name">نام نمایشی نشست:</string>
+    <string name="push_gateway_item_app_display_name">نام نمایشی کاره:</string>
+    <string name="push_gateway_item_push_key">کلید ارسال:</string>
+    <string name="push_gateway_item_app_id">شناسهٔ کاره:</string>
     <string name="settings_push_gateway_no_pushers">هیچ push gateway‌ای ثبت نشده است</string>
     <string name="settings_push_rules_no_rules">هیچ قانونی برای push تعریف نشده است</string>
     <string name="navigate_to_room_when_already_in_the_room">شما در حال مشاهده این اتاق هستید!</string>
@@ -2720,4 +2720,10 @@
     <string name="command_description_devtools">گشودن صفحهٔ ابزارهای توسعه‌دهنده</string>
     <string name="labs_enable_deferred_dm_title">به کار انداختن پیام‌های مستقیم تعویقی</string>
     <string name="action_got_it">گرفتم</string>
-</resources>
+    <string name="tooltip_attachment_voice_broadcast">آغاز یک پخش همگانی صوتی</string>
+    <string name="command_description_table_flip">(╯°□°)╯︵ ┻━┻ را به ابتدای پیام متنی خام می‌افزاید</string>
+    <string name="attachment_type_voice_broadcast">پخش همگانی صدا</string>
+    <string name="grant_permission">اعطای دسترسی</string>
+    <string name="labs_enable_rich_text_editor_summary">ویرایشگر متن غنی را بیازمایید (حالت متن خام به زودی)</string>
+    <string name="labs_enable_rich_text_editor_title">به کار انداختن ویرایشگر متن غنی</string>
+</resources>
\ No newline at end of file

From 522dd2744e40d8ddbfc49589b61fcab98409e951 Mon Sep 17 00:00:00 2001
From: Glandos <bugs-github@antipoul.fr>
Date: Sat, 15 Oct 2022 19:53:52 +0000
Subject: [PATCH 252/400] Translated using Weblate (French)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/fr/
---
 .../src/main/res/values-fr/strings.xml        | 40 ++++++++++++++++---
 1 file changed, 35 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-fr/strings.xml b/library/ui-strings/src/main/res/values-fr/strings.xml
index 860840486e..b77173519d 100644
--- a/library/ui-strings/src/main/res/values-fr/strings.xml
+++ b/library/ui-strings/src/main/res/values-fr/strings.xml
@@ -869,10 +869,10 @@
     <string name="settings_push_rules">Règles de notification</string>
     <string name="settings_push_rules_no_rules">Aucune règle de notification définie</string>
     <string name="settings_push_gateway_no_pushers">Aucune passerelle de notification enregistrée</string>
-    <string name="push_gateway_item_app_id">app_id :</string>
-    <string name="push_gateway_item_push_key">push_key :</string>
-    <string name="push_gateway_item_app_display_name">app_display_name :</string>
-    <string name="push_gateway_item_device_name">session_name :</string>
+    <string name="push_gateway_item_app_id">App ID :</string>
+    <string name="push_gateway_item_push_key">Clé Push :</string>
+    <string name="push_gateway_item_app_display_name">Nom d’affichage de l’application :</string>
+    <string name="push_gateway_item_device_name">Nom d’affichage de la session :</string>
     <string name="push_gateway_item_url">URL :</string>
     <string name="push_gateway_item_format">Format :</string>
     <string name="preference_voice_and_video">Voix et vidéo</string>
@@ -2742,4 +2742,34 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Il y a des appareils non vérifiés dans ce salon, ils ne pourront pas déchiffrer vos messages envoyés.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Ne jamais envoyer de messages chiffrés aux sessions non vérifiées dans ce salon.</string>
     <string name="action_got_it">Compris</string>
-</resources>
+    <string name="rich_text_editor_format_underline">Souligner le texte</string>
+    <string name="rich_text_editor_format_strikethrough">Barrer le texte</string>
+    <string name="rich_text_editor_format_italic">Mettre en italique</string>
+    <string name="rich_text_editor_format_bold">Mettre en gras</string>
+    <string name="labs_enable_client_info_recording_summary">Enregistre le nom du client, sa version, et son URL pour retrouvez vos sessions plus facilement dans le gestionnaire de sessions.</string>
+    <string name="labs_enable_client_info_recording_title">Activer l’enregistrement des informations du client</string>
+    <string name="labs_enable_session_manager_summary">Ayez une meilleur visibilité et plus de contrôle sur toutes vos sessions.</string>
+    <string name="labs_enable_session_manager_title">Activer le nouveau gestionnaire de session</string>
+    <string name="device_manager_session_details_device_operating_system">Système d’exploitation</string>
+    <string name="device_manager_session_details_device_model">Modèle</string>
+    <string name="device_manager_session_details_device_browser">Navigateur</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Version</string>
+    <string name="device_manager_session_details_application_name">Nom</string>
+    <string name="device_manager_session_details_application">Application</string>
+    <string name="device_manager_push_notifications_description">Recevoir les notifications push sur cette session.</string>
+    <string name="device_manager_push_notifications_title">Notifications push</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Vérifiez votre session actuelle pour découvrir le statut de vérification de cette session.</string>
+    <string name="device_manager_verification_status_unknown">Status de vérification inconnu</string>
+    <string name="push_gateway_item_enabled">Activer :</string>
+    <string name="push_gateway_item_device_id">Identifiant de session :</string>
+    <string name="error_check_network">Quelque chose s’est mal passé. Vérifiez votre connexion réseau et réessayez.</string>
+    <string name="grant_permission">Accorder la permission</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} a besoin d’une permission pour afficher les notifications.
+\nVeuillez accorder la permission.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} a besoin de la permission pour afficher les notifications. Les notifications peuvent afficher vos messages, vos invitations, etc.
+\n
+\nVeuillez autoriser l’accès sur la prochaine fenêtre pour pouvoir voir des notifications.</string>
+    <string name="labs_enable_rich_text_editor_summary">Essayer l’éditeur de texte formaté (le mode texte brut arrive bientôt)</string>
+    <string name="labs_enable_rich_text_editor_title">Activer l’éditeur de texte formaté</string>
+</resources>
\ No newline at end of file

From 209ad450c19016932749cfa7f00fc0e4a26fda67 Mon Sep 17 00:00:00 2001
From: Szimszon <github@oregpreshaz.eu>
Date: Sun, 16 Oct 2022 15:25:57 +0000
Subject: [PATCH 253/400] Translated using Weblate (Hungarian)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/hu/
---
 .../src/main/res/values-hu/strings.xml        | 77 +++++++++++++++++--
 1 file changed, 70 insertions(+), 7 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-hu/strings.xml b/library/ui-strings/src/main/res/values-hu/strings.xml
index d2e9568053..57510e55a9 100644
--- a/library/ui-strings/src/main/res/values-hu/strings.xml
+++ b/library/ui-strings/src/main/res/values-hu/strings.xml
@@ -828,10 +828,10 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="settings_push_rules">„Push” szabályok</string>
     <string name="settings_push_rules_no_rules">„Push” szabályok nincsenek</string>
     <string name="settings_push_gateway_no_pushers">„Push” átjárók nincsenek regisztrálva</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_id">Alk azon:</string>
+    <string name="push_gateway_item_push_key">Push kulcs:</string>
+    <string name="push_gateway_item_app_display_name">Alk. képernyő név:</string>
+    <string name="push_gateway_item_device_name">Munkamenet képernyő név:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Formátum:</string>
     <string name="preference_voice_and_video">Hang &amp; Videó</string>
@@ -897,7 +897,7 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="settings_discovery_no_msisdn">Amint hozzáadtál egy telefonszámot megjelenik a felderítési beállítási lehetőség.</string>
     <string name="settings_discovery_disconnect_identity_server_info">Az azonosítási szerverről való lecsatlakozással nem leszel mások által megtalálható és másokat sem tudsz meghívni e-mail címmel vagy telefonszámmal.</string>
     <string name="settings_discovery_msisdn_title">Felderíthető telefonszámok</string>
-    <string name="settings_discovery_confirm_mail">Megerősítő levelet küldtünk ide: %s, ellenőrizd az e-mailedet és kattints a megerősítő hivatkozásra</string>
+    <string name="settings_discovery_confirm_mail">E-mailt küldtünk ide: %s, ellenőrizd és kattints a megerősítő hivatkozásra</string>
     <string name="settings_discovery_enter_identity_server">Add meg az azonosítási szerver URL-jét</string>
     <string name="settings_discovery_bad_identity_server">Az azonosítási szerverhez nem lehet csatlakozni</string>
     <string name="settings_discovery_please_enter_server">Kérlek add meg az azonosítási szerver url-jét</string>
@@ -1391,7 +1391,7 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="invite_users_to_room_failure">Felhasználókat nem tudtuk meghívni. Ellenőrizd azokat a felhasználókat akiket meg szeretnél hívni és próbáld újra.</string>
     <string name="event_redacted">Üzenet eltávolítva</string>
     <string name="settings_show_redacted_summary">Helykitöltő mutatása a törölt szövegek helyett</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">Megerősítő levelet küldtünk ide: %s, először ellenőrizd az e-mailedet és kattints a megerősítő hivatkozásra</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">E-mailt küldtünk ide: %s, először ellenőrizd és kattints a megerősítő hivatkozásra</string>
     <string name="uploads_media_title">MÉDIA</string>
     <string name="uploads_files_title">FÁJLOK</string>
     <string name="uploads_files_subtitle">%1$s itt: %2$s</string>
@@ -2709,4 +2709,67 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="labs_enable_deferred_dm_title">Késleltetett közvetlen üzenetek engedélyezése</string>
     <string name="labs_enable_new_app_layout_summary">Egyszerűsített Element opcionálisan lapokkal</string>
     <string name="labs_enable_new_app_layout_title">Új kinézet engedélyezése</string>
-</resources>
+    <string name="device_manager_learn_more_session_rename">Más felhasználók akikkel közvetlenül vagy szobában beszélgetsz látják a teljes listát a munkameneteidről.
+\n
+\nEzzel ők biztosak lehetnek abban, hogy ténylegesen veled beszélgetnek. Ez azt is jelenti, hogy látják a munkamenet nevét amit itt megadsz.</string>
+    <string name="device_manager_learn_more_sessions_verified">Ellenőrzött munkamenetbe a neveddel és jelszavaddal léptek be és ellenőrizve lett vagy a biztonsági jelmondattal vagy másik munkamenetből.
+\n
+\nEz azt jelenti, hogy tartalmazzák a titkosítási kulcsokat az régi üzenetekhez, és biztosítja a többieket a kommunikációban, hogy ezt a munkamenetet tényleg te használod.</string>
+    <string name="rich_text_editor_format_underline">Aláhúzott</string>
+    <string name="rich_text_editor_format_strikethrough">Áthúzott</string>
+    <string name="rich_text_editor_format_italic">Dőlt</string>
+    <string name="rich_text_editor_format_bold">Félkövér</string>
+    <string name="labs_enable_client_info_recording_summary">Kliens neve, verziója és url felvétele a munkamenet könnyebb azonosításához a munkamenet kezelőben.</string>
+    <string name="labs_enable_client_info_recording_title">Kliens információ felvételének engedélyezése</string>
+    <string name="labs_enable_session_manager_summary">Jobb áttekintés és felügyelet a munkamenetek felett.</string>
+    <string name="labs_enable_session_manager_title">Új munkamenet kezelő engedélyezése</string>
+    <string name="device_manager_learn_more_session_rename_title">Munkamenet átnevezése</string>
+    <string name="device_manager_learn_more_sessions_verified_title">Hitelesített munkamenetek</string>
+    <string name="device_manager_learn_more_sessions_unverified">Az ellenőrizetlen munkamenetek azok amikre a felhasználói neveddel és jelszavaddal léptek be de nem lett ellenőrizve.
+\n
+\nMindenképpen győződj meg arról, hogy felismered ezeket a munkameneteket mert lehet, hogy illetéktelenül használják a fiókodat.</string>
+    <string name="device_manager_learn_more_sessions_unverified_title">Ellenőrizetlen munkamenetek</string>
+    <string name="device_manager_learn_more_sessions_inactive">Az inaktív munkamenetek azok amiket egy ideje nem használtál, de továbbra is megkapják a titkosítási kulcsokat.
+\n
+\nA nem aktív munkamenetek törlésével növelhető a biztonság és a sebesség valamint könnyebb lesz felismerni a gyanús munkameneteket.</string>
+    <string name="device_manager_learn_more_sessions_inactive_title">Nem aktív munkamenetek</string>
+    <string name="device_manager_session_rename_warning">Fontos, hogy a munkamenet neve a kommunikációban résztvevők számára látható.</string>
+    <string name="device_manager_session_rename_description">Az egyedi munkamenet név segíthet az eszköz könnyebb felismerésében.</string>
+    <string name="device_manager_session_rename_edit_hint">Munkamenet neve</string>
+    <string name="device_manager_session_rename">Munkamenet átnevezése</string>
+    <string name="device_manager_session_details_device_operating_system">Operációs rendszer</string>
+    <string name="device_manager_session_details_device_model">Modell</string>
+    <string name="device_manager_session_details_device_browser">Böngésző</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Verzió</string>
+    <string name="device_manager_session_details_application_name">Név</string>
+    <string name="device_manager_session_details_application">Alkalmazás</string>
+    <string name="device_manager_push_notifications_description">Push értesítések fogadása ebben a munkamenetben.</string>
+    <string name="device_manager_push_notifications_title">Push értesítések</string>
+    <string name="device_manager_session_overview_signout">Kijelentkezés ebből a munkamenetből</string>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Ellenőrizetlen · A jelenlegi munkameneted</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Ellenőrizd a jelenlegi munkamenetedet, hogy ismert állapotba kerüljön.</string>
+    <string name="device_manager_verification_status_unknown">Ismeretlen ellenőrzési státusz</string>
+    <string name="tooltip_attachment_voice_broadcast">Hang közvetítés indítása</string>
+    <string name="key_authenticity_not_guaranteed">A titkosított üzenetek valódiságát ezen az eszközön nem lehet garantálni.</string>
+    <string name="settings_security_incognito_keyboard_summary">Utasítja a billentyűzetet, hogy ne mentsen személyre szabott adatokat, mint előzmények vagy szótár abból amit a beszélgetésekben írsz. Vedd figyelembe, hogy nem minden billentyűzet veszi ezt figyelembe.</string>
+    <string name="settings_security_incognito_keyboard_title">Inkognitó billentyűzet</string>
+    <string name="command_description_table_flip">(╯°□°)╯︵ ┻━┻ -t tesz a szöveg elejére</string>
+    <string name="attachment_type_voice_broadcast">Hang közvetítés</string>
+    <string name="push_gateway_item_enabled">Engedélyezve:</string>
+    <string name="push_gateway_item_device_id">Munkamenet azon.:</string>
+    <string name="error_check_network">Valami nem sikerült. Kérlek ellenőrizd a hálózati kapcsolatot és próbáld újra.</string>
+    <string name="command_description_devtools">A fejlesztői eszközök képernyő megnyitása</string>
+    <string name="room_settings_global_block_unverified_info_text">🔒 Bekapcsoltad a Biztonsági beállításoknál, hogy csak ellenőrzött munkamenetek számára legyen titkosítva az üzenet bármely szobában.</string>
+    <string name="some_devices_will_not_be_able_to_decrypt">⚠ Ellenőrizetlen eszközök vannak a szobában, ezek nem fogják tudni visszafejteni az általad küldött üzeneteket.</string>
+    <string name="encryption_never_send_to_unverified_devices_in_room">Sose küldj titkosított üzenetet ellenőrizetlen munkamenetbe ebből a munkamenetből ebben a szobában.</string>
+    <string name="grant_permission">Engedély megadása</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} alkalmazásnak értesítések megjelenítéséhez engedélyre van szüksége.
+\nKérjük, adj rá engedélyt.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} alkalmazásnak szüksége van engedélyre az értesítések megjelenítéséhez. Az értesítés megjelenítheti az üzenetet, meghívót, stb.
+\n
+\nA következő felugró ablakban adj rá engedélyt, hogy az értesítések megjelenhessenek.</string>
+    <string name="labs_enable_rich_text_editor_summary">Próbálja ki az új szövegbevitelt (hamarosan érkezik a sima szöveges üzemmód)</string>
+    <string name="labs_enable_rich_text_editor_title">Vizuális szerkesztő engedélyezése</string>
+    <string name="action_got_it">Értem</string>
+</resources>
\ No newline at end of file

From 0551cf9945d05363823d28c78bce4b4d642f01b5 Mon Sep 17 00:00:00 2001
From: Linerly <linerly@protonmail.com>
Date: Sat, 15 Oct 2022 01:59:07 +0000
Subject: [PATCH 254/400] Translated using Weblate (Indonesian)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/id/
---
 .../src/main/res/values-in/strings.xml        | 42 ++++++++++++++++---
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-in/strings.xml b/library/ui-strings/src/main/res/values-in/strings.xml
index 7a389a3fca..137805f8b0 100644
--- a/library/ui-strings/src/main/res/values-in/strings.xml
+++ b/library/ui-strings/src/main/res/values-in/strings.xml
@@ -1240,10 +1240,10 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="preference_voice_and_video">Suara &amp; Video</string>
     <string name="push_gateway_item_format">Format:</string>
     <string name="push_gateway_item_url">Url:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
+    <string name="push_gateway_item_device_name">Nama Tampilan Sesi:</string>
+    <string name="push_gateway_item_app_display_name">Nama Tampilan Aplikasi:</string>
+    <string name="push_gateway_item_push_key">Kunci Dorongan:</string>
+    <string name="push_gateway_item_app_id">ID Aplikasi:</string>
     <string name="settings_push_gateway_no_pushers">Tidak ada gateway dorong terdaftar</string>
     <string name="settings_push_rules_no_rules">Tidak ada aturan push yang ditentukan</string>
     <string name="settings_push_rules">Aturan Push</string>
@@ -1632,7 +1632,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="login_set_email_submit">Lanjut</string>
     <string name="login_set_email_optional_hint">Email (opsional)</string>
     <string name="login_set_email_mandatory_hint">Email</string>
-    <string name="login_set_email_notice">Atur sebuah alamat email untuk memulihkan akun Anda. Nantinya, Anda dapat mengizinkan orang yang Anda tahu untuk menemukan Anda dari email secara opsional.</string>
+    <string name="login_set_email_notice">Atur sebuah alamat email untuk memulihkan akun Anda. Nantinya, Anda dapat mengizinkan orang yang Anda tahu untuk menemukan Anda dari email ini secara opsional.</string>
     <string name="login_set_email_title">Atur alamat email</string>
     <string name="login_reset_password_cancel_confirmation_content">Kata sandi Anda belum diubah.
 \n
@@ -2690,4 +2690,34 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Ada perangkat yang belum diverifikasi di ruangan ini, mereka tidak akan mendekripsikan pesan yang Anda kirim.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Jangan kirim pesan terenkripsi ke sesi yang belum diverifikasi di ruangan ini.</string>
     <string name="action_got_it">Saya mengerti</string>
-</resources>
+    <string name="rich_text_editor_format_underline">Terapkan format garis bawah</string>
+    <string name="rich_text_editor_format_strikethrough">Terapkan format coret</string>
+    <string name="rich_text_editor_format_italic">Terapkan format miring</string>
+    <string name="rich_text_editor_format_bold">Terapkan format tebal</string>
+    <string name="labs_enable_client_info_recording_summary">Rekam nama klien, versi, dan URL untuk lebih mudah mengenal sesi di pengelola sesi.</string>
+    <string name="labs_enable_client_info_recording_title">Aktifkan perekaman info klien</string>
+    <string name="labs_enable_session_manager_summary">Miliki keterlihatan dan kendali yang lebih baik pada semua sesi Anda.</string>
+    <string name="labs_enable_session_manager_title">Aktifkan pengelola sesi baru</string>
+    <string name="device_manager_session_details_device_operating_system">Sistem operasi</string>
+    <string name="device_manager_session_details_device_model">Model</string>
+    <string name="device_manager_session_details_device_browser">Peramban</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Versi</string>
+    <string name="device_manager_session_details_application_name">Nama</string>
+    <string name="device_manager_session_details_application">Aplikasi</string>
+    <string name="device_manager_push_notifications_description">Terima notifikasi dorongan di sesi ini.</string>
+    <string name="device_manager_push_notifications_title">Notifikasi dorongan</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Verifikasi sesi Anda saat ini untuk menampilkan status verifikasi sesi ini.</string>
+    <string name="device_manager_verification_status_unknown">Status verifikasi tidak diketahui</string>
+    <string name="push_gateway_item_enabled">Diaktifkan:</string>
+    <string name="push_gateway_item_device_id">ID Sesi:</string>
+    <string name="error_check_network">Ada sesuatu yang salah. Mohon periksa koneksi jaringan Anda dan coba lagi.</string>
+    <string name="grant_permission">Berikan Izin</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} membutuhkan izin untuk menampilkan notifikasi.
+\nMohon berikan izin itu.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} membutuhkan izin untuk menampilkan notifikasi. Notifikasi dapat menampilkan pesan Anda, undangan Anda, dll.
+\n
+\nMohon perbolehkan akses di munculan berikutnya untuk dapat melihat notifikasi.</string>
+    <string name="labs_enable_rich_text_editor_summary">Coba editor teks kaya (mode teks biasa akan datang)</string>
+    <string name="labs_enable_rich_text_editor_title">Aktifkan editor teks kaya</string>
+</resources>
\ No newline at end of file

From 5a9032b45cba1178405cd63b821db5ba8a8bc1b5 Mon Sep 17 00:00:00 2001
From: random <dictionary@tutamail.com>
Date: Mon, 17 Oct 2022 10:43:33 +0000
Subject: [PATCH 255/400] Translated using Weblate (Italian)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/it/
---
 .../src/main/res/values-it/strings.xml        | 40 ++++++++++++++++---
 1 file changed, 35 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-it/strings.xml b/library/ui-strings/src/main/res/values-it/strings.xml
index f65aca5bb7..4bd4a5401c 100644
--- a/library/ui-strings/src/main/res/values-it/strings.xml
+++ b/library/ui-strings/src/main/res/values-it/strings.xml
@@ -889,10 +889,10 @@
     <string name="settings_push_rules">Regole di push</string>
     <string name="settings_push_rules_no_rules">Nessuna regola di push definita</string>
     <string name="settings_push_gateway_no_pushers">Nessun gateway di push registrato</string>
-    <string name="push_gateway_item_app_id">id_app:</string>
-    <string name="push_gateway_item_push_key">chiave_push:</string>
-    <string name="push_gateway_item_app_display_name">nome_visualizzato_app:</string>
-    <string name="push_gateway_item_device_name">nome_sessione:</string>
+    <string name="push_gateway_item_app_id">ID app:</string>
+    <string name="push_gateway_item_push_key">Chiave push:</string>
+    <string name="push_gateway_item_app_display_name">Nome mostrato app:</string>
+    <string name="push_gateway_item_device_name">Nome mostrato sessione:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Formato:</string>
     <string name="preference_voice_and_video">Audio e Video</string>
@@ -2733,4 +2733,34 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Ci sono dispositivi non verificati in questa stanza, non potranno decifrare i messaggi che invii.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Non inviare mai messaggi cifrati a sessioni non verificate in questa stanza.</string>
     <string name="action_got_it">Capito</string>
-</resources>
+    <string name="rich_text_editor_format_underline">Applica formato sottolineato</string>
+    <string name="rich_text_editor_format_strikethrough">Applica formato sbarrato</string>
+    <string name="rich_text_editor_format_italic">Applica formato corsivo</string>
+    <string name="rich_text_editor_format_bold">Applica formato grassetto</string>
+    <string name="labs_enable_client_info_recording_summary">Registra il nome, la versione e l\'url del client per riconoscere le sessioni più facilmente nel gestore di sessioni.</string>
+    <string name="labs_enable_client_info_recording_title">Attiva registrazione info client</string>
+    <string name="labs_enable_session_manager_summary">Maggiore visibilità e controllo su tutte le tue sessioni.</string>
+    <string name="labs_enable_session_manager_title">Attiva il nuovo gestore di sessioni</string>
+    <string name="device_manager_session_details_device_operating_system">Sistema operativo</string>
+    <string name="device_manager_session_details_device_model">Modello</string>
+    <string name="device_manager_session_details_device_browser">Browser</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Versione</string>
+    <string name="device_manager_session_details_application_name">Nome</string>
+    <string name="device_manager_session_details_application">Applicazione</string>
+    <string name="device_manager_push_notifications_description">Ricevi notifiche push in questa sessione.</string>
+    <string name="device_manager_push_notifications_title">Notifiche push</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Verifica l\'attuale sessione per rivelare lo stato di verifica di questa sessione.</string>
+    <string name="device_manager_verification_status_unknown">Stato di verifica sconosciuto</string>
+    <string name="push_gateway_item_enabled">Attivato:</string>
+    <string name="push_gateway_item_device_id">ID sessione:</string>
+    <string name="error_check_network">Qualcosa è andato storto. Controlla la tua connessione di rete e riprova.</string>
+    <string name="grant_permission">Concedi l\'autorizzazione</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} chiede l\'autorizzazione per mostrare notifiche.
+\nConcedi l\'autorizzazione.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} chiede l\'autorizzazione per mostrare notifiche. Le notifiche possono mostrare i messaggi, gli inviti, ecc.
+\n
+\nConsenti l\'accesso nelle prossime schermate per potere vedere la notifica.</string>
+    <string name="labs_enable_rich_text_editor_summary">Prova l\'editor in rich text (il testo semplice è in arrivo)</string>
+    <string name="labs_enable_rich_text_editor_title">Attiva editor in rich text</string>
+</resources>
\ No newline at end of file

From b0470dce6a5aae8deb745729fabcb675514931b7 Mon Sep 17 00:00:00 2001
From: lvre <7uu3qrbvm@relay.firefox.com>
Date: Sat, 15 Oct 2022 01:55:43 +0000
Subject: [PATCH 256/400] Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/
---
 .../src/main/res/values-pt-rBR/strings.xml    | 40 ++++++++++++++++---
 1 file changed, 35 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
index e0d6e6aa86..7a709757c9 100644
--- a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
+++ b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
@@ -1007,10 +1007,10 @@
     <string name="settings_push_rules">Regras de Push</string>
     <string name="settings_push_rules_no_rules">Nenhuma regra de push definida</string>
     <string name="settings_push_gateway_no_pushers">Nenhum gateway de push registrado</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_id">ID do App:</string>
+    <string name="push_gateway_item_push_key">Chave Push:</string>
+    <string name="push_gateway_item_app_display_name">Nome de Exibição do App:</string>
+    <string name="push_gateway_item_device_name">Nome de Exibição da Sessão:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Formato:</string>
     <string name="preference_voice_and_video">Voz &amp; Vídeo</string>
@@ -2742,4 +2742,34 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ Existem dispositivos não-verificados nesta sala, eles não vão ser capazes de decriptar mensagens que você enviar.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Nunca enviar mensagens encriptadas a sessões não-verificadas nesta sala.</string>
     <string name="action_got_it">Entendido</string>
-</resources>
+    <string name="rich_text_editor_format_strikethrough">Aplicar formato tachar</string>
+    <string name="rich_text_editor_format_underline">Aplicar formato sublinhar</string>
+    <string name="rich_text_editor_format_italic">Aplicar formato itálico</string>
+    <string name="rich_text_editor_format_bold">Aplicar formato negrito</string>
+    <string name="labs_enable_client_info_recording_summary">Gravar o nome de cliente, versão, e url para reconhecer sessões mais facilmente em gerenciador de sessão.</string>
+    <string name="labs_enable_client_info_recording_title">Habilitar gravação de info de cliente</string>
+    <string name="labs_enable_session_manager_summary">Tenha visibilidade e controle maiores sobre todas suas sessões.</string>
+    <string name="labs_enable_session_manager_title">Habilitar novo gerenciador de sessão</string>
+    <string name="device_manager_session_details_device_operating_system">Sistema operativo</string>
+    <string name="device_manager_session_details_device_model">Modelo</string>
+    <string name="device_manager_session_details_device_browser">Browser</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Versão</string>
+    <string name="device_manager_session_details_application_name">Nome</string>
+    <string name="device_manager_session_details_application">Aplicativo</string>
+    <string name="device_manager_push_notifications_description">Receber notificações push nesta sessão.</string>
+    <string name="device_manager_push_notifications_title">Notificações push</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Verifique sua sessão atual para revelar o status de verificação desta sessão.</string>
+    <string name="device_manager_verification_status_unknown">Status de verificação desconhecido</string>
+    <string name="push_gateway_item_enabled">Habilitado:</string>
+    <string name="push_gateway_item_device_id">ID da Sessão:</string>
+    <string name="error_check_network">Algo deu errado. Por favor cheque sua conexão de rede e tente de novo.</string>
+    <string name="grant_permission">Conceder Permissão</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} precisa de permissão para mostrar notificações.
+\nPor favor conceda a permissão.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} precisa de permissão para exibir notificações. Notificações podem exibir suas mensagens, seus convites, etc.
+\n
+\nPor favor permita acesso nos próximos pop-ups para ser capaz de visualizar notificação.</string>
+    <string name="labs_enable_rich_text_editor_summary">Experimente o editor de texto rico (modo de texto puro vindo em breve)</string>
+    <string name="labs_enable_rich_text_editor_title">Habilitar editor de texto rico</string>
+</resources>
\ No newline at end of file

From 440e3be7394a0387abdb03697f026758ad5948d0 Mon Sep 17 00:00:00 2001
From: Nui Harime <harime.nui@yandex.ru>
Date: Mon, 17 Oct 2022 10:19:05 +0000
Subject: [PATCH 257/400] Translated using Weblate (Russian)

Currently translated at 96.7% (2389 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ru/
---
 .../src/main/res/values-ru/strings.xml        | 96 +++++++++++--------
 1 file changed, 54 insertions(+), 42 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml
index 8d19e8d9d0..1953e46698 100644
--- a/library/ui-strings/src/main/res/values-ru/strings.xml
+++ b/library/ui-strings/src/main/res/values-ru/strings.xml
@@ -398,8 +398,8 @@
     <string name="settings_pin_missed_notifications">Прикрепить комнаты с отключенными уведомлениями</string>
     <string name="settings_pin_unread_messages">Прикрепить комнаты с непрочитанными сообщениями</string>
     <string name="devices_details_id_title">ID</string>
-    <string name="devices_details_name_title">Публичное имя</string>
-    <string name="devices_details_device_name">Обновить публичное имя</string>
+    <string name="devices_details_name_title">Публичное название</string>
+    <string name="devices_details_device_name">Обновить публичное название</string>
     <string name="devices_details_last_seen_title">Недавно</string>
     <string name="devices_details_last_seen_format">%1$s @ %2$s</string>
     <string name="devices_delete_dialog_title">Аутентификация</string>
@@ -431,7 +431,7 @@
     <string name="room_settings_set_main_address">Установить как основной адрес</string>
     <string name="room_settings_unset_main_address">Сбросить основной адрес</string>
     <string name="encryption_information_decryption_error">Ошибка дешифровки</string>
-    <string name="encryption_information_device_name">Публичное имя</string>
+    <string name="encryption_information_device_name">Публичное название</string>
     <string name="device_manager_session_details_session_id">ID сессии</string>
     <string name="encryption_information_device_key">Ключ сессии</string>
     <string name="encryption_export_e2e_room_keys">Экспорт E2E ключей комнаты</string>
@@ -727,21 +727,21 @@
     <string name="notification_silent">Беззвучный</string>
     <string name="passphrase_empty_error_message">Пожалуйста, введите мнемоническую фразу</string>
     <string name="passphrase_passphrase_too_weak">Парольная фраза слишком простая</string>
-    <string name="keys_backup_passphrase_not_empty_error_message">Пожалуйста, удалите мнемоническую фразу, если хотите, чтобы ${app_name} сгенерировал ключ восстановления.</string>
+    <string name="keys_backup_passphrase_not_empty_error_message">Пожалуйста, удалите мнемоническую фразу, если хотите, чтобы ${app_name} сгенерировал бумажный ключ.</string>
     <string name="keys_backup_setup_step1_title">Никогда не теряйте зашифрованных сообщений</string>
     <string name="keys_backup_setup_step1_description">Сообщения в зашифрованных комнатах защищены сквозным шифрованием. Ключи для прочтения этих сообщений есть только у вас и получателя(ей).
 \n
 \nНадёжно сохраните резервную копию ключей, чтобы не потерять их.</string>
     <string name="keys_backup_setup_step2_button_title">Установите парольную фразу</string>
-    <string name="keys_backup_setup_step3_copy_button_title">Сохраните ключ восстановления</string>
+    <string name="keys_backup_setup_step3_copy_button_title">Сохранить бумажный ключ</string>
     <string name="keys_backup_setup_step3_button_title">Готово</string>
     <string name="keys_backup_setup_step3_save_button_title">Сохранить как файл</string>
     <string name="keys_backup_setup_step3_please_make_copy">Пожалуйста, сделайте копию</string>
-    <string name="keys_backup_setup_step3_share_intent_chooser_title">Поделиться ключом восстановления с…</string>
-    <string name="recovery_key">Ключ для восстановления</string>
+    <string name="keys_backup_setup_step3_share_intent_chooser_title">Поделиться бумажным ключом с…</string>
+    <string name="recovery_key">Бумажный ключ</string>
     <string name="unexpected_error">Непредвиденная ошибка</string>
     <string name="keys_backup_setup_skip_title">Уверены?</string>
-    <string name="keys_backup_settings_delete_confirm_message">Удалить резервную копию ключей шифрования с сервера? Вы больше не сможете использовать ключ восстановления для чтения истории зашифрованных сообщений.</string>
+    <string name="keys_backup_settings_delete_confirm_message">Удалить резервную копию ключей шифрования с сервера\? Вы больше не сможете использовать бумажный ключ для чтения истории зашифрованных сообщений.</string>
     <string name="keys_backup_settings_delete_confirm_title">Удалить резервную копию</string>
     <string name="keys_backup_settings_deleting_backup">Удаление резервной копии…</string>
     <string name="keys_backup_settings_untrusted_backup">Чтобы использовать резервную копию ключа в этой сессии, восстановите его с помощью своей парольной фразы или ключа восстановления.</string>
@@ -755,17 +755,17 @@
     <string name="keys_backup_settings_status_ok">Резервное копирование ключей успешно настроено для этой сессии.</string>
     <string name="keys_backup_settings_delete_backup_button">Удалить резервную копию</string>
     <string name="keys_backup_settings_restore_backup_button">Восстановить из резервной копии</string>
-    <string name="keys_backup_recovery_code_empty_error_message">Пожалуйста, введите ключ восстановления</string>
+    <string name="keys_backup_recovery_code_empty_error_message">Пожалуйста, введите бумажный ключ</string>
     <string name="keys_backup_unlock_button">Разблокировать историю</string>
     <string name="keys_backup_restoring_waiting_message">Восстановление резервной копии:</string>
-    <string name="keys_backup_restore_key_enter_hint">Введите ключ восстановления</string>
-    <string name="keys_backup_restore_with_recovery_key">Используйте ключ восстановления для разблокировки истории зашифрованных сообщений</string>
-    <string name="keys_backup_restore_with_passphrase_helper_with_link">Если вы не знаете вашу парольную фразу для восстановления, вы можете %s.</string>
-    <string name="keys_backup_restore_use_recovery_key">используйте ключ восстановления</string>
+    <string name="keys_backup_restore_key_enter_hint">Введите бумажный ключ</string>
+    <string name="keys_backup_restore_with_recovery_key">Используйте бумажный ключ для разблокировки зашифрованных сообщений</string>
+    <string name="keys_backup_restore_with_passphrase_helper_with_link">Если забыли свою мнемоническую фразу, вы можете %s.</string>
+    <string name="keys_backup_restore_use_recovery_key">используйте бумажный ключ</string>
     <string name="keys_backup_setup_skip_msg">Вы можете потерять доступ к сообщениям, если выйдете из системы или потеряете это устройство.</string>
     <string name="keys_backup_restore_is_getting_backup_version">Получение версии резервной копии…</string>
-    <string name="keys_backup_restore_with_passphrase">Используйте парольную фразу для разблокировки истории зашифрованных сообщений</string>
-    <string name="keys_backup_restore_with_key_helper">Потеряли ключ восстановления? В настройках вы можете создать новый.</string>
+    <string name="keys_backup_restore_with_passphrase">Используйте мнемоническую фразу для разблокировки зашифрованных сообщений</string>
+    <string name="keys_backup_restore_with_key_helper">Потеряли бумажный ключ\? В настройках вы можете создать новый.</string>
     <string name="keys_backup_restore_success_title">Резервная копия восстановлена %s !</string>
     <string name="keys_backup_settings_invalid_signature_from_unverified_device">Резервная копия имеет недействительную подпись из неподтвержденной сессии %s</string>
     <string name="keys_backup_get_version_error">Не удалось получить последнюю версию ключей восстановления (%s).</string>
@@ -780,9 +780,9 @@
         <item quantity="few">Восстановлены резервные копии с %d ключами.</item>
         <item quantity="many">Восстановлены резервные копии с %d ключами.</item>
     </plurals>
-    <string name="keys_backup_recovery_code_error_decrypt">Невозможно расшифровать резервную копию с помощью этого ключа восстановления: убедитесь, что вы ввели правильный ключ.</string>
-    <string name="keys_backup_passphrase_error_decrypt">Невозможно расшифровать резервную копию с помощью этого пароля: убедитесь, что вы ввели правильный пароль.</string>
-    <string name="keys_backup_setup_step3_generating_key_status">Генерация ключей восстановления с использованием парольной фразы может занять несколько секунд.</string>
+    <string name="keys_backup_recovery_code_error_decrypt">Невозможно расшифровать резервную копию с помощью этого бумажного ключа: пожалуйста, убедитесь, что вы ввели правильный бумажный ключ.</string>
+    <string name="keys_backup_passphrase_error_decrypt">Невозможно расшифровать резервную копию с помощью этой мнемонической фразы: пожалуйста, убедитесь, что вы ввели правильную мнемоническую фразу.</string>
+    <string name="keys_backup_setup_step3_generating_key_status">Генерация бумажного ключа с использованием мнемонической фразы может занять несколько секунд.</string>
     <string name="settings_troubleshoot_test_fcm_failed_account_missing">[%1$s]
 \nЭта ошибка вне контроля ${app_name}. На телефоне нет учетной записи Google. Пожалуйста, добавьте аккаунт Google.</string>
     <string name="settings_troubleshoot_test_fcm_failed_service_not_available">[%1$s]
@@ -816,7 +816,7 @@
     <string name="keys_backup_banner_recover_line1">Никогда не теряйте зашифрованные сообщения</string>
     <string name="keys_backup_setup_step3_share_recovery_file">Поделиться</string>
     <string name="keys_backup_setup_step3_button_title_no_passphrase">Я сделал(а) копию</string>
-    <string name="keys_backup_setup_step3_text_line2_no_passphrase">Храните ключ восстановления в надежном месте, например, в диспетчере паролей (или в сейфе)</string>
+    <string name="keys_backup_setup_step3_text_line2_no_passphrase">Храните бумажный ключ в очень надёжном месте, например, в менеджере паролей (или в сейфе)</string>
     <string name="keys_backup_setup_step2_text_title">Защитите резервную копию мнемонической фразой.</string>
     <string name="encryption_message_recovery">Восстановление зашифрованных сообщений</string>
     <string name="keys_backup_setup">Начать использовать резервное копирование ключей</string>
@@ -825,16 +825,16 @@
     <string name="keys_backup_banner_update_line1">Новые ключи зашифрованных сообщений</string>
     <string name="keys_backup_setup_step3_text_line1">Ваши ключи копируются.</string>
     <string name="keys_backup_setup_step2_skip_button_title">(Дополнительно) Настройка с ключом восстановления</string>
-    <string name="keys_backup_setup_step1_recovery_key_alternative">Или защитите резервную копию с помощью ключа восстановления, сохранив его в безопасном месте.</string>
+    <string name="keys_backup_setup_step1_recovery_key_alternative">Или защитите резервную копию бумажным ключом, сохранив его в надёжном месте.</string>
     <string name="sign_out_bottom_sheet_warning_backup_not_active">Безопасная резервная копия ключей должна быть активирована на всех ваших сессиях, чтобы не потерять доступ к зашифрованным сообщениям.</string>
-    <string name="keys_backup_setup_step2_text_description">Зашифрованная копия ключей будет храниться на вашем сервере. Для безопасности защитите её парольной фразой.
+    <string name="keys_backup_setup_step2_text_description">Зашифрованная копия ключей будет храниться на вашем сервере. Для безопасности защитите её мнемонической фразой.
 \n
-\nДля максимальной безопасности парольная фраза должна отличаться от пароля вашей учётной записи.</string>
+\nДля максимальной безопасности мнемоническая фраза должна отличаться от пароля вашей учётной записи.</string>
     <string name="keys_backup_setup_step3_text_line2">Ключ восстановления — это страховка, вы можете использовать его для восстановления доступа к вашим зашифрованным сообщениям, если забудете вашу парольную фразу. 
 \nХраните ключ восстановления в надёжном месте, например, в диспетчере паролей (или в сейфе)</string>
     <string name="keys_backup_restoring_importing_keys_waiting_message">Импортирование ключей…</string>
     <string name="keys_backup_restoring_downloading_backup_waiting_message">Скачивание ключей…</string>
-    <string name="keys_backup_restoring_computing_key_waiting_message">Вычисление ключа восстановления…</string>
+    <string name="keys_backup_restoring_computing_key_waiting_message">Вычисление бумажного ключа…</string>
     <string name="action_ignore">Игнорировать</string>
     <string name="action_mark_room_read">Отметить как прочитанное</string>
     <string name="auth_login_sso">Войти с помощью единого входа</string>
@@ -929,10 +929,10 @@
     <string name="settings_preferences">Предпочтения</string>
     <string name="settings_security_and_privacy">Безопасность</string>
     <string name="settings_push_rules">Правила push-уведомлений</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
+    <string name="push_gateway_item_app_id">ID приложения:</string>
     <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_display_name">Отображаемое название приложения:</string>
+    <string name="push_gateway_item_device_name">Отображаемое название сессии:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Формат:</string>
     <string name="preference_voice_and_video">Голос и видео</string>
@@ -1219,10 +1219,10 @@
     <string name="room_profile_section_more">Ещё</string>
     <string name="a11y_qr_code_for_verification">QR-код</string>
     <string name="no_connectivity_to_the_server_indicator">Соединение с сервером потеряно</string>
-    <string name="verification_cannot_access_other_session">Используйте пароль восстановления или ключ</string>
+    <string name="verification_cannot_access_other_session">Используйте мнемоническую фразу или бумажный ключ</string>
     <string name="e2e_use_keybackup">Разблокировать историю зашифрованных сообщений</string>
     <string name="verify_cancelled_notice">Проверка была отменена. Вы можете начать проверку снова.</string>
-    <string name="recovery_passphrase">Мнемоническая фраза для восстановления</string>
+    <string name="recovery_passphrase">Мнемоническая фраза</string>
     <string name="enter_account_password">Введите %s, чтобы продолжить.</string>
     <string name="bootstrap_dont_reuse_pwd">Не переиспользуйте пароль учётной записи.</string>
     <string name="bootstrap_loading_text">Это может занять несколько секунд, пожалуйста, наберитесь терпения.</string>
@@ -1314,7 +1314,7 @@
     <string name="settings_secure_backup_reset">Сброс безопасного резервного копирования</string>
     <string name="settings_secure_backup_enter_to_setup">Настроить на этом устройстве</string>
     <string name="settings_secure_backup_section_info">Защитите себя от потери доступа к зашифрованным сообщениям и данным, создав резервные копии ключей шифрования на вашем сервере.</string>
-    <string name="reset_secure_backup_title">Создайте новый ключ безопасности или задайте новую секретную фразу для существующей резервной копии.</string>
+    <string name="reset_secure_backup_title">Создайте новый бумажный ключ или задайте новую мнемоническую фразу для существующей резервной копии.</string>
     <string name="reset_secure_backup_warning">Это заменит ваш текущий ключ или фразу.</string>
     <string name="disabled_integration_dialog_title">Интеграции отключены</string>
     <string name="disabled_integration_dialog_content">Включите «Управление интеграциями» в настройках, чтобы сделать это.</string>
@@ -1326,7 +1326,7 @@
     <string name="encryption_exported_successfully">Ключи успешно экспортированы</string>
     <string name="active_widget_view_action">ОБЗОР</string>
     <string name="active_widgets_title">Активные виджеты</string>
-    <string name="recovery_key_export_saved">Ключ восстановления был сохранён.</string>
+    <string name="recovery_key_export_saved">Бумажный ключ сохранён.</string>
     <string name="secure_backup_banner_setup_line1">Безопасное резервное копирование</string>
     <string name="secure_backup_banner_setup_line2">Защита от потери доступа к зашифрованным сообщениям и данным</string>
     <string name="secure_backup_setup">Настроить безопасное резервное копирование</string>
@@ -1553,12 +1553,12 @@
     <string name="auth_invalid_login_deactivated_account">Эта учётная запись была деактивирована.</string>
     <string name="bootstrap_enter_recovery">Введите %s, чтобы продолжить</string>
     <string name="use_file">Использовать файл</string>
-    <string name="bootstrap_invalid_recovery_key">Это недействительный ключ восстановления</string>
-    <string name="recovery_key_empty_error_message">Пожалуйста, введите ключ восстановления</string>
+    <string name="bootstrap_invalid_recovery_key">Этот бумажный ключ недействителен</string>
+    <string name="recovery_key_empty_error_message">Пожалуйста, введите бумажный ключ</string>
     <string name="bootstrap_progress_checking_backup">Проверка ключа резервного копирования</string>
     <string name="bootstrap_progress_checking_backup_with_info">Проверка ключа резервного копирования (%s)</string>
     <string name="bootstrap_progress_compute_curve_key">Получение кривой ключа</string>
-    <string name="bootstrap_progress_generating_ssss_recovery">Генерация ключа SSSS из ключа восстановления</string>
+    <string name="bootstrap_progress_generating_ssss_recovery">Генерация ключа SSSS из бумажного ключа</string>
     <string name="bootstrap_progress_storing_in_sss">Сохранение резервной копии ключа в SSSS</string>
     <string name="bootstrap_migration_use_recovery_key">используйте ваш ключ восстановления ключа резервной копии</string>
     <string name="bootstrap_migration_backup_recovery_key">Ключ восстановления ключа резервной копии</string>
@@ -1571,7 +1571,7 @@
 \n${app_name} для Android</string>
     <string name="or_other_mx_capable_client">или другой клиент Matrix поддерживающий перекрестную подпись</string>
     <string name="command_description_discard_session">Принудительно отбрасывает текущую групповую сессию для отправки сообщений в зашифрованную комнату</string>
-    <string name="enter_secret_storage_passphrase_or_key">Чтобы продолжить, используйте ваш %1$s или используйте ваш %2$s.</string>
+    <string name="enter_secret_storage_passphrase_or_key">Чтобы продолжить, используйте %1$s или %2$s.</string>
     <string name="use_recovery_key">Используйте ключ восстановления</string>
     <string name="enter_secret_storage_input_key">Выберите ключ восстановления или введите его вручную, введя или вставив из буфера обмена</string>
     <string name="failed_to_access_secure_storage">Не удалось получить доступ к защищенному хранилищу данных</string>
@@ -1624,13 +1624,13 @@
     <string name="bottom_sheet_setup_secure_backup_submit">Настроить</string>
     <string name="bottom_sheet_setup_secure_backup_security_key_title">Используйте ключ безопасности</string>
     <string name="bottom_sheet_setup_secure_backup_security_key_subtitle">Создайте ключ безопасности для хранения в надежном месте, например в менеджере паролей или сейфе.</string>
-    <string name="bottom_sheet_setup_secure_backup_security_phrase_title">Использовать секретную фразу</string>
+    <string name="bottom_sheet_setup_secure_backup_security_phrase_title">Использовать мнемоническую фразу</string>
     <string name="bottom_sheet_setup_secure_backup_security_phrase_subtitle">Введите секретную фразу, известную только вам, и создайте ключ для резервного копирования.</string>
     <string name="bottom_sheet_save_your_recovery_key_title">Сохраните свой ключ безопасности</string>
-    <string name="bottom_sheet_save_your_recovery_key_content">Храните ключ безопасности в надежном месте, например в менеджере паролей или сейфе.</string>
+    <string name="bottom_sheet_save_your_recovery_key_content">Храните бумажный ключ в надёжном месте, например, в менеджере паролей или в сейфе.</string>
     <string name="set_a_security_phrase_title">Задайте секретную фразу</string>
     <string name="set_a_security_phrase_notice">Введите секретную фразу, известную только вам, для защиты данных на вашем сервере.</string>
-    <string name="set_a_security_phrase_hint">Секретная фраза</string>
+    <string name="set_a_security_phrase_hint">Мнемоническая фраза</string>
     <string name="set_a_security_phrase_again_notice">Для подтверждения введите вашу секретную фразу ещё раз.</string>
     <string name="room_settings_name_hint">Название комнаты</string>
     <string name="room_settings_topic_hint">Тема</string>
@@ -1646,7 +1646,7 @@
     <string name="disclaimer_content">Мы рады сообщить, что сменили имя! Ваше приложение обновлено, и вы вошли в свою учетную запись.</string>
     <string name="disclaimer_negative_button">ПОНЯТНО</string>
     <string name="disclaimer_positive_button">УЗНАТЬ БОЛЬШЕ</string>
-    <string name="save_recovery_key_chooser_hint">Сохранить ключ восстановления в</string>
+    <string name="save_recovery_key_chooser_hint">Сохранить бумажный ключ в</string>
     <string name="loading_contact_book">Получаем ваши контакты…</string>
     <string name="empty_contact_book">Ваша контактная книга пуста</string>
     <string name="contacts_book_title">Книга контактов</string>
@@ -1706,7 +1706,7 @@
     <string name="settings_remove_three_pid_confirmation_content">Удалить %s\?</string>
     <string name="error_threepid_auth_failed">Убедитесь, что вы перешли по ссылке в электронном письме, которое мы вам отправили.</string>
     <string name="settings_emails_and_phone_numbers_title">Электронная почта и номера телефонов</string>
-    <string name="settings_emails_and_phone_numbers_summary">Управляйте электронной почтой и номерами телефонов, привязанными к вашей учетной записи Matrix</string>
+    <string name="settings_emails_and_phone_numbers_summary">Управляйте адресами электронной почты и номерами телефонов, привязанными к вашей учётной записи Matrix</string>
     <string name="settings_text_message_sent_hint">Код</string>
     <string name="login_msisdn_notice">Используйте международный формат (номер телефона должен начинаться с \'+\')</string>
     <string name="confirm_your_identity_quad_s">Подтвердите свою личность, проверив этот логин, предоставив ему доступ к зашифрованным сообщениям.</string>
@@ -2633,7 +2633,7 @@
     <string name="ftue_auth_sign_in_choose_server_header">Где хранятся ваши переписки</string>
     <string name="ftue_auth_create_account_choose_server_header">Где будут храниться ваши переписки</string>
     <string name="ftue_auth_create_account_password_entry_footer">Должно быть 8 или более символов</string>
-    <string name="crosssigning_cannot_verify_this_session">Не удалось подтвердить это устройство</string>
+    <string name="crosssigning_cannot_verify_this_session">Не удалось подтвердить эту сессию</string>
     <string name="permalink_unsupported_groups">Невозможно открыть эту ссылку: сообщества были заменены пространствами</string>
     <string name="ftue_auth_login_username_entry">Имя пользователя / Почта / Телефон</string>
     <string name="ftue_auth_password_reset_email_confirmation_subtitle">Следуйте инструкциям, отправленным на %s</string>
@@ -2758,11 +2758,23 @@
     <string name="action_got_it">Понятно</string>
     <string name="room_settings_global_block_unverified_info_text">🔒 В настройках безопасности вы включили шифрование только для заверенных сессий во всех комнатах.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Не отправлять зашифрованные сообщения незаверенным сессиям в этой комнате.</string>
-    <string name="device_manager_learn_more_sessions_inactive">Неактивные сессии — это сессии, которыми вы не пользовались определенное время, но они продолжают получать ключи шифрования.
+    <string name="device_manager_learn_more_sessions_inactive">Неактивные сессии — это сессии, которыми вы не пользовались определённое время, но они продолжают получать ключи шифрования.
 \n
 \nУдаление неактивных сессий повышает безопасность и производительность, а также облегчает выявление подозрительных новых сессий.</string>
     <string name="device_manager_learn_more_session_rename_title">Переименование сессий</string>
     <string name="device_manager_learn_more_session_rename">Другие пользователи в личных сообщениях и комнатах, к которым вы присоединились, могут просматривать весь список ваших сессий.
 \n
 \nЭто даёт им уверенность в том, что они действительно общаются с вами, но это также означает, что они могут видеть название сессии, которое вы ввели здесь.</string>
-</resources>
+    <string name="labs_enable_rich_text_editor_title">Визуальный редактор текста</string>
+    <string name="push_gateway_item_device_id">ID сессии:</string>
+    <string name="device_manager_push_notifications_title">Уведомления</string>
+    <string name="device_manager_push_notifications_description">Получать push-уведомления в этой сессии.</string>
+    <string name="device_manager_session_details_application_url">URL-адрес</string>
+    <string name="device_manager_session_details_application">Приложение</string>
+    <string name="device_manager_session_details_application_name">Название</string>
+    <string name="device_manager_session_details_application_version">Версия</string>
+    <string name="device_manager_session_details_device_browser">Веб-браузер</string>
+    <string name="device_manager_session_details_device_model">Модель</string>
+    <string name="device_manager_session_details_device_operating_system">Операционная система</string>
+    <string name="labs_enable_session_manager_title">Новый менеджер сессий</string>
+</resources>
\ No newline at end of file

From a1a9d8f150fb1917477a92fc32c7e82db3ba1dad Mon Sep 17 00:00:00 2001
From: Jozef Gaal <preklady@mayday.sk>
Date: Sat, 15 Oct 2022 19:12:22 +0000
Subject: [PATCH 258/400] Translated using Weblate (Slovak)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/
---
 .../src/main/res/values-sk/strings.xml        | 34 +++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-sk/strings.xml b/library/ui-strings/src/main/res/values-sk/strings.xml
index d4c3f8c40c..ac2c4bea4a 100644
--- a/library/ui-strings/src/main/res/values-sk/strings.xml
+++ b/library/ui-strings/src/main/res/values-sk/strings.xml
@@ -1700,7 +1700,7 @@
     <string name="login_set_msisdn_notice2">Prosím, použite medzinárodný formát.</string>
     <string name="login_set_msisdn_notice">Nastavte si telefónne číslo, aby ste voliteľne umožnili ľuďom, ktorých poznáte, aby vás objavili.</string>
     <string name="does_not_look_like_valid_email">Toto nevyzerá ako platná e-mailová adresa</string>
-    <string name="login_set_email_notice">Nastavte si e-mail na obnovenie konta. Neskôr môžete voliteľne povoliť známym, aby vás objavili podľa vášho e-mailu.</string>
+    <string name="login_set_email_notice">Nastavte si e-mail na obnovenie konta. Neskôr môžete voliteľne povoliť svojim známym, aby vás objavili podľa tohto e-mailu.</string>
     <string name="login_set_email_title">Nastaviť e-mailovú adresu</string>
     <string name="login_reset_password_success_submit">Späť na prihlásenie</string>
     <string name="login_reset_password_success_notice">Vaše heslo bolo obnovené.</string>
@@ -2796,4 +2796,34 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ V tejto miestnosti sa nachádzajú neoverené zariadenia, ktoré nebudú schopné dešifrovať odoslané správy.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Nikdy neposielať šifrované správy do neoverených relácií v tejto miestnosti.</string>
     <string name="action_got_it">Rozumiem</string>
-</resources>
+    <string name="rich_text_editor_format_underline">Použiť formát podčiarknutia</string>
+    <string name="rich_text_editor_format_strikethrough">Použiť formát prečiarknutia</string>
+    <string name="rich_text_editor_format_italic">Použiť formát kurzívou</string>
+    <string name="rich_text_editor_format_bold">Použiť tučný formát</string>
+    <string name="labs_enable_client_info_recording_summary">Zaznamenať názov klienta, verziu a url, aby bolo možné ľahšie rozpoznať relácie v správcovi relácií.</string>
+    <string name="labs_enable_client_info_recording_title">Povoliť zaznamenanie informácií o klientovi</string>
+    <string name="labs_enable_session_manager_summary">Majte lepší prehľad a kontrolu nad všetkými reláciami.</string>
+    <string name="labs_enable_session_manager_title">Použiť nového správcu relácií</string>
+    <string name="device_manager_session_details_device_operating_system">Operačný systém</string>
+    <string name="device_manager_session_details_device_model">Model</string>
+    <string name="device_manager_session_details_device_browser">Prehliadač</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Verzia</string>
+    <string name="device_manager_session_details_application_name">Názov</string>
+    <string name="device_manager_session_details_application">Aplikácia</string>
+    <string name="device_manager_push_notifications_description">Prijímať push oznámenia v tejto relácii.</string>
+    <string name="device_manager_push_notifications_title">Push oznámenia</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Overením aktuálnej relácie zistíte stav overenia tejto relácie.</string>
+    <string name="device_manager_verification_status_unknown">Neznámy stav overenia</string>
+    <string name="push_gateway_item_enabled">Zapnuté:</string>
+    <string name="push_gateway_item_device_id">ID relácie:</string>
+    <string name="error_check_network">Niečo sa pokazilo. Skontrolujte, prosím, svoje sieťové pripojenie a skúste to znova.</string>
+    <string name="grant_permission">Udeliť oprávnenie</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} potrebuje povolenie na zobrazovanie oznámení.
+\nProsím, udeľte toto povolenie.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} potrebuje povolenie na zobrazovanie oznámení. Oznámenia môžu zobrazovať vaše správy, pozvánky atď.
+\n
+\nPovoľte prístup na ďalších vyskakovacích oknách, aby ste mohli zobrazovať oznámenia.</string>
+    <string name="labs_enable_rich_text_editor_summary">Vyskúšajte rozšírený textový editor (čistý textový režim sa objaví čoskoro)</string>
+    <string name="labs_enable_rich_text_editor_title">Povoliť rozšírený textový editor</string>
+</resources>
\ No newline at end of file

From b2e2cbaf47712ab20a746a7301f3b4bca573e9b8 Mon Sep 17 00:00:00 2001
From: LinAGKar <linus.kardell@gmail.com>
Date: Sat, 15 Oct 2022 20:07:31 +0000
Subject: [PATCH 259/400] Translated using Weblate (Swedish)

Currently translated at 98.2% (2426 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/sv/
---
 .../src/main/res/values-sv/strings.xml        | 79 +++++++++++++++++--
 1 file changed, 73 insertions(+), 6 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-sv/strings.xml b/library/ui-strings/src/main/res/values-sv/strings.xml
index bf083a1117..1f38d2069c 100644
--- a/library/ui-strings/src/main/res/values-sv/strings.xml
+++ b/library/ui-strings/src/main/res/values-sv/strings.xml
@@ -615,7 +615,7 @@
     <string name="login_reset_password_mail_confirmation_submit">Jag har verifierat min e-postadress</string>
     <string name="login_reset_password_success_notice_2">Du har blivit utloggad ur alla sessioner och kommer inte längre motta pushnotiser. För att återaktivera pushnotiser, logga in igen på varje enhet.</string>
     <string name="login_set_email_title">Sätt e-postadress</string>
-    <string name="login_set_email_notice">Sätt en e-postadress för att kunna återförva ditt konto. Senare kan du valfritt låta personer du känner upptäcka dig med din e-postadress.</string>
+    <string name="login_set_email_notice">Sätt en e-postadress för att kunna återförvärva ditt konto. Senare kan du valfritt låta personer du känner upptäcka dig med den här e-postadressen.</string>
     <string name="login_set_email_mandatory_hint">E-post</string>
     <string name="login_set_email_optional_hint">E-post (valfritt)</string>
     <string name="login_set_msisdn_notice">Sätt ett telefonnummer som valfritt kan användas för att vara upptäckbar av folk som känner dig.</string>
@@ -1312,10 +1312,10 @@
     <string name="keys_backup_unable_to_get_keys_backup_data">Ett fel inträffade vid hämtning av nyckelsäkerhetskopia</string>
     <string name="navigate_to_room_when_already_in_the_room">Du tittar redan på det här rummet!</string>
     <string name="settings_push_gateway_no_pushers">Inga registrerade pushgateways</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_id">App-ID:</string>
+    <string name="push_gateway_item_push_key">Pushnyckel:</string>
+    <string name="push_gateway_item_app_display_name">Appens visningsnamn:</string>
+    <string name="push_gateway_item_device_name">Sessionens visningsnamn:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Format:</string>
     <string name="settings_troubleshoot_test_token_registration_quick_fix">Registrera token</string>
@@ -2651,4 +2651,71 @@
     <string name="room_list_filter_favourites">Favoriter</string>
     <string name="room_list_filter_unreads">Olästa</string>
     <string name="room_list_filter_all">Alla</string>
-</resources>
+    <string name="device_manager_push_notifications_title">Pushnotiser</string>
+    <string name="device_manager_session_details_description">Applikations-, enhets- och aktivitetsinformation.</string>
+    <string name="device_manager_session_details_title">Sessionsdetaljer</string>
+    <string name="device_manager_session_overview_signout">Logga ut ur den här sessionen</string>
+    <string name="device_manager_other_sessions_clear_filter">Rensa filter</string>
+    <string name="device_manager_other_sessions_no_inactive_sessions_found">Inga inaktiva sessioner hittade.</string>
+    <string name="device_manager_other_sessions_no_unverified_sessions_found">Inga overifierade sessioner hittade.</string>
+    <string name="device_manager_other_sessions_no_verified_sessions_found">Inga verifierade sessioner hittade.</string>
+    <plurals name="device_manager_other_sessions_recommendation_description_inactive">
+        <item quantity="one">Överväg att logga ut ur gamla sessioner (%1$d dag eller längre) du inte använder längre.</item>
+        <item quantity="other">Överväg att logga ut ur gamla sessioner (%1$d dagar eller längre) du inte använder längre.</item>
+    </plurals>
+    <string name="device_manager_other_sessions_recommendation_title_inactive">Inaktiv</string>
+    <string name="device_manager_other_sessions_recommendation_description_unverified">Verifiera dina sessioner för förbättrad säker meddelandehantering eller logga ut ur de du inte känner igen eller använder längre.</string>
+    <string name="device_manager_other_sessions_recommendation_title_unverified">Overifierad</string>
+    <string name="device_manager_other_sessions_recommendation_description_verified">För bäst säkerhet, logga ut från sessioner du inte känner igen eller använder längre.</string>
+    <string name="device_manager_other_sessions_recommendation_title_verified">Verifierad</string>
+    <string name="a11y_device_manager_filter">Filter</string>
+    <plurals name="device_manager_inactive_sessions_description">
+        <item quantity="one">Överväg att logga ut ur gamla sessioner (%1$d dag eller längre) som du inte använder längre.</item>
+        <item quantity="other">Överväg att logga ut ur gamla sessioner (%1$d dagar eller längre) som du inte använder längre.</item>
+    </plurals>
+    <plurals name="device_manager_filter_option_inactive_description">
+        <item quantity="one">Inaktiv %1$d dag eller längre</item>
+        <item quantity="other">Inaktiv %1$d dagar eller längre</item>
+    </plurals>
+    <string name="device_manager_filter_option_inactive">Inaktiv</string>
+    <string name="device_manager_filter_option_unverified_description">Inte redo för säkra meddelanden</string>
+    <string name="device_manager_filter_option_unverified">Overifierad</string>
+    <string name="device_manager_filter_option_verified_description">Redo för säkra meddelanden</string>
+    <string name="device_manager_filter_option_verified">Verifierade</string>
+    <string name="device_manager_filter_option_all_sessions">Alla sessioner</string>
+    <string name="device_manager_filter_bottom_sheet_title">Filter</string>
+    <string name="device_manager_session_last_activity">Senast aktiv %1$s</string>
+    <string name="device_manager_device_title">Enhet</string>
+    <string name="device_manager_session_title">Session</string>
+    <string name="device_manager_current_session_title">Nuvarande session</string>
+    <string name="device_manager_inactive_sessions_title">Inaktiva sessioner</string>
+    <string name="device_manager_unverified_sessions_description">Verifiera eller logga ut ur overifierade sessioner.</string>
+    <string name="device_manager_unverified_sessions_title">Overifierade sessioner</string>
+    <string name="device_manager_header_section_security_recommendations_description">Förbättra din kontosäkerhet genom att följa dessa rekommendationer.</string>
+    <string name="device_manager_header_section_security_recommendations_title">Säkerhetsrekommendationer</string>
+    <plurals name="device_manager_other_sessions_description_inactive">
+        <item quantity="one">Inaktiv %1$d+ dag (%2$s)</item>
+        <item quantity="other">Inaktiv %1$d+ dagar (%2$s)</item>
+    </plurals>
+    <string name="device_manager_other_sessions_description_unverified_current_session">Overifierad · Din nuvarande session</string>
+    <string name="device_manager_other_sessions_description_unverified">Overifierad · Senast aktiv %1$s</string>
+    <string name="device_manager_other_sessions_description_verified">Verifierad · Senast aktiv %1$s</string>
+    <string name="device_manager_other_sessions_view_all">Visa alla (%1$d)</string>
+    <string name="device_manager_view_details">Visa detaljer</string>
+    <string name="device_manager_verify_session">Verifiera session</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Verifiera din nuvarande session för att visa den här sessionens verifieringsstatus.</string>
+    <string name="device_manager_verification_status_detail_other_session_unverified">Verifiera eller logga ut från den här sessionen för bäst säkerhet och pålitlighet.</string>
+    <string name="device_manager_verification_status_detail_current_session_unverified">Verifiera din nuvarande session för förbättrad säker meddelandehantering.</string>
+    <string name="device_manager_verification_status_unknown">Okänd verifieringsstatus</string>
+    <string name="push_gateway_item_enabled">Aktiverad:</string>
+    <string name="push_gateway_item_device_id">Sessions-ID:</string>
+    <string name="error_check_network">Nåt gick fel. Kolla din nätverksanslutning och pröva igen.</string>
+    <string name="grant_permission">Ge åtkomst</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} behöver behörighet att visa aviseringar.
+\nVänligen ge åtkomst.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} behöver behörighet att visa aviseringar. Aviseringar kan visa dina meddelanden, dina inbjudningar, o.s.v.
+\n
+\nVänligen ge åtkomst på nästa pop-uper för att kunna se aviseringar.</string>
+    <string name="labs_enable_rich_text_editor_title">Aktivera rik-text-redigerare</string>
+    <string name="labs_enable_rich_text_editor_summary">Testa den nya rik-text-redigeraren</string>
+</resources>
\ No newline at end of file

From d05ad9425efaec64ae67755823d935008d3cbed6 Mon Sep 17 00:00:00 2001
From: Ihor Hordiichuk <igor_ck@outlook.com>
Date: Fri, 14 Oct 2022 21:12:28 +0000
Subject: [PATCH 260/400] Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/
---
 .../src/main/res/values-uk/strings.xml        | 40 ++++++++++++++++---
 1 file changed, 35 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-uk/strings.xml b/library/ui-strings/src/main/res/values-uk/strings.xml
index 45ee44213c..20b7348bdf 100644
--- a/library/ui-strings/src/main/res/values-uk/strings.xml
+++ b/library/ui-strings/src/main/res/values-uk/strings.xml
@@ -1740,10 +1740,10 @@
     <string name="feedback">Відгук</string>
     <string name="push_gateway_item_format">Формат:</string>
     <string name="push_gateway_item_url">Url:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
+    <string name="push_gateway_item_device_name">Показувана назва сеансу:</string>
+    <string name="push_gateway_item_app_display_name">Показувана назва застосунку:</string>
+    <string name="push_gateway_item_push_key">Ключ Push:</string>
+    <string name="push_gateway_item_app_id">ID застосунку:</string>
     <string name="settings_sdk_version">Версія Matrix SDK</string>
     <string name="create_room_federation_error">Кімнату створено, але деякі запрошення не надіслано з такої причини:
 \n
@@ -2850,4 +2850,34 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ У цій кімнаті є неперевірені пристрої, вони не зможуть розшифрувати повідомлення, які ви надсилаєте.</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">Ніколи не надсилати зашифровані повідомлення на неперевірені сеанси в цій кімнаті.</string>
     <string name="action_got_it">Зрозуміло</string>
-</resources>
+    <string name="rich_text_editor_format_underline">Застосувати форматування підкресленим</string>
+    <string name="rich_text_editor_format_strikethrough">Застосувати форматування перекресленим</string>
+    <string name="rich_text_editor_format_italic">Застосувати форматування курсивом</string>
+    <string name="rich_text_editor_format_bold">Застосувати форматування жирним</string>
+    <string name="labs_enable_client_info_recording_summary">Записуйте назву клієнта, версію та URL-адресу, щоб легше розпізнавати сеанси в менеджері сеансів.</string>
+    <string name="labs_enable_client_info_recording_title">Увімкнути запис відомостей про клієнт</string>
+    <string name="labs_enable_session_manager_summary">Отримайте кращу видимість і контроль над усіма вашими сеансами.</string>
+    <string name="labs_enable_session_manager_title">Увімкнути новий менеджер сеансів</string>
+    <string name="device_manager_session_details_device_operating_system">Операційна система</string>
+    <string name="device_manager_session_details_device_model">Модель</string>
+    <string name="device_manager_session_details_device_browser">Браузер</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Версія</string>
+    <string name="device_manager_session_details_application_name">Назва</string>
+    <string name="device_manager_session_details_application">Застосунок</string>
+    <string name="device_manager_push_notifications_description">Отримувати push-сповіщення про цей сеанс.</string>
+    <string name="device_manager_push_notifications_title">Push-сповіщення</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Звірте свій поточний сеанс, щоб побачити стан перевірки цього сеансу.</string>
+    <string name="device_manager_verification_status_unknown">Невідомий стан перевірки</string>
+    <string name="push_gateway_item_enabled">Увімкнено:</string>
+    <string name="push_gateway_item_device_id">ID сеансу:</string>
+    <string name="error_check_network">Щось пішло не так. Будь ласка, перевірте мережеве з\'єднання та спробуйте ще раз.</string>
+    <string name="grant_permission">Надати дозвіл</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} потребує дозволу на показ сповіщень.
+\nНадайте дозвіл.</string>
+    <string name="permissions_rationale_msg_notification">Для показу сповіщень ${app_name} потрібен дозвіл. Сповіщення можуть показувати ваші повідомлення, запрошення тощо.
+\n
+\nДозвольте доступ до наступних спливних вікон, щоб мати змогу переглядати сповіщення.</string>
+    <string name="labs_enable_rich_text_editor_summary">Спробуйте розширений текстовий редактор (незабаром з\'явиться режим звичайного тексту)</string>
+    <string name="labs_enable_rich_text_editor_title">Увімкнути розширений текстовий редактор</string>
+</resources>
\ No newline at end of file

From fd226b8eab68cd83ba8696638f45b190439042de Mon Sep 17 00:00:00 2001
From: Jeff Huang <s8321414@gmail.com>
Date: Mon, 17 Oct 2022 02:42:41 +0000
Subject: [PATCH 261/400] Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (2470 of 2470 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/
---
 .../src/main/res/values-zh-rTW/strings.xml    | 42 ++++++++++++++++---
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
index e3cd44adca..b72f9bc5c6 100644
--- a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
@@ -873,10 +873,10 @@
     <string name="settings_push_rules">推送規則</string>
     <string name="settings_push_rules_no_rules">未定義通送規則</string>
     <string name="settings_push_gateway_no_pushers">沒有已註冊的推送閘道</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_device_name">session_name:</string>
+    <string name="push_gateway_item_app_id">App ID:</string>
+    <string name="push_gateway_item_push_key">推送金鑰:</string>
+    <string name="push_gateway_item_app_display_name">應用程式顯示名稱:</string>
+    <string name="push_gateway_item_device_name">工作階段顯示名稱:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">格式:</string>
     <string name="preference_voice_and_video">音訊與視訊</string>
@@ -1092,7 +1092,7 @@
 \n
 \n停止密碼變更流程?</string>
     <string name="login_set_email_title">設定電子郵件地址</string>
-    <string name="login_set_email_notice">設定電子郵件地址以復原您的帳號。之後您也可以選擇性地讓您認識的人透過您的這個地址找到您。</string>
+    <string name="login_set_email_notice">設定電子郵件地址以復原您的帳號。之後您也可以選擇性地讓您認識的人透過此地址找到您。</string>
     <string name="login_set_email_mandatory_hint">電子郵件</string>
     <string name="login_set_email_optional_hint">電子郵件(選擇性)</string>
     <string name="login_set_email_submit">下一個</string>
@@ -2688,4 +2688,34 @@
     <string name="some_devices_will_not_be_able_to_decrypt">⚠ 此聊天室中有未驗證的裝置,它們將無法解密您傳送的訊息。</string>
     <string name="encryption_never_send_to_unverified_devices_in_room">切莫向此聊天室中未經驗證的工作階段傳送加密訊息。</string>
     <string name="action_got_it">知道了</string>
-</resources>
+    <string name="rich_text_editor_format_underline">套用底線格式</string>
+    <string name="rich_text_editor_format_strikethrough">套用刪除線格式</string>
+    <string name="rich_text_editor_format_italic">套用義式斜體格式</string>
+    <string name="rich_text_editor_format_bold">套用粗體格式</string>
+    <string name="labs_enable_client_info_recording_summary">記錄客戶端名稱、版本與 URL,以便在工作階段管理程式中可以更簡單地辨認工作階段。</string>
+    <string name="labs_enable_client_info_recording_title">啟用客戶端資訊記錄</string>
+    <string name="labs_enable_session_manager_summary">對所有工作階段有更大的能見度與控制。</string>
+    <string name="labs_enable_session_manager_title">啟用新的工作階段管理程式</string>
+    <string name="device_manager_session_details_device_operating_system">作業系統</string>
+    <string name="device_manager_session_details_device_model">模型</string>
+    <string name="device_manager_session_details_device_browser">瀏覽器</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">版本</string>
+    <string name="device_manager_session_details_application_name">名稱</string>
+    <string name="device_manager_session_details_application">應用程式</string>
+    <string name="device_manager_push_notifications_description">接收關於此工作階段的推播通知。</string>
+    <string name="device_manager_push_notifications_title">推播通知</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">驗證您目前的工作階段以顯示此工作階段的驗證狀態。</string>
+    <string name="device_manager_verification_status_unknown">未知的驗證狀態</string>
+    <string name="push_gateway_item_enabled">已啟用:</string>
+    <string name="push_gateway_item_device_id">工作階段 ID:</string>
+    <string name="error_check_network">發生了一些問題。請檢查您的網路連線並再試一次。</string>
+    <string name="grant_permission">授予權限</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} 需要權限以顯示通知。
+\n請授予權限。</string>
+    <string name="permissions_rationale_msg_notification">${app_name} 需要權限才能顯示通知。通知可以顯示您的訊息、您的邀請等等。
+\n
+\n請在下一個彈出式視窗允許存取以檢視通知。</string>
+    <string name="labs_enable_rich_text_editor_summary">試用格式化文字編輯器(純文字模式即將推出)</string>
+    <string name="labs_enable_rich_text_editor_title">啟用格式化文字編輯器</string>
+</resources>
\ No newline at end of file

From d3e61a23a9a23ea75fe57f5b4c01cf25ea642558 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:29:45 +0100
Subject: [PATCH 262/400] Fix generator

---
 .../api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
index ab58179f5a..75f0024fda 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt
@@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
-@JsonClass(generateAdapter = true)
+@JsonClass(generateAdapter = false)
 enum class SecureRendezvousChannelAlgorithm(val value: String) {
     @Json(name = "org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")
     ECDH_V1("org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256")

From 41dbdbcd7bc5c492620612f4e7f3bcd05c89e7e1 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:30:54 +0100
Subject: [PATCH 263/400] Lint

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index ed2fb6e0f5..2647c5974d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -37,7 +37,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
         @Assisted private val initialState: QrCodeLoginViewState,
         private val authenticationService: AuthenticationService,
         private val activeSessionHolder: ActiveSessionHolder,
-        private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase
+        private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase,
 ) : VectorViewModel<QrCodeLoginViewState, QrCodeLoginAction, QrCodeLoginViewEvents>(initialState) {
 
     @AssistedFactory

From 8c8190202f9792b708d623ef5944016220edc69c Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:31:14 +0100
Subject: [PATCH 264/400] Better function name

---
 .../vector/app/features/login/qr/QrCodeLoginStatusFragment.kt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
index 097c956f2c..6ef261e6d9 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt
@@ -66,14 +66,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
         views.qrCodeLoginStatusCancelButton.isVisible = true
         views.qrCodeLoginStatusTryAgainButton.isVisible = connectionStatus.canTryAgain
         views.qrCodeLoginStatusHeaderView.setTitle(getString(R.string.qr_code_login_header_failed_title))
-        views.qrCodeLoginStatusHeaderView.setDescription(getErrorCode(connectionStatus.errorType))
+        views.qrCodeLoginStatusHeaderView.setDescription(getErrorDescription(connectionStatus.errorType))
         views.qrCodeLoginStatusHeaderView.setImage(
                 imageResource = R.drawable.ic_qr_code_login_failed,
                 backgroundTintColor = ThemeUtils.getColor(requireContext(), R.attr.colorError)
         )
     }
 
-    private fun getErrorCode(reason: RendezvousFailureReason): String {
+    private fun getErrorDescription(reason: RendezvousFailureReason): String {
         return when (reason) {
             RendezvousFailureReason.UnsupportedAlgorithm,
             RendezvousFailureReason.UnsupportedTransport -> getString(R.string.qr_code_login_header_failed_device_is_not_supported_description)

From 8f4d998362d4a89ea0aed78d4abe7c7e2738e9f2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:33:43 +0100
Subject: [PATCH 265/400] Lint

---
 .../internal/session/homeserver/GetHomeServerCapabilitiesTask.kt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 4b56d8e756..2c3cb440b6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -20,7 +20,6 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.api.MatrixPatterns.getServerName
 import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
 import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
-import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.internal.auth.version.Versions

From 6d17d51fe9e7a5483288f833106f4e572fafcb46 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 17:36:35 +0100
Subject: [PATCH 266/400] remove nullability

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  |  4 ++--
 .../features/login/qr/QrCodeLoginViewModel.kt | 22 +++++++++----------
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index 9ad889fca0..e41a72db94 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -89,7 +89,7 @@ class Rendezvous(
     }
 
     @Throws(RendezvousError::class)
-    suspend fun startAfterScanningCode(): String? {
+    suspend fun startAfterScanningCode(): String {
         val checksum = channel.connect()
 
         Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum")
@@ -111,7 +111,7 @@ class Rendezvous(
     }
 
     @Throws(RendezvousError::class)
-    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? {
+    suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session {
         Timber.tag(TAG).i("Waiting for login_token")
 
         val loginToken = receive()
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 2647c5974d..4134dc8ab2 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -100,21 +100,19 @@ class QrCodeLoginViewModel @AssistedInject constructor(
             try {
                 val confirmationCode = rendezvous.startAfterScanningCode()
                 Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode")
-                confirmationCode?.let {
-                    onConnectionEstablished(it)
-                    val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
-                    onSigningIn()
-                    session?.let {
-                        activeSessionHolder.setActiveSession(session)
-                        authenticationService.reset()
 
-                        configureAndStartSessionUseCase.execute(session)
+                onConnectionEstablished(confirmationCode)
 
-                        rendezvous.completeVerificationOnNewDevice(session)
+                val session = rendezvous.waitForLoginOnNewDevice(authenticationService)
+                onSigningIn()
 
-                        _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
-                    }
-                }
+                activeSessionHolder.setActiveSession(session)
+                authenticationService.reset()
+                configureAndStartSessionUseCase.execute(session)
+
+                rendezvous.completeVerificationOnNewDevice(session)
+
+                _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen)
             } catch (t: Throwable) {
                 Timber.tag(TAG).e(t, "Error occurred during sign in")
                 if (t is RendezvousError) {

From a88a172f0fc63d65a7c4a43224335b9d8647e261 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 6 Oct 2022 17:04:27 +0200
Subject: [PATCH 267/400] Trigger play/pause/resume/stop actions on
 VoiceRecorder

---
 .../room/detail/composer/AudioMessageHelper.kt  | 17 +++++++++++++++--
 .../app/features/voice/AbstractVoiceRecorder.kt |  4 ++--
 .../vector/app/features/voice/VoiceRecorder.kt  | 10 ++++++++++
 .../vector/app/features/voice/VoiceRecorderL.kt | 11 ++++++++++-
 .../vector/app/features/voice/VoiceRecorderQ.kt | 11 +++++++++++
 .../usecase/PauseVoiceBroadcastUseCase.kt       |  8 +++++++-
 .../usecase/ResumeVoiceBroadcastUseCase.kt      |  8 +++++++-
 .../usecase/StartVoiceBroadcastUseCase.kt       |  8 +++++++-
 .../usecase/StopVoiceBroadcastUseCase.kt        |  8 +++++++-
 9 files changed, 76 insertions(+), 9 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
index a5e899c672..f068330cb1 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
@@ -114,8 +114,13 @@ class AudioMessageHelper @Inject constructor(
      * When entering in playback mode actually.
      */
     fun pauseRecording() {
-        voiceRecorder.stopRecord()
-        stopRecordingAmplitudes()
+        voiceRecorder.pauseRecord()
+        pauseRecordingAmplitudes()
+    }
+
+    fun resumeRecording() {
+        voiceRecorder.resumeRecord()
+        resumeRecordingAmplitudes()
     }
 
     fun deleteRecording() {
@@ -221,6 +226,14 @@ class AudioMessageHelper @Inject constructor(
         }
     }
 
+    private fun pauseRecordingAmplitudes() {
+        amplitudeTicker?.pause()
+    }
+
+    private fun resumeRecordingAmplitudes() {
+        amplitudeTicker?.resume()
+    }
+
     private fun stopRecordingAmplitudes() {
         amplitudeTicker?.stop()
         amplitudeTicker = null
diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
index 5e27aa5bb2..b28d76f176 100644
--- a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
@@ -32,7 +32,7 @@ abstract class AbstractVoiceRecorder(
 ) : VoiceRecorder {
     private val outputDirectory: File by lazy { ensureAudioDirectory(context) }
 
-    private var mediaRecorder: MediaRecorder? = null
+    protected var mediaRecorder: MediaRecorder? = null
     private var outputFile: File? = null
 
     abstract fun setOutputFormat(mediaRecorder: MediaRecorder)
@@ -79,8 +79,8 @@ abstract class AbstractVoiceRecorder(
     }
 
     override fun stopRecord() {
-        // Can throw when the record is less than 1 second.
         mediaRecorder?.let {
+            // Can throw when the record is less than 1 second.
             tryOrNull { it.stop() }
             it.reset()
             it.release()
diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt
index 785fb9b4da..761aad2334 100644
--- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt
@@ -34,6 +34,16 @@ interface VoiceRecorder {
      */
     fun startRecord(roomId: String)
 
+    /**
+     * Pause the recording.
+     */
+    fun pauseRecord()
+
+    /**
+     * Resume the recording.
+     */
+    fun resumeRecord()
+
     /**
      * Stop the recording.
      */
diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt
index d5395cc849..1460f1a88f 100644
--- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt
@@ -23,6 +23,7 @@ import android.media.MediaRecorder
 import android.media.audiofx.AutomaticGainControl
 import android.media.audiofx.NoiseSuppressor
 import android.os.Build
+import android.widget.Toast
 import io.element.android.opusencoder.OggOpusEncoder
 import io.element.android.opusencoder.configuration.SampleRate
 import kotlinx.coroutines.CoroutineScope
@@ -40,7 +41,7 @@ import kotlin.coroutines.CoroutineContext
  * VoiceRecorder to be used on Android versions < [Build.VERSION_CODES.Q]. It uses libopus to record ogg files.
  */
 class VoiceRecorderL(
-        context: Context,
+        private val context: Context,
         coroutineContext: CoroutineContext,
         private val codec: OggOpusEncoder,
 ) : VoiceRecorder {
@@ -112,6 +113,14 @@ class VoiceRecorderL(
         }
     }
 
+    override fun pauseRecord() {
+        Toast.makeText(context, "Not implemented for this Android version", Toast.LENGTH_SHORT).show()
+    }
+
+    override fun resumeRecord() {
+        Toast.makeText(context, "Not implemented for this Android version", Toast.LENGTH_SHORT).show()
+    }
+
     override fun stopRecord() {
         val recorder = this.audioRecorder ?: return
         recordingJob?.cancel()
diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt
index 5091ddfa3b..1eb850b8f5 100644
--- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt
@@ -20,12 +20,23 @@ import android.content.Context
 import android.media.MediaRecorder
 import android.os.Build
 import androidx.annotation.RequiresApi
+import org.matrix.android.sdk.api.extensions.tryOrNull
 
 /**
  * VoiceRecorder to be used on Android versions >= [Build.VERSION_CODES.Q]. It uses the native OPUS support on Android 10+.
  */
 @RequiresApi(Build.VERSION_CODES.Q)
 class VoiceRecorderQ(context: Context) : AbstractVoiceRecorder(context, "ogg") {
+
+    override fun pauseRecord() {
+        // Can throw when the record is less than 1 second.
+        tryOrNull { mediaRecorder?.pause() }
+    }
+
+    override fun resumeRecord() {
+        mediaRecorder?.resume()
+    }
+
     override fun setOutputFormat(mediaRecorder: MediaRecorder) {
         // We can directly use OGG here
         mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.OGG)
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
index 8f61284423..6c779b7a50 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
+import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -31,6 +32,7 @@ import javax.inject.Inject
 
 class PauseVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
+        private val audioMessageHelper: AudioMessageHelper,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -60,6 +62,10 @@ class PauseVoiceBroadcastUseCase @Inject constructor(
                 ).toContent(),
         )
 
-        // TODO pause recording audio files
+        pauseRecording()
+    }
+
+    private fun pauseRecording() {
+        audioMessageHelper.pauseRecording()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
index d0d82b42c3..fbbbc32612 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
+import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -31,6 +32,7 @@ import javax.inject.Inject
 
 class ResumeVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
+        private val audioMessageHelper: AudioMessageHelper,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -65,6 +67,10 @@ class ResumeVoiceBroadcastUseCase @Inject constructor(
                 ).toContent(),
         )
 
-        // TODO resume recording audio files
+        resumeRecording()
+    }
+
+    private fun resumeRecording() {
+        audioMessageHelper.resumeRecording()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 0b8328cd4b..03dbbed5e4 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
+import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -30,6 +31,7 @@ import javax.inject.Inject
 
 class StartVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
+        private val audioMessageHelper: AudioMessageHelper,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -62,6 +64,10 @@ class StartVoiceBroadcastUseCase @Inject constructor(
                 ).toContent()
         )
 
-        // TODO start recording audio files
+        startRecording(room)
+    }
+
+    private fun startRecording(room: Room) {
+        audioMessageHelper.startRecording(room.roomId)
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
index 8b22193770..ed868fced8 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
+import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -31,6 +32,7 @@ import javax.inject.Inject
 
 class StopVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
+        private val audioMessageHelper: AudioMessageHelper,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -61,6 +63,10 @@ class StopVoiceBroadcastUseCase @Inject constructor(
                 ).toContent(),
         )
 
-        // TODO stop recording audio files
+        stopRecording()
+    }
+
+    private fun stopRecording() {
+        audioMessageHelper.stopRecording()
     }
 }

From dbc61971df6f5bab1e44f5d9af7dbd4a30fe3cbd Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 19 Sep 2022 17:32:36 +0200
Subject: [PATCH 268/400] Reduce duplicated code

---
 .../room/send/LocalEchoEventFactory.kt        | 44 +++++--------------
 1 file changed, 12 insertions(+), 32 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
index 4d5e574592..afff51a4a5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
@@ -403,14 +403,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         size = attachment.size
                 ),
                 url = attachment.queryUri.toString(),
-                relatesTo = rootThreadEventId?.let {
-                    RelationDefaultContent(
-                            type = RelationType.THREAD,
-                            eventId = it,
-                            isFallingBack = true,
-                            inReplyTo = ReplyToContent(eventId = localEchoRepository.getLatestThreadEvent(it))
-                    )
-                }
+                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }
@@ -447,14 +440,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         thumbnailInfo = thumbnailInfo
                 ),
                 url = attachment.queryUri.toString(),
-                relatesTo = rootThreadEventId?.let {
-                    RelationDefaultContent(
-                            type = RelationType.THREAD,
-                            eventId = it,
-                            isFallingBack = true,
-                            inReplyTo = ReplyToContent(eventId = localEchoRepository.getLatestThreadEvent(it))
-                    )
-                }
+                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }
@@ -479,14 +465,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         waveform = waveformSanitizer.sanitize(attachment.waveform)
                 ),
                 voiceMessageIndicator = if (!isVoiceMessage) null else emptyMap(),
-                relatesTo = rootThreadEventId?.let {
-                    RelationDefaultContent(
-                            type = RelationType.THREAD,
-                            eventId = it,
-                            isFallingBack = true,
-                            inReplyTo = ReplyToContent(eventId = localEchoRepository.getLatestThreadEvent(it))
-                    )
-                }
+                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }
@@ -500,14 +479,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         size = attachment.size
                 ),
                 url = attachment.queryUri.toString(),
-                relatesTo = rootThreadEventId?.let {
-                    RelationDefaultContent(
-                            type = RelationType.THREAD,
-                            eventId = it,
-                            isFallingBack = true,
-                            inReplyTo = ReplyToContent(eventId = localEchoRepository.getLatestThreadEvent(it))
-                    )
-                }
+                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }
@@ -629,6 +601,14 @@ internal class LocalEchoEventFactory @Inject constructor(
         return createMessageEvent(roomId, content)
     }
 
+    private fun generateThreadRelationContent(rootThreadEventId: String) =
+            RelationDefaultContent(
+                    type = RelationType.THREAD,
+                    eventId = rootThreadEventId,
+                    isFallingBack = true,
+                    inReplyTo = ReplyToContent(eventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId))
+            )
+
     /**
      * Generates the appropriate relatesTo object for a reply event.
      * It can either be a regular reply or a reply within a thread

From 33a021c8edd491de5ff769de762a0983f4f35b1f Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 19 Sep 2022 18:11:38 +0200
Subject: [PATCH 269/400] Add sdk entry to attach reference to the outgoing
 events

---
 .../sdk/api/session/room/send/SendService.kt  |  5 ++-
 .../session/room/send/DefaultSendService.kt   |  7 ++--
 .../room/send/LocalEchoEventFactory.kt        | 36 +++++++++++--------
 3 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
index de9bcfbf0d..7bd6337548 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
@@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.Content
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.room.model.message.MessageType
 import org.matrix.android.sdk.api.session.room.model.message.PollType
+import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.util.Cancelable
 
@@ -81,13 +82,15 @@ interface SendService {
      * @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
      *                It can be useful to send media to multiple room. It's safe to include the current roomId in this set
      * @param rootThreadEventId when this param is not null, the Media will be sent in this specific thread
+     * @param relatesTo add a relation content to the media event
      * @return a [Cancelable]
      */
     fun sendMedia(
             attachment: ContentAttachmentData,
             compressBeforeSending: Boolean,
             roomIds: Set<String>,
-            rootThreadEventId: String? = null
+            rootThreadEventId: String? = null,
+            relatesTo: RelationDefaultContent? = null
     ): Cancelable
 
     /**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index a3f2825a0c..aa305e6067 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
 import org.matrix.android.sdk.api.session.room.model.message.PollType
 import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
+import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
 import org.matrix.android.sdk.api.session.room.send.SendService
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@@ -280,7 +281,8 @@ internal class DefaultSendService @AssistedInject constructor(
             attachment: ContentAttachmentData,
             compressBeforeSending: Boolean,
             roomIds: Set<String>,
-            rootThreadEventId: String?
+            rootThreadEventId: String?,
+            relatesTo: RelationDefaultContent?,
     ): Cancelable {
         // Ensure that the event will not be send in a thread if we are a different flow.
         // Like sending files to multiple rooms
@@ -295,7 +297,8 @@ internal class DefaultSendService @AssistedInject constructor(
             localEchoEventFactory.createMediaEvent(
                     roomId = it,
                     attachment = attachment,
-                    rootThreadEventId = rootThreadId
+                    rootThreadEventId = rootThreadId,
+                    relatesTo,
             ).also { event ->
                 createLocalEcho(event)
             }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
index afff51a4a5..0fb2c9506a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
@@ -347,14 +347,21 @@ internal class LocalEchoEventFactory @Inject constructor(
     fun createMediaEvent(
             roomId: String,
             attachment: ContentAttachmentData,
-            rootThreadEventId: String?
+            rootThreadEventId: String?,
+            relatesTo: RelationDefaultContent?,
     ): Event {
         return when (attachment.type) {
-            ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment, rootThreadEventId)
-            ContentAttachmentData.Type.VIDEO -> createVideoEvent(roomId, attachment, rootThreadEventId)
-            ContentAttachmentData.Type.AUDIO -> createAudioEvent(roomId, attachment, isVoiceMessage = false, rootThreadEventId = rootThreadEventId)
-            ContentAttachmentData.Type.VOICE_MESSAGE -> createAudioEvent(roomId, attachment, isVoiceMessage = true, rootThreadEventId = rootThreadEventId)
-            ContentAttachmentData.Type.FILE -> createFileEvent(roomId, attachment, rootThreadEventId)
+            ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment, rootThreadEventId, relatesTo)
+            ContentAttachmentData.Type.VIDEO -> createVideoEvent(roomId, attachment, rootThreadEventId, relatesTo)
+            ContentAttachmentData.Type.AUDIO -> createAudioEvent(roomId, attachment, isVoiceMessage = false, rootThreadEventId = rootThreadEventId, relatesTo)
+            ContentAttachmentData.Type.VOICE_MESSAGE -> createAudioEvent(
+                    roomId,
+                    attachment,
+                    isVoiceMessage = true,
+                    rootThreadEventId = rootThreadEventId,
+                    relatesTo
+            )
+            ContentAttachmentData.Type.FILE -> createFileEvent(roomId, attachment, rootThreadEventId, relatesTo)
         }
     }
 
@@ -378,7 +385,7 @@ internal class LocalEchoEventFactory @Inject constructor(
         )
     }
 
-    private fun createImageEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?): Event {
+    private fun createImageEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?, relatesTo: RelationDefaultContent?): Event {
         var width = attachment.width
         var height = attachment.height
 
@@ -403,12 +410,12 @@ internal class LocalEchoEventFactory @Inject constructor(
                         size = attachment.size
                 ),
                 url = attachment.queryUri.toString(),
-                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
+                relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }
 
-    private fun createVideoEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?): Event {
+    private fun createVideoEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?, relatesTo: RelationDefaultContent?): Event {
         val mediaDataRetriever = MediaMetadataRetriever()
         mediaDataRetriever.setDataSource(context, attachment.queryUri)
 
@@ -440,7 +447,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         thumbnailInfo = thumbnailInfo
                 ),
                 url = attachment.queryUri.toString(),
-                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
+                relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }
@@ -449,7 +456,8 @@ internal class LocalEchoEventFactory @Inject constructor(
             roomId: String,
             attachment: ContentAttachmentData,
             isVoiceMessage: Boolean,
-            rootThreadEventId: String?
+            rootThreadEventId: String?,
+            relatesTo: RelationDefaultContent?,
     ): Event {
         val content = MessageAudioContent(
                 msgType = MessageType.MSGTYPE_AUDIO,
@@ -465,12 +473,12 @@ internal class LocalEchoEventFactory @Inject constructor(
                         waveform = waveformSanitizer.sanitize(attachment.waveform)
                 ),
                 voiceMessageIndicator = if (!isVoiceMessage) null else emptyMap(),
-                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
+                relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }
 
-    private fun createFileEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?): Event {
+    private fun createFileEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?, relatesTo: RelationDefaultContent?): Event {
         val content = MessageFileContent(
                 msgType = MessageType.MSGTYPE_FILE,
                 body = attachment.name ?: "file",
@@ -479,7 +487,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         size = attachment.size
                 ),
                 url = attachment.queryUri.toString(),
-                relatesTo = rootThreadEventId?.let { generateThreadRelationContent(it) }
+                relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
         return createMessageEvent(roomId, content)
     }

From e775404e35822ab5e9e9307d589762138ff9dac6 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 13 Oct 2022 23:15:06 +0200
Subject: [PATCH 270/400] Improve VoiceRecorder abstraction

---
 .../detail/composer/AudioMessageHelper.kt     |   4 +-
 .../composer/MessageComposerViewModel.kt      |   2 +-
 .../features/voice/AbstractVoiceRecorder.kt   |  75 ++---------
 .../features/voice/AbstractVoiceRecorderQ.kt  | 124 ++++++++++++++++++
 .../app/features/voice/VoiceRecorder.kt       |  14 +-
 .../app/features/voice/VoiceRecorderL.kt      |  33 +----
 .../app/features/voice/VoiceRecorderQ.kt      |  23 +---
 7 files changed, 163 insertions(+), 112 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
index f068330cb1..d98240904c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
@@ -55,8 +55,8 @@ class AudioMessageHelper @Inject constructor(
     private var amplitudeTicker: CountUpTimer? = null
     private var playbackTicker: CountUpTimer? = null
 
-    fun initializeRecorder(attachmentData: ContentAttachmentData) {
-        voiceRecorder.initializeRecord(attachmentData)
+    fun initializeRecorder(roomId: String, attachmentData: ContentAttachmentData) {
+        voiceRecorder.initializeRecord(roomId, attachmentData)
         amplitudeList.clear()
         attachmentData.waveform?.let {
             amplitudeList.addAll(it)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
index 5d3465ab2e..6f1210a584 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
@@ -943,7 +943,7 @@ class MessageComposerViewModel @AssistedInject constructor(
     }
 
     private fun handleInitializeVoiceRecorder(attachmentData: ContentAttachmentData) {
-        audioMessageHelper.initializeRecorder(attachmentData)
+        audioMessageHelper.initializeRecorder(room.roomId, attachmentData)
         setState { copy(voiceRecordingUiState = VoiceMessageRecorderView.RecordingUiState.Draft) }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
index b28d76f176..9755a0b3fb 100644
--- a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorder.kt
@@ -17,77 +17,24 @@
 package im.vector.app.features.voice
 
 import android.content.Context
-import android.media.MediaRecorder
-import android.os.Build
-import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
 import org.matrix.android.sdk.api.util.md5
 import java.io.File
-import java.io.FileOutputStream
 import java.util.UUID
 
 abstract class AbstractVoiceRecorder(
         private val context: Context,
-        private val filenameExt: String,
 ) : VoiceRecorder {
+
     private val outputDirectory: File by lazy { ensureAudioDirectory(context) }
+    protected var outputFile: File? = null
 
-    protected var mediaRecorder: MediaRecorder? = null
-    private var outputFile: File? = null
-
-    abstract fun setOutputFormat(mediaRecorder: MediaRecorder)
-
-    private fun init() {
-        createMediaRecorder().let {
-            it.setAudioSource(MediaRecorder.AudioSource.DEFAULT)
-            setOutputFormat(it)
-            it.setAudioEncodingBitRate(24000)
-            it.setAudioSamplingRate(48000)
-            mediaRecorder = it
+    override fun initializeRecord(roomId: String, attachmentData: ContentAttachmentData?) {
+        if (attachmentData != null) {
+            outputFile = attachmentData.findVoiceFile(outputDirectory)
         }
     }
 
-    private fun createMediaRecorder(): MediaRecorder {
-        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
-            MediaRecorder(context)
-        } else {
-            @Suppress("DEPRECATION")
-            MediaRecorder()
-        }
-    }
-
-    override fun initializeRecord(attachmentData: ContentAttachmentData) {
-        outputFile = attachmentData.findVoiceFile(outputDirectory)
-    }
-
-    override fun startRecord(roomId: String) {
-        init()
-        val fileName = "${UUID.randomUUID()}.$filenameExt"
-        val outputDirectoryForRoom = File(outputDirectory, roomId.md5()).apply {
-            mkdirs()
-        }
-        outputFile = File(outputDirectoryForRoom, fileName)
-
-        val mr = mediaRecorder ?: return
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            mr.setOutputFile(outputFile)
-        } else {
-            mr.setOutputFile(FileOutputStream(outputFile).fd)
-        }
-        mr.prepare()
-        mr.start()
-    }
-
-    override fun stopRecord() {
-        mediaRecorder?.let {
-            // Can throw when the record is less than 1 second.
-            tryOrNull { it.stop() }
-            it.reset()
-            it.release()
-        }
-        mediaRecorder = null
-    }
-
     override fun cancelRecord() {
         stopRecord()
 
@@ -95,11 +42,15 @@ abstract class AbstractVoiceRecorder(
         outputFile = null
     }
 
-    override fun getMaxAmplitude(): Int {
-        return mediaRecorder?.maxAmplitude ?: 0
-    }
-
     override fun getVoiceMessageFile(): File? {
         return outputFile
     }
+
+    protected fun createOutputFile(roomId: String): File {
+        val fileName = "${UUID.randomUUID()}.$fileNameExt"
+        val outputDirectoryForRoom = File(outputDirectory, roomId.md5()).apply {
+            mkdirs()
+        }
+        return File(outputDirectoryForRoom, fileName)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
new file mode 100644
index 0000000000..bd30c38366
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.voice
+
+import android.content.Context
+import android.media.MediaRecorder
+import android.os.Build
+import androidx.annotation.RequiresApi
+import org.matrix.android.sdk.api.extensions.tryOrNull
+import org.matrix.android.sdk.api.session.content.ContentAttachmentData
+import java.io.File
+
+/**
+ * VoiceRecorder abstraction to be used on Android versions >= [Build.VERSION_CODES.Q].
+ */
+@RequiresApi(Build.VERSION_CODES.Q)
+abstract class AbstractVoiceRecorderQ(private val context: Context) : AbstractVoiceRecorder(context) {
+
+    var mediaRecorder: MediaRecorder? = null
+    protected var nextOutputFile: File? = null
+
+    private val audioSource: Int = MediaRecorder.AudioSource.DEFAULT
+    private val audioEncodingBitRate: Int = 24_000
+    private val audioSamplingRate: Int = 48_000
+
+    abstract val outputFormat: Int // see MediaRecorder.OutputFormat
+    abstract val audioEncoder: Int // see MediaRecorder.AudioEncoder
+
+    override fun initializeRecord(roomId: String, attachmentData: ContentAttachmentData?) {
+        super.initializeRecord(roomId, attachmentData)
+        mediaRecorder = createMediaRecorder().apply {
+            setAudioSource(audioSource)
+            setOutputFormat()
+            setAudioEncodingBitRate(audioEncodingBitRate)
+            setAudioSamplingRate(audioSamplingRate)
+        }
+        setOutputFile(roomId)
+    }
+
+    override fun startRecord(roomId: String) {
+        initializeRecord(roomId = roomId)
+        mediaRecorder?.prepare()
+        mediaRecorder?.start()
+    }
+
+    override fun pauseRecord() {
+        // Can throw when the record is less than 1 second.
+        tryOrNull { mediaRecorder?.pause() }
+    }
+
+    override fun resumeRecord() {
+        mediaRecorder?.resume()
+    }
+
+    override fun stopRecord() {
+        // Can throw when the record is less than 1 second.
+        tryOrNull { mediaRecorder?.stop() }
+        mediaRecorder?.reset()
+        release()
+    }
+
+    override fun cancelRecord() {
+        super.cancelRecord()
+        nextOutputFile?.delete()
+        nextOutputFile = null
+    }
+
+    override fun getMaxAmplitude(): Int {
+        return mediaRecorder?.maxAmplitude ?: 0
+    }
+
+    protected open fun release() {
+        mediaRecorder?.release()
+        mediaRecorder = null
+    }
+
+    fun setNextOutputFile(roomId: String) {
+        val mediaRecorder = mediaRecorder ?: return
+        nextOutputFile = createOutputFile(roomId)
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            mediaRecorder.setNextOutputFile(nextOutputFile)
+        } else {
+            mediaRecorder.setNextOutputFile(nextOutputFile?.outputStream()?.fd)
+        }
+    }
+
+    private fun createMediaRecorder(): MediaRecorder {
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            MediaRecorder(context)
+        } else {
+            @Suppress("DEPRECATION")
+            MediaRecorder()
+        }
+    }
+
+    private fun MediaRecorder.setOutputFormat() {
+        setOutputFormat(outputFormat)
+        setAudioEncoder(audioEncoder)
+    }
+
+    private fun setOutputFile(roomId: String) {
+        val mediaRecorder = mediaRecorder ?: return
+        outputFile = outputFile ?: createOutputFile(roomId)
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            mediaRecorder.setOutputFile(outputFile)
+        } else {
+            mediaRecorder.setOutputFile(outputFile?.outputStream()?.fd)
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt
index 761aad2334..bf38e4adbf 100644
--- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorder.kt
@@ -22,11 +22,19 @@ import org.matrix.android.sdk.api.session.content.ContentAttachmentData
 import java.io.File
 
 interface VoiceRecorder {
+
     /**
-     * Initialize recording with a pre-recorded file.
-     * @param attachmentData data of the recorded file
+     * Audio file extension (eg. `mp4`).
      */
-    fun initializeRecord(attachmentData: ContentAttachmentData)
+    val fileNameExt: String
+
+    /**
+     * Initialize recording with an optional pre-recorded file.
+     *
+     * @param roomId id of the room to initialize record
+     * @param attachmentData data of the pre-recorded file, if any.
+     */
+    fun initializeRecord(roomId: String, attachmentData: ContentAttachmentData? = null)
 
     /**
      * Start the recording.
diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt
index 1460f1a88f..13ddf60620 100644
--- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderL.kt
@@ -31,10 +31,6 @@ import kotlinx.coroutines.Job
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.tryOrNull
-import org.matrix.android.sdk.api.session.content.ContentAttachmentData
-import org.matrix.android.sdk.api.util.md5
-import java.io.File
-import java.util.UUID
 import kotlin.coroutines.CoroutineContext
 
 /**
@@ -44,16 +40,13 @@ class VoiceRecorderL(
         private val context: Context,
         coroutineContext: CoroutineContext,
         private val codec: OggOpusEncoder,
-) : VoiceRecorder {
+) : AbstractVoiceRecorder(context) {
 
     companion object {
         private val SAMPLE_RATE = SampleRate.Rate48kHz
         private const val BITRATE = 24 * 1024
     }
 
-    private val outputDirectory: File by lazy { ensureAudioDirectory(context) }
-    private var outputFile: File? = null
-
     private val recorderScope = CoroutineScope(coroutineContext)
     private var recordingJob: Job? = null
 
@@ -65,6 +58,8 @@ class VoiceRecorderL(
     private var bufferSizeInShorts = 0
     private var maxAmplitude = 0
 
+    override val fileNameExt: String = "ogg"
+
     private fun initializeCodec(filePath: String) {
         codec.init(filePath, SAMPLE_RATE)
         codec.setBitrate(BITRATE)
@@ -86,19 +81,10 @@ class VoiceRecorderL(
         }
     }
 
-    override fun initializeRecord(attachmentData: ContentAttachmentData) {
-        outputFile = attachmentData.findVoiceFile(outputDirectory)
-    }
-
     override fun startRecord(roomId: String) {
-        val fileName = "${UUID.randomUUID()}.ogg"
-        val outputDirectoryForRoom = File(outputDirectory, roomId.md5()).apply {
-            mkdirs()
+        outputFile = createOutputFile(roomId).also {
+            initializeCodec(it.absolutePath)
         }
-        val outputFile = File(outputDirectoryForRoom, fileName)
-        this.outputFile = outputFile
-
-        initializeCodec(outputFile.absolutePath)
 
         recordingJob = recorderScope.launch {
             audioRecorder?.startRecording()
@@ -140,19 +126,10 @@ class VoiceRecorderL(
         codec.release()
     }
 
-    override fun cancelRecord() {
-        outputFile?.delete()
-        outputFile = null
-    }
-
     override fun getMaxAmplitude(): Int {
         return maxAmplitude
     }
 
-    override fun getVoiceMessageFile(): File? {
-        return outputFile
-    }
-
     private fun createAudioRecord() {
         val channelConfig = AudioFormat.CHANNEL_IN_MONO
         val format = AudioFormat.ENCODING_PCM_16BIT
diff --git a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt
index 1eb850b8f5..f128673e27 100644
--- a/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/VoiceRecorderQ.kt
@@ -20,26 +20,17 @@ import android.content.Context
 import android.media.MediaRecorder
 import android.os.Build
 import androidx.annotation.RequiresApi
-import org.matrix.android.sdk.api.extensions.tryOrNull
 
 /**
- * VoiceRecorder to be used on Android versions >= [Build.VERSION_CODES.Q]. It uses the native OPUS support on Android 10+.
+ * VoiceRecorder to be used on Android versions >= [Build.VERSION_CODES.Q].
+ * It uses the native OPUS support on Android 10+.
  */
 @RequiresApi(Build.VERSION_CODES.Q)
-class VoiceRecorderQ(context: Context) : AbstractVoiceRecorder(context, "ogg") {
+class VoiceRecorderQ(context: Context) : AbstractVoiceRecorderQ(context) {
 
-    override fun pauseRecord() {
-        // Can throw when the record is less than 1 second.
-        tryOrNull { mediaRecorder?.pause() }
-    }
+    // We can directly use OGG here
+    override val outputFormat = MediaRecorder.OutputFormat.OGG
+    override val audioEncoder = MediaRecorder.AudioEncoder.OPUS
 
-    override fun resumeRecord() {
-        mediaRecorder?.resume()
-    }
-
-    override fun setOutputFormat(mediaRecorder: MediaRecorder) {
-        // We can directly use OGG here
-        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.OGG)
-        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.OPUS)
-    }
+    override val fileNameExt: String = "ogg"
 }

From ad2bf8d1cefe9da56afa764509594ef45a8d2bf0 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 13 Oct 2022 23:45:01 +0200
Subject: [PATCH 271/400] Add VoiceBroadcastRecorder

---
 .../java/im/vector/app/core/di/VoiceModule.kt | 40 +++++++++
 .../voicebroadcast/VoiceBroadcastRecorder.kt  | 83 +++++++++++++++++++
 2 files changed, 123 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/core/di/VoiceModule.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt

diff --git a/vector/src/main/java/im/vector/app/core/di/VoiceModule.kt b/vector/src/main/java/im/vector/app/core/di/VoiceModule.kt
new file mode 100644
index 0000000000..992ed80677
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/di/VoiceModule.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.core.di
+
+import android.content.Context
+import android.os.Build
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object VoiceModule {
+    @Provides
+    @Singleton
+    fun providesVoiceBroadcastRecorder(context: Context): VoiceBroadcastRecorder? {
+        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            VoiceBroadcastRecorder(context)
+        } else {
+            null
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
new file mode 100644
index 0000000000..2ec882a3d6
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.voicebroadcast
+
+import android.content.Context
+import android.media.MediaRecorder
+import android.os.Build
+import androidx.annotation.RequiresApi
+import im.vector.app.features.voice.AbstractVoiceRecorderQ
+import org.matrix.android.sdk.api.session.content.ContentAttachmentData
+import java.io.File
+
+@RequiresApi(Build.VERSION_CODES.Q)
+class VoiceBroadcastRecorder(
+        context: Context,
+) : AbstractVoiceRecorderQ(context) {
+
+    private val maxFileSize = 25_000L // 0,025 Mb = 25 Kb ~= 6s
+
+    var listener: Listener? = null
+
+    override val outputFormat = MediaRecorder.OutputFormat.MPEG_4
+    override val audioEncoder = MediaRecorder.AudioEncoder.HE_AAC
+
+    override val fileNameExt: String = "mp4"
+
+    override fun initializeRecord(roomId: String, attachmentData: ContentAttachmentData?) {
+        super.initializeRecord(roomId, attachmentData)
+        mediaRecorder?.setMaxFileSize(maxFileSize)
+        mediaRecorder?.setOnInfoListener { _, what, _ ->
+            when (what) {
+                MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING -> onMaxFileSizeApproaching(roomId)
+                MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED -> onNextOutputFileStarted()
+                else -> Unit // Nothing to do
+            }
+        }
+    }
+
+    override fun stopRecord() {
+        super.stopRecord()
+        notifyOutputFileCreated()
+        listener = null
+    }
+
+    override fun release() {
+        mediaRecorder?.setOnInfoListener(null)
+        super.release()
+    }
+
+    private fun onMaxFileSizeApproaching(roomId: String) {
+        setNextOutputFile(roomId)
+    }
+
+    private fun onNextOutputFileStarted() {
+        notifyOutputFileCreated()
+    }
+
+    private fun notifyOutputFileCreated() {
+        outputFile?.let {
+            listener?.onVoiceMessageCreated(it)
+            outputFile = nextOutputFile
+            nextOutputFile = null
+        }
+    }
+
+    fun interface Listener {
+        fun onVoiceMessageCreated(file: File)
+    }
+}

From 3ad245db8bbc5e9c5254909bc073ac0515e82d4e Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 13 Oct 2022 23:45:49 +0200
Subject: [PATCH 272/400] Trigger VoiceBroadcast recording actions

---
 .../usecase/PauseVoiceBroadcastUseCase.kt     |  6 +--
 .../usecase/ResumeVoiceBroadcastUseCase.kt    |  6 +--
 .../usecase/StartVoiceBroadcastUseCase.kt     | 46 ++++++++++++++++---
 .../usecase/StopVoiceBroadcastUseCase.kt      |  9 ++--
 4 files changed, 52 insertions(+), 15 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
index 6c779b7a50..1ba3f7aaa9 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
@@ -16,8 +16,8 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
@@ -32,7 +32,7 @@ import javax.inject.Inject
 
 class PauseVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
-        private val audioMessageHelper: AudioMessageHelper,
+        private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -66,6 +66,6 @@ class PauseVoiceBroadcastUseCase @Inject constructor(
     }
 
     private fun pauseRecording() {
-        audioMessageHelper.pauseRecording()
+        voiceBroadcastRecorder?.pauseRecord()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
index fbbbc32612..69d3fe99a9 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
@@ -16,8 +16,8 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
@@ -32,7 +32,7 @@ import javax.inject.Inject
 
 class ResumeVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
-        private val audioMessageHelper: AudioMessageHelper,
+        private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -71,6 +71,6 @@ class ResumeVoiceBroadcastUseCase @Inject constructor(
     }
 
     private fun resumeRecording() {
-        audioMessageHelper.resumeRecording()
+        voiceBroadcastRecorder?.resumeRecord()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 03dbbed5e4..f2d1df0ea4 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -16,22 +16,33 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
+import android.content.Context
+import android.os.Build
+import androidx.core.content.FileProvider
+import im.vector.app.core.resources.BuildMeta
+import im.vector.app.features.attachments.toContentAttachmentData
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
+import im.vector.lib.multipicker.utils.toMultiPickerAudioType
 import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.events.model.RelationType
 import org.matrix.android.sdk.api.session.events.model.toContent
 import org.matrix.android.sdk.api.session.getRoom
 import org.matrix.android.sdk.api.session.room.Room
+import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
 import timber.log.Timber
+import java.io.File
 import javax.inject.Inject
 
 class StartVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
-        private val audioMessageHelper: AudioMessageHelper,
+        private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
+        private val context: Context,
+        private val buildMeta: BuildMeta,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -55,7 +66,7 @@ class StartVoiceBroadcastUseCase @Inject constructor(
 
     private suspend fun startVoiceBroadcast(room: Room) {
         Timber.d("## StartVoiceBroadcastUseCase: Send new voice broadcast info state event")
-        room.stateService().sendStateEvent(
+        val eventId = room.stateService().sendStateEvent(
                 eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,
                 body = MessageVoiceBroadcastInfoContent(
@@ -64,10 +75,33 @@ class StartVoiceBroadcastUseCase @Inject constructor(
                 ).toContent()
         )
 
-        startRecording(room)
+        startRecording(room, eventId)
     }
 
-    private fun startRecording(room: Room) {
-        audioMessageHelper.startRecording(room.roomId)
+    private fun startRecording(room: Room, eventId: String) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file ->
+                sendVoiceFile(room, file, eventId)
+            }
+            voiceBroadcastRecorder?.startRecord(room.roomId)
+        }
+    }
+
+    private fun sendVoiceFile(room: Room, voiceMessageFile: File, referenceEventId: String) {
+        val outputFileUri = FileProvider.getUriForFile(
+                context,
+                buildMeta.applicationId + ".fileProvider",
+                voiceMessageFile,
+                "Voice message.${voiceMessageFile.extension}"
+        )
+        val audioType = outputFileUri.toMultiPickerAudioType(context) ?: return
+        if (audioType.duration > 1000) {
+            room.sendService().sendMedia(
+                    attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
+                    compressBeforeSending = false,
+                    roomIds = emptySet(),
+                    relatesTo = RelationDefaultContent(RelationType.REFERENCE, referenceEventId)
+            )
+        }
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
index ed868fced8..7f352fc591 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
@@ -16,8 +16,9 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.home.room.detail.composer.AudioMessageHelper
+import android.os.Build
 import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
@@ -32,7 +33,7 @@ import javax.inject.Inject
 
 class StopVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
-        private val audioMessageHelper: AudioMessageHelper,
+        private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
 ) {
 
     suspend fun execute(roomId: String): Result<Unit> = runCatching {
@@ -67,6 +68,8 @@ class StopVoiceBroadcastUseCase @Inject constructor(
     }
 
     private fun stopRecording() {
-        audioMessageHelper.stopRecording()
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            voiceBroadcastRecorder?.stopRecord()
+        }
     }
 }

From aecb66015d19728378ac9452f97d9cd2b2e1c97e Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 08:45:25 +0200
Subject: [PATCH 273/400] Move Voice Broadcast constants into dedicated object

---
 .../java/im/vector/app/core/extensions/TimelineEvent.kt   | 6 ++++--
 .../timeline/action/CheckIfCanRedactEventUseCase.kt       | 4 ++--
 .../room/detail/timeline/factory/TimelineItemFactory.kt   | 4 ++--
 .../detail/timeline/helper/TimelineDisplayableEvents.kt   | 4 ++--
 .../room/detail/timeline/helper/TimelineEventsGroups.kt   | 4 ++--
 .../features/voicebroadcast/VoiceBroadcastConstants.kt    | 7 +++++--
 .../model/MessageVoiceBroadcastInfoContent.kt             | 4 ++--
 .../features/voicebroadcast/model/VoiceBroadcastEvent.kt  | 8 ++++----
 .../voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt  | 6 +++---
 .../voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt | 6 +++---
 .../voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt  | 6 +++---
 .../voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt   | 6 +++---
 .../timeline/action/CheckIfCanRedactEventUseCaseTest.kt   | 4 ++--
 .../voicebroadcast/model/VoiceBroadcastEventTest.kt       | 6 +++---
 .../usecase/PauseVoiceBroadcastUseCaseTest.kt             | 6 +++---
 .../usecase/ResumeVoiceBroadcastUseCaseTest.kt            | 6 +++---
 .../usecase/StartVoiceBroadcastUseCaseTest.kt             | 6 +++---
 .../usecase/StopVoiceBroadcastUseCaseTest.kt              | 6 +++---
 18 files changed, 52 insertions(+), 47 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt b/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt
index cdb84387ce..5c3393416b 100644
--- a/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt
+++ b/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.core.extensions
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
@@ -39,7 +39,9 @@ fun TimelineEvent.canReact(): Boolean {
 fun TimelineEvent.getVectorLastMessageContent(): MessageContent? {
     // Iterate on event types which are not part of the matrix sdk, otherwise fallback to the sdk method
     return when (root.getClearType()) {
-        STATE_ROOM_VOICE_BROADCAST_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessageVoiceBroadcastInfoContent>()
+        VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> {
+            (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessageVoiceBroadcastInfoContent>()
+        }
         else -> getLastMessageContent()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
index eda1929133..8cb82691d9 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCase.kt
@@ -17,7 +17,7 @@
 package im.vector.app.features.home.room.detail.timeline.action
 
 import im.vector.app.core.di.ActiveSessionHolder
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import javax.inject.Inject
@@ -31,7 +31,7 @@ class CheckIfCanRedactEventUseCase @Inject constructor(
         val canRedactEventTypes: List<String> = listOf(
                 EventType.MESSAGE,
                 EventType.STICKER,
-                STATE_ROOM_VOICE_BROADCAST_INFO,
+                VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
         ) +
                 EventType.POLL_START +
                 EventType.STATE_ROOM_BEACON_INFO
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 0b8f95b4a1..31ff257214 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
@@ -21,7 +21,7 @@ import im.vector.app.core.epoxy.TimelineEmptyItem_
 import im.vector.app.core.epoxy.VectorEpoxyModel
 import im.vector.app.features.analytics.DecryptionFailureTracker
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import timber.log.Timber
@@ -89,7 +89,7 @@ class TimelineItemFactory @Inject constructor(
                     // State room create
                     EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(params)
                     in EventType.STATE_ROOM_BEACON_INFO -> messageItemFactory.create(params)
-                    STATE_ROOM_VOICE_BROADCAST_INFO -> messageItemFactory.create(params)
+                    VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> messageItemFactory.create(params)
                     // Unhandled state event types
                     else -> {
                         // Should only happen when shouldShowHiddenEvents() settings is ON
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt
index 87844aba8e..2411cb3877 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.home.room.detail.timeline.helper
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 
@@ -52,7 +52,7 @@ object TimelineDisplayableEvents {
             EventType.STATE_ROOM_JOIN_RULES,
             EventType.KEY_VERIFICATION_DONE,
             EventType.KEY_VERIFICATION_CANCEL,
-            STATE_ROOM_VOICE_BROADCAST_INFO,
+            VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
     ) +
             EventType.POLL_START +
             EventType.STATE_ROOM_BEACON_INFO +
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
index bd211a4513..13de456e84 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
@@ -17,7 +17,7 @@
 package im.vector.app.features.home.room.detail.timeline.helper
 
 import im.vector.app.core.utils.TextUtils
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import org.matrix.android.sdk.api.extensions.orFalse
@@ -59,7 +59,7 @@ class TimelineEventsGroups {
         val content = root.getClearContent()
         return when {
             EventType.isCallEvent(type) -> (content?.get("call_id") as? String)
-            type == STATE_ROOM_VOICE_BROADCAST_INFO -> root.asVoiceBroadcastEvent()?.reference?.eventId
+            type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> root.asVoiceBroadcastEvent()?.reference?.eventId
             type == EventType.STATE_ROOM_WIDGET || type == EventType.STATE_ROOM_WIDGET_LEGACY -> root.stateKey
             else -> {
                 null
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index d7d74b08e9..8c005deb1f 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -16,5 +16,8 @@
 
 package im.vector.app.features.voicebroadcast
 
-/** Voice Broadcast State Event. */
-const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
+object VoiceBroadcastConstants {
+
+    /** Voice Broadcast State Event. */
+    const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
+}
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
index b33d6cc4da..7e4a3d04be 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
@@ -18,7 +18,7 @@ package im.vector.app.features.voicebroadcast.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import org.matrix.android.sdk.api.session.events.model.Content
 import org.matrix.android.sdk.api.session.room.model.message.MessageContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageType.MSGTYPE_VOICE_BROADCAST_INFO
@@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
 import timber.log.Timber
 
 /**
- * Content of the state event of type [STATE_ROOM_VOICE_BROADCAST_INFO].
+ * Content of the state event of type [VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO].
  *
  * It contains general info related to a voice broadcast.
  */
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEvent.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEvent.kt
index c09a5712a8..d464a253d3 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEvent.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEvent.kt
@@ -16,14 +16,14 @@
 
 package im.vector.app.features.voicebroadcast.model
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.RelationType
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
 
 /**
- * [Event] wrapper for [STATE_ROOM_VOICE_BROADCAST_INFO] event type.
+ * [Event] wrapper for [VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO] event type.
  * Provides additional fields and functions related to voice broadcast.
  */
 @JvmInline
@@ -50,6 +50,6 @@ value class VoiceBroadcastEvent(val root: Event) {
 }
 
 /**
- * Map a [STATE_ROOM_VOICE_BROADCAST_INFO] state event to a [VoiceBroadcastEvent].
+ * Map a [VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO] state event to a [VoiceBroadcastEvent].
  */
-fun Event.asVoiceBroadcastEvent() = if (type == STATE_ROOM_VOICE_BROADCAST_INFO) VoiceBroadcastEvent(this) else null
+fun Event.asVoiceBroadcastEvent() = if (type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO) VoiceBroadcastEvent(this) else null
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
index 1ba3f7aaa9..835a57c102 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -41,7 +41,7 @@ class PauseVoiceBroadcastUseCase @Inject constructor(
         Timber.d("## PauseVoiceBroadcastUseCase: Pause voice broadcast requested")
 
         val lastVoiceBroadcastEvent = room.stateService().getStateEvent(
-                STATE_ROOM_VOICE_BROADCAST_INFO,
+                VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 QueryStringValue.Equals(session.myUserId)
         )?.asVoiceBroadcastEvent()
         when (val voiceBroadcastState = lastVoiceBroadcastEvent?.content?.voiceBroadcastState) {
@@ -54,7 +54,7 @@ class PauseVoiceBroadcastUseCase @Inject constructor(
     private suspend fun pauseVoiceBroadcast(room: Room, reference: RelationDefaultContent?) {
         Timber.d("## PauseVoiceBroadcastUseCase: Send new voice broadcast info state event")
         room.stateService().sendStateEvent(
-                eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,
                 body = MessageVoiceBroadcastInfoContent(
                         relatesTo = reference,
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
index 69d3fe99a9..2f03d4194c 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCase.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -41,7 +41,7 @@ class ResumeVoiceBroadcastUseCase @Inject constructor(
         Timber.d("## ResumeVoiceBroadcastUseCase: Resume voice broadcast requested")
 
         val lastVoiceBroadcastEvent = room.stateService().getStateEvent(
-                STATE_ROOM_VOICE_BROADCAST_INFO,
+                VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 QueryStringValue.Equals(session.myUserId)
         )?.asVoiceBroadcastEvent()
         when (val voiceBroadcastState = lastVoiceBroadcastEvent?.content?.voiceBroadcastState) {
@@ -59,7 +59,7 @@ class ResumeVoiceBroadcastUseCase @Inject constructor(
     private suspend fun resumeVoiceBroadcast(room: Room, reference: RelationDefaultContent?) {
         Timber.d("## ResumeVoiceBroadcastUseCase: Send new voice broadcast info state event")
         room.stateService().sendStateEvent(
-                eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,
                 body = MessageVoiceBroadcastInfoContent(
                         relatesTo = reference,
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index f2d1df0ea4..6be8e27345 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -21,7 +21,7 @@ import android.os.Build
 import androidx.core.content.FileProvider
 import im.vector.app.core.resources.BuildMeta
 import im.vector.app.features.attachments.toContentAttachmentData
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -51,7 +51,7 @@ class StartVoiceBroadcastUseCase @Inject constructor(
         Timber.d("## StartVoiceBroadcastUseCase: Start voice broadcast requested")
 
         val onGoingVoiceBroadcastEvents = room.stateService().getStateEvents(
-                setOf(STATE_ROOM_VOICE_BROADCAST_INFO),
+                setOf(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO),
                 QueryStringValue.IsNotEmpty
         )
                 .mapNotNull { it.asVoiceBroadcastEvent() }
@@ -67,7 +67,7 @@ class StartVoiceBroadcastUseCase @Inject constructor(
     private suspend fun startVoiceBroadcast(room: Room) {
         Timber.d("## StartVoiceBroadcastUseCase: Send new voice broadcast info state event")
         val eventId = room.stateService().sendStateEvent(
-                eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,
                 body = MessageVoiceBroadcastInfoContent(
                         voiceBroadcastStateStr = VoiceBroadcastState.STARTED.value,
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
index 7f352fc591..85ffde0d02 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
@@ -17,7 +17,7 @@
 package im.vector.app.features.voicebroadcast.usecase
 
 import android.os.Build
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -42,7 +42,7 @@ class StopVoiceBroadcastUseCase @Inject constructor(
         Timber.d("## StopVoiceBroadcastUseCase: Stop voice broadcast requested")
 
         val lastVoiceBroadcastEvent = room.stateService().getStateEvent(
-                STATE_ROOM_VOICE_BROADCAST_INFO,
+                VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 QueryStringValue.Equals(session.myUserId)
         )?.asVoiceBroadcastEvent()
         when (val voiceBroadcastState = lastVoiceBroadcastEvent?.content?.voiceBroadcastState) {
@@ -56,7 +56,7 @@ class StopVoiceBroadcastUseCase @Inject constructor(
     private suspend fun stopVoiceBroadcast(room: Room, reference: RelationDefaultContent?) {
         Timber.d("## StopVoiceBroadcastUseCase: Send new voice broadcast info state event")
         room.stateService().sendStateEvent(
-                eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,
                 body = MessageVoiceBroadcastInfoContent(
                         relatesTo = reference,
diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt
index fcb052cb2b..e2157c3af0 100644
--- a/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/action/CheckIfCanRedactEventUseCaseTest.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.home.room.detail.timeline.action
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.test.fakes.FakeActiveSessionHolder
 import io.mockk.mockk
 import org.amshove.kluent.shouldBe
@@ -35,7 +35,7 @@ class CheckIfCanRedactEventUseCaseTest {
 
     @Test
     fun `given an event which can be redacted and owned by user when use case executes then the result is true`() {
-        val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER, STATE_ROOM_VOICE_BROADCAST_INFO) +
+        val canRedactEventTypes = listOf(EventType.MESSAGE, EventType.STICKER, VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO) +
                 EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO
 
         canRedactEventTypes.forEach { eventType ->
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt
index 8865e870f0..8c3d24342f 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.model
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import org.amshove.kluent.shouldBeEqualTo
 import org.amshove.kluent.shouldBeNull
 import org.amshove.kluent.shouldNotBeNull
@@ -48,7 +48,7 @@ class VoiceBroadcastEventTest {
         )
         val event = Event(
                 eventId = AN_EVENT_ID,
-                type = STATE_ROOM_VOICE_BROADCAST_INFO,
+                type = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 content = content.toContent(),
         )
         val expectedReference = RelationDefaultContent(RelationType.REFERENCE, event.eventId)
@@ -71,7 +71,7 @@ class VoiceBroadcastEventTest {
                 relatesTo = RelationDefaultContent(RelationType.REFERENCE, A_REFERENCED_EVENT_ID),
         )
         val event = Event(
-                type = STATE_ROOM_VOICE_BROADCAST_INFO,
+                type = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 content = content.toContent(),
         )
         val expectedReference = content.relatesTo
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt
index 3139f20cd4..7d950d1c73 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.test.fakes.FakeRoom
@@ -80,7 +80,7 @@ class PauseVoiceBroadcastUseCaseTest {
         // Then
         coVerify {
             fakeRoom.stateService().sendStateEvent(
-                    eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = fakeSession.myUserId,
                     body = any(),
             )
@@ -114,7 +114,7 @@ class PauseVoiceBroadcastUseCaseTest {
         val event = state?.let {
             Event(
                     eventId = if (state == VoiceBroadcastState.STARTED) A_STARTED_VOICE_BROADCAST_EVENT_ID else AN_EVENT_ID,
-                    type = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    type = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = fakeSession.myUserId,
                     content = MessageVoiceBroadcastInfoContent(
                             voiceBroadcastStateStr = state.value,
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt
index 23d506482b..215969a65d 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.test.fakes.FakeRoom
@@ -80,7 +80,7 @@ class ResumeVoiceBroadcastUseCaseTest {
         // Then
         coVerify {
             fakeRoom.stateService().sendStateEvent(
-                    eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = fakeSession.myUserId,
                     body = any(),
             )
@@ -114,7 +114,7 @@ class ResumeVoiceBroadcastUseCaseTest {
         val event = state?.let {
             Event(
                     eventId = if (state == VoiceBroadcastState.STARTED) A_STARTED_VOICE_BROADCAST_EVENT_ID else AN_EVENT_ID,
-                    type = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    type = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = fakeSession.myUserId,
                     content = MessageVoiceBroadcastInfoContent(
                             voiceBroadcastStateStr = state.value,
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt
index 398d6fedf0..a0336800ca 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.test.fakes.FakeRoom
@@ -81,7 +81,7 @@ class StartVoiceBroadcastUseCaseTest {
         // Then
         coVerify {
             fakeRoom.stateService().sendStateEvent(
-                    eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = fakeSession.myUserId,
                     body = any(),
             )
@@ -106,7 +106,7 @@ class StartVoiceBroadcastUseCaseTest {
     private fun givenAVoiceBroadcasts(voiceBroadcasts: List<VoiceBroadcast>) {
         val events = voiceBroadcasts.map {
             Event(
-                    type = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    type = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = it.userId,
                     content = MessageVoiceBroadcastInfoContent(
                             voiceBroadcastStateStr = it.state.value
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt
index aa8dcddf30..dd69f12a4c 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.voicebroadcast.STATE_ROOM_VOICE_BROADCAST_INFO
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.test.fakes.FakeRoom
@@ -80,7 +80,7 @@ class StopVoiceBroadcastUseCaseTest {
         // Then
         coVerify {
             fakeRoom.stateService().sendStateEvent(
-                    eventType = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = fakeSession.myUserId,
                     body = any(),
             )
@@ -114,7 +114,7 @@ class StopVoiceBroadcastUseCaseTest {
         val event = state?.let {
             Event(
                     eventId = if (state == VoiceBroadcastState.STARTED) A_STARTED_VOICE_BROADCAST_EVENT_ID else AN_EVENT_ID,
-                    type = STATE_ROOM_VOICE_BROADCAST_INFO,
+                    type = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                     stateKey = fakeSession.myUserId,
                     content = MessageVoiceBroadcastInfoContent(
                             voiceBroadcastStateStr = state.value,

From ad730d55c1ed42c99ad71cbd3a22dcf8362ee09d Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 08:56:14 +0200
Subject: [PATCH 274/400] Fix tests

---
 .../usecase/PauseVoiceBroadcastUseCaseTest.kt         |  5 ++++-
 .../usecase/ResumeVoiceBroadcastUseCaseTest.kt        |  5 ++++-
 .../usecase/StartVoiceBroadcastUseCaseTest.kt         | 11 ++++++++++-
 .../usecase/StopVoiceBroadcastUseCaseTest.kt          |  5 ++++-
 4 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt
index 7d950d1c73..5c42b26c54 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCaseTest.kt
@@ -17,6 +17,7 @@
 package im.vector.app.features.voicebroadcast.usecase
 
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.test.fakes.FakeRoom
@@ -25,6 +26,7 @@ import im.vector.app.test.fakes.FakeSession
 import io.mockk.clearAllMocks
 import io.mockk.coEvery
 import io.mockk.coVerify
+import io.mockk.mockk
 import io.mockk.slot
 import kotlinx.coroutines.test.runTest
 import org.amshove.kluent.shouldBe
@@ -44,7 +46,8 @@ class PauseVoiceBroadcastUseCaseTest {
 
     private val fakeRoom = FakeRoom()
     private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
-    private val pauseVoiceBroadcastUseCase = PauseVoiceBroadcastUseCase(fakeSession)
+    private val fakeVoiceBroadcastRecorder = mockk<VoiceBroadcastRecorder>(relaxed = true)
+    private val pauseVoiceBroadcastUseCase = PauseVoiceBroadcastUseCase(fakeSession, fakeVoiceBroadcastRecorder)
 
     @Test
     fun `given a room id with a potential existing voice broadcast state when calling execute then the voice broadcast is paused or not`() = runTest {
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt
index 215969a65d..a1bc3a04ec 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/ResumeVoiceBroadcastUseCaseTest.kt
@@ -17,6 +17,7 @@
 package im.vector.app.features.voicebroadcast.usecase
 
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.test.fakes.FakeRoom
@@ -25,6 +26,7 @@ import im.vector.app.test.fakes.FakeSession
 import io.mockk.clearAllMocks
 import io.mockk.coEvery
 import io.mockk.coVerify
+import io.mockk.mockk
 import io.mockk.slot
 import kotlinx.coroutines.test.runTest
 import org.amshove.kluent.shouldBe
@@ -44,7 +46,8 @@ class ResumeVoiceBroadcastUseCaseTest {
 
     private val fakeRoom = FakeRoom()
     private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
-    private val resumeVoiceBroadcastUseCase = ResumeVoiceBroadcastUseCase(fakeSession)
+    private val fakeVoiceBroadcastRecorder = mockk<VoiceBroadcastRecorder>(relaxed = true)
+    private val resumeVoiceBroadcastUseCase = ResumeVoiceBroadcastUseCase(fakeSession, fakeVoiceBroadcastRecorder)
 
     @Test
     fun `given a room id with a potential existing voice broadcast state when calling execute then the voice broadcast is resumed or not`() = runTest {
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt
index a0336800ca..9fa6b7a450 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCaseTest.kt
@@ -17,14 +17,17 @@
 package im.vector.app.features.voicebroadcast.usecase
 
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
+import im.vector.app.test.fakes.FakeContext
 import im.vector.app.test.fakes.FakeRoom
 import im.vector.app.test.fakes.FakeRoomService
 import im.vector.app.test.fakes.FakeSession
 import io.mockk.clearAllMocks
 import io.mockk.coEvery
 import io.mockk.coVerify
+import io.mockk.mockk
 import io.mockk.slot
 import kotlinx.coroutines.test.runTest
 import org.amshove.kluent.shouldBe
@@ -44,7 +47,13 @@ class StartVoiceBroadcastUseCaseTest {
 
     private val fakeRoom = FakeRoom()
     private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
-    private val startVoiceBroadcastUseCase = StartVoiceBroadcastUseCase(fakeSession)
+    private val fakeVoiceBroadcastRecorder = mockk<VoiceBroadcastRecorder>(relaxed = true)
+    private val startVoiceBroadcastUseCase = StartVoiceBroadcastUseCase(
+            fakeSession,
+            fakeVoiceBroadcastRecorder,
+            FakeContext().instance,
+            mockk()
+    )
 
     @Test
     fun `given a room id with potential several existing voice broadcast states when calling execute then the voice broadcast is started or not`() = runTest {
diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt
index dd69f12a4c..ee6b141bd9 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCaseTest.kt
@@ -17,6 +17,7 @@
 package im.vector.app.features.voicebroadcast.usecase
 
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.test.fakes.FakeRoom
@@ -25,6 +26,7 @@ import im.vector.app.test.fakes.FakeSession
 import io.mockk.clearAllMocks
 import io.mockk.coEvery
 import io.mockk.coVerify
+import io.mockk.mockk
 import io.mockk.slot
 import kotlinx.coroutines.test.runTest
 import org.amshove.kluent.shouldBe
@@ -44,7 +46,8 @@ class StopVoiceBroadcastUseCaseTest {
 
     private val fakeRoom = FakeRoom()
     private val fakeSession = FakeSession(fakeRoomService = FakeRoomService(fakeRoom))
-    private val stopVoiceBroadcastUseCase = StopVoiceBroadcastUseCase(fakeSession)
+    private val fakeVoiceBroadcastRecorder = mockk<VoiceBroadcastRecorder>(relaxed = true)
+    private val stopVoiceBroadcastUseCase = StopVoiceBroadcastUseCase(fakeSession, fakeVoiceBroadcastRecorder)
 
     @Test
     fun `given a room id with a potential existing voice broadcast state when calling execute then the voice broadcast is stopped or not`() = runTest {

From c492fda00028a170bc24504d39239b8a64380b5d Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 10:24:28 +0200
Subject: [PATCH 275/400] Change VoiceBroadcastRecorder as Interface

---
 .../java/im/vector/app/core/di/VoiceModule.kt |  3 +-
 .../voicebroadcast/VoiceBroadcastRecorder.kt  | 60 +-------------
 .../voicebroadcast/VoiceBroadcastRecorderQ.kt | 78 +++++++++++++++++++
 3 files changed, 83 insertions(+), 58 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt

diff --git a/vector/src/main/java/im/vector/app/core/di/VoiceModule.kt b/vector/src/main/java/im/vector/app/core/di/VoiceModule.kt
index 992ed80677..54d556ea91 100644
--- a/vector/src/main/java/im/vector/app/core/di/VoiceModule.kt
+++ b/vector/src/main/java/im/vector/app/core/di/VoiceModule.kt
@@ -23,6 +23,7 @@ import dagger.Provides
 import dagger.hilt.InstallIn
 import dagger.hilt.components.SingletonComponent
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorderQ
 import javax.inject.Singleton
 
 @Module
@@ -32,7 +33,7 @@ object VoiceModule {
     @Singleton
     fun providesVoiceBroadcastRecorder(context: Context): VoiceBroadcastRecorder? {
         return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-            VoiceBroadcastRecorder(context)
+            VoiceBroadcastRecorderQ(context)
         } else {
             null
         }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
index 2ec882a3d6..916f39106a 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
@@ -16,66 +16,12 @@
 
 package im.vector.app.features.voicebroadcast
 
-import android.content.Context
-import android.media.MediaRecorder
-import android.os.Build
-import androidx.annotation.RequiresApi
-import im.vector.app.features.voice.AbstractVoiceRecorderQ
-import org.matrix.android.sdk.api.session.content.ContentAttachmentData
+import im.vector.app.features.voice.VoiceRecorder
 import java.io.File
 
-@RequiresApi(Build.VERSION_CODES.Q)
-class VoiceBroadcastRecorder(
-        context: Context,
-) : AbstractVoiceRecorderQ(context) {
+interface VoiceBroadcastRecorder : VoiceRecorder {
 
-    private val maxFileSize = 25_000L // 0,025 Mb = 25 Kb ~= 6s
-
-    var listener: Listener? = null
-
-    override val outputFormat = MediaRecorder.OutputFormat.MPEG_4
-    override val audioEncoder = MediaRecorder.AudioEncoder.HE_AAC
-
-    override val fileNameExt: String = "mp4"
-
-    override fun initializeRecord(roomId: String, attachmentData: ContentAttachmentData?) {
-        super.initializeRecord(roomId, attachmentData)
-        mediaRecorder?.setMaxFileSize(maxFileSize)
-        mediaRecorder?.setOnInfoListener { _, what, _ ->
-            when (what) {
-                MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING -> onMaxFileSizeApproaching(roomId)
-                MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED -> onNextOutputFileStarted()
-                else -> Unit // Nothing to do
-            }
-        }
-    }
-
-    override fun stopRecord() {
-        super.stopRecord()
-        notifyOutputFileCreated()
-        listener = null
-    }
-
-    override fun release() {
-        mediaRecorder?.setOnInfoListener(null)
-        super.release()
-    }
-
-    private fun onMaxFileSizeApproaching(roomId: String) {
-        setNextOutputFile(roomId)
-    }
-
-    private fun onNextOutputFileStarted() {
-        notifyOutputFileCreated()
-    }
-
-    private fun notifyOutputFileCreated() {
-        outputFile?.let {
-            listener?.onVoiceMessageCreated(it)
-            outputFile = nextOutputFile
-            nextOutputFile = null
-        }
-    }
+    var listener: Listener?
 
     fun interface Listener {
         fun onVoiceMessageCreated(file: File)
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
new file mode 100644
index 0000000000..3aaad19a47
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.voicebroadcast
+
+import android.content.Context
+import android.media.MediaRecorder
+import android.os.Build
+import androidx.annotation.RequiresApi
+import im.vector.app.features.voice.AbstractVoiceRecorderQ
+import org.matrix.android.sdk.api.session.content.ContentAttachmentData
+
+@RequiresApi(Build.VERSION_CODES.Q)
+class VoiceBroadcastRecorderQ(
+        context: Context,
+) : AbstractVoiceRecorderQ(context), VoiceBroadcastRecorder {
+
+    private val maxFileSize = 25_000L // 0,025 Mb = 25 Kb ~= 6s
+
+    override var listener: VoiceBroadcastRecorder.Listener? = null
+
+    override val outputFormat = MediaRecorder.OutputFormat.MPEG_4
+    override val audioEncoder = MediaRecorder.AudioEncoder.HE_AAC
+
+    override val fileNameExt: String = "mp4"
+
+    override fun initializeRecord(roomId: String, attachmentData: ContentAttachmentData?) {
+        super.initializeRecord(roomId, attachmentData)
+        mediaRecorder?.setMaxFileSize(maxFileSize)
+        mediaRecorder?.setOnInfoListener { _, what, _ ->
+            when (what) {
+                MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING -> onMaxFileSizeApproaching(roomId)
+                MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED -> onNextOutputFileStarted()
+                else -> Unit // Nothing to do
+            }
+        }
+    }
+
+    override fun stopRecord() {
+        super.stopRecord()
+        notifyOutputFileCreated()
+        listener = null
+    }
+
+    override fun release() {
+        mediaRecorder?.setOnInfoListener(null)
+        super.release()
+    }
+
+    private fun onMaxFileSizeApproaching(roomId: String) {
+        setNextOutputFile(roomId)
+    }
+
+    private fun onNextOutputFileStarted() {
+        notifyOutputFileCreated()
+    }
+
+    private fun notifyOutputFileCreated() {
+        outputFile?.let {
+            listener?.onVoiceMessageCreated(it)
+            outputFile = nextOutputFile
+            nextOutputFile = null
+        }
+    }
+}

From 06bceef7c0f528927b1305ed5d046ca8a98fd4f0 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 10:52:28 +0200
Subject: [PATCH 276/400] Add changelog

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

diff --git a/changelog.d/7363.wip b/changelog.d/7363.wip
new file mode 100644
index 0000000000..b5a5f4c352
--- /dev/null
+++ b/changelog.d/7363.wip
@@ -0,0 +1 @@
+[Voice Broadcast] Record and send not aggregated voice messages to the room

From fb9c747a20e8c1adb42bbe156fd915ffe2c5e0da Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 11:35:53 +0200
Subject: [PATCH 277/400] Reformat and add trailing commas

---
 .../sdk/api/session/room/send/SendService.kt  |  6 +--
 .../room/send/LocalEchoEventFactory.kt        | 49 ++++++++++++-------
 2 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
index 7bd6337548..53b49129c4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
@@ -72,7 +72,7 @@ interface SendService {
             text: String,
             formattedText: String? = null,
             autoMarkdown: Boolean,
-            rootThreadEventId: String? = null
+            rootThreadEventId: String? = null,
     ): Cancelable
 
     /**
@@ -90,7 +90,7 @@ interface SendService {
             compressBeforeSending: Boolean,
             roomIds: Set<String>,
             rootThreadEventId: String? = null,
-            relatesTo: RelationDefaultContent? = null
+            relatesTo: RelationDefaultContent? = null,
     ): Cancelable
 
     /**
@@ -106,7 +106,7 @@ interface SendService {
             attachments: List<ContentAttachmentData>,
             compressBeforeSending: Boolean,
             roomIds: Set<String>,
-            rootThreadEventId: String? = null
+            rootThreadEventId: String? = null,
     ): Cancelable
 
     /**
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
index 0fb2c9506a..1d7f624eba 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
@@ -127,7 +127,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             newBodyFormattedText: CharSequence?,
             newBodyAutoMarkdown: Boolean,
             msgType: String,
-            compatibilityText: String
+            compatibilityText: String,
     ): Event {
         val content = if (newBodyFormattedText != null) {
             TextContent(newBodyText.toString(), newBodyFormattedText.toString()).toMessageTextContent(msgType)
@@ -148,7 +148,7 @@ internal class LocalEchoEventFactory @Inject constructor(
     private fun createPollContent(
             question: String,
             options: List<String>,
-            pollType: PollType
+            pollType: PollType,
     ): MessagePollContent {
         return MessagePollContent(
                 unstablePollCreationInfo = PollCreationInfo(
@@ -166,7 +166,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             pollType: PollType,
             targetEventId: String,
             question: String,
-            options: List<String>
+            options: List<String>,
     ): Event {
         val newContent = MessagePollContent(
                 relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId),
@@ -186,7 +186,7 @@ internal class LocalEchoEventFactory @Inject constructor(
     fun createPollReplyEvent(
             roomId: String,
             pollEventId: String,
-            answerId: String
+            answerId: String,
     ): Event {
         val content = MessagePollResponseContent(
                 body = answerId,
@@ -212,7 +212,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             roomId: String,
             pollType: PollType,
             question: String,
-            options: List<String>
+            options: List<String>,
     ): Event {
         val content = createPollContent(question, options, pollType)
         val localId = LocalEcho.createLocalEchoId()
@@ -229,7 +229,7 @@ internal class LocalEchoEventFactory @Inject constructor(
 
     fun createEndPollEvent(
             roomId: String,
-            eventId: String
+            eventId: String,
     ): Event {
         val content = MessageEndPollContent(
                 relatesTo = RelationDefaultContent(
@@ -254,7 +254,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             latitude: Double,
             longitude: Double,
             uncertainty: Double?,
-            isUserLocation: Boolean
+            isUserLocation: Boolean,
     ): Event {
         val geoUri = buildGeoUri(latitude, longitude, uncertainty)
         val assetType = if (isUserLocation) LocationAssetType.SELF else LocationAssetType.PIN
@@ -274,7 +274,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             roomId: String,
             latitude: Double,
             longitude: Double,
-            uncertainty: Double?
+            uncertainty: Double?,
     ): Event {
         val geoUri = buildGeoUri(latitude, longitude, uncertainty)
         val content = MessageBeaconLocationDataContent(
@@ -305,7 +305,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             newBodyText: String,
             autoMarkdown: Boolean,
             msgType: String,
-            compatibilityText: String
+            compatibilityText: String,
     ): Event {
         val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false)
         val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: ""
@@ -359,7 +359,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                     attachment,
                     isVoiceMessage = true,
                     rootThreadEventId = rootThreadEventId,
-                    relatesTo
+                    relatesTo,
             )
             ContentAttachmentData.Type.FILE -> createFileEvent(roomId, attachment, rootThreadEventId, relatesTo)
         }
@@ -385,7 +385,12 @@ internal class LocalEchoEventFactory @Inject constructor(
         )
     }
 
-    private fun createImageEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?, relatesTo: RelationDefaultContent?): Event {
+    private fun createImageEvent(
+            roomId: String,
+            attachment: ContentAttachmentData,
+            rootThreadEventId: String?,
+            relatesTo: RelationDefaultContent?,
+    ): Event {
         var width = attachment.width
         var height = attachment.height
 
@@ -415,7 +420,12 @@ internal class LocalEchoEventFactory @Inject constructor(
         return createMessageEvent(roomId, content)
     }
 
-    private fun createVideoEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?, relatesTo: RelationDefaultContent?): Event {
+    private fun createVideoEvent(
+            roomId: String,
+            attachment: ContentAttachmentData,
+            rootThreadEventId: String?,
+            relatesTo: RelationDefaultContent?,
+    ): Event {
         val mediaDataRetriever = MediaMetadataRetriever()
         mediaDataRetriever.setDataSource(context, attachment.queryUri)
 
@@ -478,7 +488,12 @@ internal class LocalEchoEventFactory @Inject constructor(
         return createMessageEvent(roomId, content)
     }
 
-    private fun createFileEvent(roomId: String, attachment: ContentAttachmentData, rootThreadEventId: String?, relatesTo: RelationDefaultContent?): Event {
+    private fun createFileEvent(
+            roomId: String,
+            attachment: ContentAttachmentData,
+            rootThreadEventId: String?,
+            relatesTo: RelationDefaultContent?,
+    ): Event {
         val content = MessageFileContent(
                 msgType = MessageType.MSGTYPE_FILE,
                 body = attachment.name ?: "file",
@@ -539,7 +554,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             text: CharSequence,
             msgType: String,
             autoMarkdown: Boolean,
-            formattedText: String?
+            formattedText: String?,
     ): Event {
         val content = formattedText?.let { TextContent(text.toString(), it) } ?: createTextContent(text, autoMarkdown)
         return createEvent(
@@ -568,7 +583,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             replyTextFormatted: CharSequence?,
             autoMarkdown: Boolean,
             rootThreadEventId: String? = null,
-            showInThread: Boolean
+            showInThread: Boolean,
     ): Event? {
         // Fallbacks and event representation
         // TODO Add error/warning logs when any of this is null
@@ -614,7 +629,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                     type = RelationType.THREAD,
                     eventId = rootThreadEventId,
                     isFallingBack = true,
-                    inReplyTo = ReplyToContent(eventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId))
+                    inReplyTo = ReplyToContent(eventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId)),
             )
 
     /**
@@ -760,7 +775,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             text: String,
             formattedText: String?,
             autoMarkdown: Boolean,
-            rootThreadEventId: String?
+            rootThreadEventId: String?,
     ): Event {
         val messageContent = quotedEvent.getLastMessageContent()
         val textMsg = if (messageContent is MessageContentWithFormattedBody) { messageContent.formattedBody } else { messageContent?.body }

From 64e6a2bfaba5aee8448a6e54617fd4b2d60e97c6 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 12:23:02 +0200
Subject: [PATCH 278/400] Compute file size from chunk length

---
 .../voicebroadcast/VoiceBroadcastConstants.kt    |  3 +++
 .../voicebroadcast/VoiceBroadcastRecorder.kt     |  2 ++
 .../voicebroadcast/VoiceBroadcastRecorderQ.kt    |  7 ++++++-
 .../model/MessageVoiceBroadcastInfoContent.kt    |  2 +-
 .../usecase/StartVoiceBroadcastUseCase.kt        | 16 +++++++---------
 5 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index 8c005deb1f..07bd94ca8c 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -20,4 +20,7 @@ object VoiceBroadcastConstants {
 
     /** Voice Broadcast State Event. */
     const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
+
+    /** Default voice broadcast chunk duration, in seconds */
+    const val DEFAULT_CHUNK_LENGTH = 5
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
index 916f39106a..2668501a8d 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
@@ -23,6 +23,8 @@ interface VoiceBroadcastRecorder : VoiceRecorder {
 
     var listener: Listener?
 
+    fun startRecord(roomId: String, chunkLength: Int)
+
     fun interface Listener {
         fun onVoiceMessageCreated(file: File)
     }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
index 3aaad19a47..fe8222e840 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -28,7 +28,7 @@ class VoiceBroadcastRecorderQ(
         context: Context,
 ) : AbstractVoiceRecorderQ(context), VoiceBroadcastRecorder {
 
-    private val maxFileSize = 25_000L // 0,025 Mb = 25 Kb ~= 6s
+    private var maxFileSize = 25_000L // 0,025 Mb = 25 Kb ~= 6s
 
     override var listener: VoiceBroadcastRecorder.Listener? = null
 
@@ -49,6 +49,11 @@ class VoiceBroadcastRecorderQ(
         }
     }
 
+    override fun startRecord(roomId: String, chunkLength: Int) {
+        maxFileSize = (chunkLength * 0.004166).toLong() // TODO change this approximate conversion
+        startRecord(roomId)
+    }
+
     override fun stopRecord() {
         super.stopRecord()
         notifyOutputFileCreated()
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
index 7e4a3d04be..5044bb5c34 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
@@ -41,7 +41,7 @@ data class MessageVoiceBroadcastInfoContent(
         /** The [VoiceBroadcastState] value. **/
         @Json(name = "state") val voiceBroadcastStateStr: String = "",
         /** The length of the voice chunks in seconds. **/
-        @Json(name = "chunk_length") val chunkLength: Long? = null,
+        @Json(name = "chunk_length") val chunkLength: Int? = null,
 ) : MessageContent {
 
     val voiceBroadcastState: VoiceBroadcastState? = VoiceBroadcastState.values()
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 6be8e27345..64f1dfb317 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -17,7 +17,6 @@
 package im.vector.app.features.voicebroadcast.usecase
 
 import android.content.Context
-import android.os.Build
 import androidx.core.content.FileProvider
 import im.vector.app.core.resources.BuildMeta
 import im.vector.app.features.attachments.toContentAttachmentData
@@ -66,25 +65,24 @@ class StartVoiceBroadcastUseCase @Inject constructor(
 
     private suspend fun startVoiceBroadcast(room: Room) {
         Timber.d("## StartVoiceBroadcastUseCase: Send new voice broadcast info state event")
+        val chunkLength = VoiceBroadcastConstants.DEFAULT_CHUNK_LENGTH // Todo Get the length from the room settings
         val eventId = room.stateService().sendStateEvent(
                 eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,
                 body = MessageVoiceBroadcastInfoContent(
                         voiceBroadcastStateStr = VoiceBroadcastState.STARTED.value,
-                        chunkLength = 5L, // TODO Get length from voice broadcast settings
+                        chunkLength = chunkLength,
                 ).toContent()
         )
 
-        startRecording(room, eventId)
+        startRecording(room, eventId, chunkLength)
     }
 
-    private fun startRecording(room: Room, eventId: String) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-            voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file ->
-                sendVoiceFile(room, file, eventId)
-            }
-            voiceBroadcastRecorder?.startRecord(room.roomId)
+    private fun startRecording(room: Room, eventId: String, chunkLength: Int) {
+        voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file ->
+            sendVoiceFile(room, file, eventId)
         }
+        voiceBroadcastRecorder?.startRecord(room.roomId, chunkLength)
     }
 
     private fun sendVoiceFile(room: Room, voiceMessageFile: File, referenceEventId: String) {

From 9d35e81db7a8e3b7206b2afadc94435863145c99 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 16:37:59 +0200
Subject: [PATCH 279/400] Compute max file size from chunk length

---
 .../im/vector/app/features/voice/AbstractVoiceRecorderQ.kt    | 2 +-
 .../app/features/voicebroadcast/VoiceBroadcastConstants.kt    | 2 +-
 .../app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt    | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
index bd30c38366..428b3c578b 100644
--- a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
@@ -34,8 +34,8 @@ abstract class AbstractVoiceRecorderQ(private val context: Context) : AbstractVo
     protected var nextOutputFile: File? = null
 
     private val audioSource: Int = MediaRecorder.AudioSource.DEFAULT
-    private val audioEncodingBitRate: Int = 24_000
     private val audioSamplingRate: Int = 48_000
+    protected val audioEncodingBitRate: Int = 24_000
 
     abstract val outputFormat: Int // see MediaRecorder.OutputFormat
     abstract val audioEncoder: Int // see MediaRecorder.AudioEncoder
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index 07bd94ca8c..83d83b167e 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -22,5 +22,5 @@ object VoiceBroadcastConstants {
     const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
 
     /** Default voice broadcast chunk duration, in seconds */
-    const val DEFAULT_CHUNK_LENGTH = 5
+    const val DEFAULT_CHUNK_LENGTH = 30
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
index fe8222e840..620db721c9 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -28,7 +28,7 @@ class VoiceBroadcastRecorderQ(
         context: Context,
 ) : AbstractVoiceRecorderQ(context), VoiceBroadcastRecorder {
 
-    private var maxFileSize = 25_000L // 0,025 Mb = 25 Kb ~= 6s
+    private var maxFileSize = 0L // zero or negative for no limit
 
     override var listener: VoiceBroadcastRecorder.Listener? = null
 
@@ -50,7 +50,7 @@ class VoiceBroadcastRecorderQ(
     }
 
     override fun startRecord(roomId: String, chunkLength: Int) {
-        maxFileSize = (chunkLength * 0.004166).toLong() // TODO change this approximate conversion
+        maxFileSize = (chunkLength * audioEncodingBitRate / 8).toLong()
         startRecord(roomId)
     }
 

From 62596b38c78e4bc569777ce2371f43ea1064bb0f Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 16:40:22 +0200
Subject: [PATCH 280/400] Pause recording when the composer is not visible
 anymore

---
 .../detail/composer/MessageComposerFragment.kt   | 16 ++++++++++++----
 .../detail/composer/MessageComposerViewModel.kt  | 15 +++++++++++++++
 .../detail/composer/MessageComposerViewState.kt  |  9 +++++++++
 3 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
index b3abfa480e..4721b81571 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
@@ -227,10 +227,18 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
     override fun onPause() {
         super.onPause()
 
-        if (withState(messageComposerViewModel) { it.isVoiceRecording } && requireActivity().isChangingConfigurations) {
-            // we're rotating, maintain any active recordings
-        } else {
-            messageComposerViewModel.handle(MessageComposerAction.OnEntersBackground(composer.text.toString()))
+        withState(messageComposerViewModel) {
+            when {
+                it.isVoiceRecording && requireActivity().isChangingConfigurations -> {
+                    // we're rotating, maintain any active recordings
+                }
+                // TODO remove this when there will be a recording indicator outside of the timeline
+                // Pause voice broadcast if the timeline is not shown anymore
+                it.isVoiceBroadcasting && !requireActivity().isChangingConfigurations -> timelineViewModel.handle(VoiceBroadcastAction.Pause)
+                else -> {
+                    messageComposerViewModel.handle(MessageComposerAction.OnEntersBackground(composer.text.toString()))
+                }
+            }
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
index 6f1210a584..eef06d11b7 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.home.room.detail.composer
 
+import androidx.lifecycle.asFlow
 import com.airbnb.mvrx.MavericksViewModelFactory
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -40,8 +41,11 @@ import im.vector.app.features.home.room.detail.toMessageType
 import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
 import im.vector.app.features.session.coroutineScope
 import im.vector.app.features.settings.VectorPreferences
+import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.Session
@@ -90,6 +94,7 @@ class MessageComposerViewModel @AssistedInject constructor(
     init {
         loadDraftIfAny()
         observePowerLevelAndEncryption()
+        observeVoiceBroadcast()
         subscribeToStateInternal()
     }
 
@@ -182,6 +187,16 @@ class MessageComposerViewModel @AssistedInject constructor(
         }
     }
 
+    private fun observeVoiceBroadcast() {
+        room.stateService().getStateEventLive(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO, QueryStringValue.Equals(session.myUserId))
+                .asFlow()
+                .unwrap()
+                .mapNotNull { it.asVoiceBroadcastEvent()?.content?.voiceBroadcastState }
+                .setOnEach {
+                    copy(voiceBroadcastState = it)
+                }
+    }
+
     private fun handleEnterQuoteMode(action: MessageComposerAction.EnterQuoteMode) {
         room.getTimelineEvent(action.eventId)?.let { timelineEvent ->
             setState { copy(sendMode = SendMode.Quote(timelineEvent, currentComposerText)) }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt
index 47a7122584..0df1dbebd8 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewState.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail.composer
 import com.airbnb.mvrx.MavericksState
 import im.vector.app.features.home.room.detail.arguments.TimelineArgs
 import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import kotlin.random.Random
@@ -67,6 +68,7 @@ data class MessageComposerViewState(
         val startsThread: Boolean = false,
         val sendMode: SendMode = SendMode.Regular("", false),
         val voiceRecordingUiState: VoiceMessageRecorderView.RecordingUiState = VoiceMessageRecorderView.RecordingUiState.Idle,
+        val voiceBroadcastState: VoiceBroadcastState? = null,
         val text: CharSequence? = null,
 ) : MavericksState {
 
@@ -77,6 +79,13 @@ data class MessageComposerViewState(
         is VoiceMessageRecorderView.RecordingUiState.Recording -> true
     }
 
+    val isVoiceBroadcasting = when (voiceBroadcastState) {
+        VoiceBroadcastState.STARTED,
+        VoiceBroadcastState.PAUSED,
+        VoiceBroadcastState.RESUMED -> true
+        else -> false
+    }
+
     val isVoiceMessageIdle = !isVoiceRecording
 
     val isComposerVisible = canSendMessage.boolean() && !isVoiceRecording

From 3a951f207604d638bb7ab66453e4d4162e84b32b Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Fri, 14 Oct 2022 16:49:27 +0200
Subject: [PATCH 281/400] Add punctuation to kdoc

---
 .../app/features/voicebroadcast/VoiceBroadcastConstants.kt      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index 83d83b167e..bd37251dd4 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -21,6 +21,6 @@ object VoiceBroadcastConstants {
     /** Voice Broadcast State Event. */
     const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
 
-    /** Default voice broadcast chunk duration, in seconds */
+    /** Default voice broadcast chunk duration, in seconds. */
     const val DEFAULT_CHUNK_LENGTH = 30
 }

From 8cfe6b13639158011e63b40e2592aa770316e5a0 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Mon, 17 Oct 2022 23:46:28 +0100
Subject: [PATCH 282/400] Wording updates

---
 library/ui-strings/src/main/res/values/strings.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index e28ebb31c6..7fe8dd8d08 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3386,12 +3386,12 @@
     <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your intent connection(s); Your device(s);</string>
     <string name="qr_code_login_header_failed_other_device_already_signed_in_description">The other device is already signed in.</string>
     <string name="qr_code_login_header_failed_other_device_not_signed_in_description">The other device must be signed in.</string>
-    <string name="qr_code_login_header_failed_invalid_qr_code_description">The QR code scanned is invalid.</string>
+    <string name="qr_code_login_header_failed_invalid_qr_code_description">That QR code is invalid.</string>
     <string name="qr_code_login_header_failed_user_cancelled_description">The sign in was cancelled on the other device.</string>
     <string name="qr_code_login_header_failed_homeserver_is_not_supported_description">The homeserver doesn\'t support sign in with QR code.</string>
-    <string name="qr_code_login_new_device_instruction_1">Open ${app_name} on your other device</string>
-    <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy -> Show All Sessions</string>
-    <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code in this device\'</string>
+    <string name="qr_code_login_new_device_instruction_1">Open the app on your other device</string>
+    <string name="qr_code_login_new_device_instruction_2">Go to Settings -> Security &amp; Privacy</string>
+    <string name="qr_code_login_new_device_instruction_3">Select \'Show QR code\'</string>
     <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Start at the sign in screen</string>
     <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Select \'Sign in with QR code\'</string>
     <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Start at the sign in screen</string>

From 7017cb97e94d10ebdfa5cfa4ce47a1b0f1c21173 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 17 Oct 2022 23:04:02 +0000
Subject: [PATCH 283/400] Bump michaelkaye/setup-matrix-synapse from 1.0.3 to
 1.0.4

Bumps [michaelkaye/setup-matrix-synapse](https://github.com/michaelkaye/setup-matrix-synapse) from 1.0.3 to 1.0.4.
- [Release notes](https://github.com/michaelkaye/setup-matrix-synapse/releases)
- [Commits](https://github.com/michaelkaye/setup-matrix-synapse/compare/v1.0.3...v1.0.4)

---
updated-dependencies:
- dependency-name: michaelkaye/setup-matrix-synapse
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/post-pr.yml | 2 +-
 .github/workflows/tests.yml   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/post-pr.yml b/.github/workflows/post-pr.yml
index bf948064ed..628e12593c 100644
--- a/.github/workflows/post-pr.yml
+++ b/.github/workflows/post-pr.yml
@@ -52,7 +52,7 @@ jobs:
           restore-keys: |
             ${{ runner.os }}-gradle-
       - name: Start synapse server
-        uses: michaelkaye/setup-matrix-synapse@v1.0.3
+        uses: michaelkaye/setup-matrix-synapse@v1.0.4
         with:
           uploadLogs: true
           httpPort: 8080
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 1816fe3a78..bb16d8abe8 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -50,7 +50,7 @@ jobs:
       - uses: actions/setup-python@v4
         with:
           python-version: 3.8
-      - uses: michaelkaye/setup-matrix-synapse@v1.0.3
+      - uses: michaelkaye/setup-matrix-synapse@v1.0.4
         with:
           uploadLogs: true
           httpPort: 8080

From 5c4c3c92d4c294ec101d956c96117b70f51c4e49 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 17 Oct 2022 23:08:08 +0000
Subject: [PATCH 284/400] Bump flipper from 0.170.0 to 0.171.0

Bumps `flipper` from 0.170.0 to 0.171.0.

Updates `flipper` from 0.170.0 to 0.171.0
- [Release notes](https://github.com/facebook/flipper/releases)
- [Commits](https://github.com/facebook/flipper/compare/v0.170.0...v0.171.0)

Updates `flipper-network-plugin` from 0.170.0 to 0.171.0
- [Release notes](https://github.com/facebook/flipper/releases)
- [Commits](https://github.com/facebook/flipper/compare/v0.170.0...v0.171.0)

---
updated-dependencies:
- dependency-name: com.facebook.flipper:flipper
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: com.facebook.flipper:flipper-network-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index d60283e825..7ca95b0ba1 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -18,7 +18,7 @@ def markwon = "4.6.2"
 def moshi = "1.14.0"
 def lifecycle = "2.5.1"
 def flowBinding = "1.2.0"
-def flipper = "0.170.0"
+def flipper = "0.171.0"
 def epoxy = "5.0.0"
 def mavericks = "3.0.1"
 def glide = "4.14.2"

From cf1c7515fb87283915412c7718917a0d9902221f Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 00:35:13 +0100
Subject: [PATCH 285/400] Automatically try again on partial failed QR scan

---
 .../features/login/qr/QrCodeLoginInstructionsFragment.kt   | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
index efd23f2530..40fcbbbb85 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt
@@ -63,6 +63,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
             val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
             val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
 
+            Timber.d("Scanned QR code: $scannedQrCode, was QR code: $wasQrCode")
             if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
                 onQrCodeScanned(scannedQrCode)
             } else {
@@ -76,7 +77,11 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment<FragmentQrCodeLoginIn
     }
 
     private fun onQrCodeScannerFailed() {
-        Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed")
+        // The user scanned something unexpected, so we try scanning again.
+        // This seems to happen particularly with the large QRs needed for rendezvous
+        // especially when the QR is partially off the screen
+        Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed - showing scanner again")
+        QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
     }
 
     override fun invalidate() = withState(viewModel) { state ->

From a3126b0026dc70ad6c9e2cf12a11fe3ba0944179 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 00:35:35 +0100
Subject: [PATCH 286/400] Progress to status screen on failure

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 4134dc8ab2..1a8ed945c0 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -125,6 +125,8 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun onFailed(reason: RendezvousFailureReason) {
+        _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen)
+
         setState {
             copy(
                     connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry)

From 8a62dfb34aca1c3ab52d05d240ea74cc2b5a6d54 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 00:35:43 +0100
Subject: [PATCH 287/400] Lint

---
 .../matrix/android/sdk/api/rendezvous/RendezvousTransport.kt  | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
index 5daf906930..81632e951a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt
@@ -22,11 +22,15 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails
 
 interface RendezvousTransport {
     var ready: Boolean
+
     @Throws(RendezvousError::class)
     suspend fun details(): RendezvousTransportDetails
+
     @Throws(RendezvousError::class)
     suspend fun send(contentType: MediaType, data: ByteArray)
+
     @Throws(RendezvousError::class)
     suspend fun receive(): ByteArray?
+
     suspend fun close()
 }

From afa55649c53e31511758cd09030d98b92869f540 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 18 Oct 2022 00:52:02 +0000
Subject: [PATCH 288/400] Bump play-services-location from 20.0.0 to 21.0.0

Bumps play-services-location from 20.0.0 to 21.0.0.

---
updated-dependencies:
- dependency-name: com.google.android.gms:play-services-location
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 vector-app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector-app/build.gradle b/vector-app/build.gradle
index ca77e4b86f..66fdf3fbf4 100644
--- a/vector-app/build.gradle
+++ b/vector-app/build.gradle
@@ -372,7 +372,7 @@ dependencies {
     debugImplementation 'com.facebook.soloader:soloader:0.10.4'
     debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0"
 
-    gplayImplementation "com.google.android.gms:play-services-location:20.0.0"
+    gplayImplementation "com.google.android.gms:play-services-location:21.0.0"
     // UnifiedPush gplay flavor only
     gplayImplementation('com.google.firebase:firebase-messaging:23.1.0') {
         exclude group: 'com.google.firebase', module: 'firebase-core'

From ba822d8231783174ec493e935edefc29f44c5d88 Mon Sep 17 00:00:00 2001
From: Johannes Marbach <n0-0ne+github@mailbox.org>
Date: Mon, 17 Oct 2022 19:49:00 +0000
Subject: [PATCH 289/400] Translated using Weblate (German)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 .../src/main/res/values-de/strings.xml        | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index a8e24369b5..837a00e30b 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -2771,4 +2771,37 @@
     <string name="permissions_rationale_msg_notification">${app_name} braucht die Berechtigung, um Benachrichtigungen anzuzeigen. Benachrichtigungen können deine Nachrichten, Einladungen etc. anzeigen.
 \n
 \nBitte erlaube den Zugriff im nächsten Dialog, damit Benachrichtigungen angezeigt werden können.</string>
+    <string name="qr_code_login_confirm_security_code_description">Bitte vergewissere dich, dass du den Ursprung dieses Codes kennst. Durch die Verbindung beider Geräte, gewährst du Zugriff auf deinen gesamten Account.</string>
+    <string name="qr_code_login_confirm_security_code">Bestätigen</string>
+    <string name="qr_code_login_try_again">Nochmal versuchen</string>
+    <string name="qr_code_login_status_no_match">Keine Übereinstimmung\?</string>
+    <string name="qr_code_login_signing_in">Du wirst angemeldet</string>
+    <string name="qr_code_login_connecting_to_device">Stelle Verbindung zum Gerät her</string>
+    <string name="qr_code_login_scan_qr_code_button">QR-Code scannen</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Mobiles Gerät anmelden\?</string>
+    <string name="qr_code_login_show_qr_code_button">QR-Code auf diesem Gerät anzeigen</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Wähle \'QR-Code scannen\'</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Beginne auf dem Anmeldebildschirm</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Wähle \'Mit QR-Code anmelden\'</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Beginne auf dem Anmeldebildschirm</string>
+    <string name="qr_code_login_new_device_instruction_3">Wähle \'QR-Code auf diesem Gerät anzeigen\'</string>
+    <string name="qr_code_login_new_device_instruction_2">Gehe zu Einstellungen -&gt; Sicherheit und Privatsphäre -&gt; Alle Sitzungen anzeigen</string>
+    <string name="qr_code_login_new_device_instruction_1">Öffne ${app_name} auf deinem anderen Gerät</string>
+    <string name="qr_code_login_header_failed_denied_description">Die Anfrage wurde auf dem anderen Gerät abgelehnt.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Die Verbindung konnte nicht in der erforderlichen Zeit hergestellt werden.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Verbindung mit diesem Gerät nicht unterstützt.</string>
+    <string name="qr_code_login_header_failed_title">Verbindung fehlgeschlagen</string>
+    <string name="qr_code_login_header_connected_description">Überprüfe dein angemeldetes Gerät. Der unten gezeigte Code sollte angezeigt werden. Bestätige, dass beide Codes übereinstimmen:</string>
+    <string name="qr_code_login_header_connected_title">Sichere Verbindung hergestellt</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Scanne den unten angezeigten QR-Code mit deinem nicht angemeldeten Gerät.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Benutze dein angemeldetes Gerät um den unten angezeigten QR-Code zu scannen:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Mit QR-Code anmelden</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Benutze die Kamera auf diesem Gerät um den vom anderen Gerät angezeigten QR-Code zu scannen:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">QR-Code scannen</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Du kannst dieses Gerät benutzen um ein anderes Gerät per QR-Code anzumelden. Dafür gibt es zwei Wege:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Mit QR-Code anmelden</string>
+    <string name="login_scan_qr_code">QR-Code scannen</string>
 </resources>
\ No newline at end of file

From 1db3d69aebf20d14faf223ee0f0da5648b58e769 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 08:58:21 +0200
Subject: [PATCH 290/400] Change chunk_length type in unit test

---
 .../features/voicebroadcast/model/VoiceBroadcastEventTest.kt    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt b/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt
index 8c3d24342f..ca2e8320c9 100644
--- a/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt
+++ b/vector/src/test/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastEventTest.kt
@@ -34,7 +34,7 @@ import org.matrix.android.sdk.api.session.room.model.relation.ReplyToContent
 
 private const val AN_EVENT_ID = "event_id"
 private const val A_REFERENCED_EVENT_ID = "event_id_ref"
-private const val A_CHUNK_LENGTH = 3_600L
+private const val A_CHUNK_LENGTH = 30
 
 class VoiceBroadcastEventTest {
 

From f297117df2fe66c61e8589e73bc88cf7a99586bb Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 08:48:28 +0100
Subject: [PATCH 291/400] Use mutex

---
 .../channels/ECDHRendezvousChannel.kt         | 75 ++++++++++---------
 1 file changed, 38 insertions(+), 37 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 2f368d6520..19c46db34d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -19,6 +19,8 @@ package org.matrix.android.sdk.api.rendezvous.channels
 import android.util.Base64
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 import okhttp3.MediaType.Companion.toMediaType
 import org.matrix.android.sdk.api.logger.LoggerTag
 import org.matrix.android.sdk.api.rendezvous.RendezvousChannel
@@ -71,6 +73,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             @Json val iv: String? = null
     )
 
+    private var olmSASMutex = Mutex()
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)
@@ -87,45 +90,44 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     @Throws(RendezvousError::class)
     override suspend fun connect(): String {
-        olmSAS ?.let { olmSAS ->
-            val isInitiator = theirPublicKey == null
+        val sas = olmSAS ?: throw RendezvousError("Channel closed", RendezvousFailureReason.Unknown)
+        val isInitiator = theirPublicKey == null
 
-            if (isInitiator) {
-                Timber.tag(TAG).i("Waiting for other device to send their public key")
-                val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError)
+        if (isInitiator) {
+            Timber.tag(TAG).i("Waiting for other device to send their public key")
+            val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError)
 
-                if (res.key == null) {
-                    throw RendezvousError(
-                            "Unsupported algorithm: ${res.algorithm}",
-                            RendezvousFailureReason.UnsupportedAlgorithm,
-                    )
-                }
-                theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
-            } else {
-                // send our public key unencrypted
-                Timber.tag(TAG).i("Sending public key")
-                send(
-                        ECDHPayload(
-                                algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
-                                key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
-                        )
+            if (res.key == null) {
+                throw RendezvousError(
+                        "Unsupported algorithm: ${res.algorithm}",
+                        RendezvousFailureReason.UnsupportedAlgorithm,
                 )
             }
+            theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP)
+        } else {
+            // send our public key unencrypted
+            Timber.tag(TAG).i("Sending public key")
+            send(
+                    ECDHPayload(
+                            algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1,
+                            key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)
+                    )
+            )
+        }
 
-            synchronized(olmSAS) {
-                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
-                olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+        olmSASMutex.withLock {
+            sas.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
+            sas.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP))
 
-                val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
-                val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
-                val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
+            val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP)
+            val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP)
+            val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey"
 
-                aesKey = olmSAS.generateShortCode(aesInfo, 32)
+            aesKey = sas.generateShortCode(aesInfo, 32)
 
-                val rawChecksum = olmSAS.generateShortCode(aesInfo, 5)
-                return getDecimalCodeRepresentation(rawChecksum)
-            }
-        } ?: throw RuntimeException("Channel closed")
+            val rawChecksum = sas.generateShortCode(aesInfo, 5)
+            return getDecimalCodeRepresentation(rawChecksum)
+        }
     }
 
     private suspend fun send(payload: ECDHPayload) {
@@ -154,12 +156,11 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     override suspend fun close() {
-        olmSAS ?.let {
-            synchronized(it) {
-                // this does a double release check already so we don't re-check ourselves
-                it.releaseSas()
-                olmSAS = null
-            }
+        val sas = olmSAS ?: throw IllegalStateException("Channel already closed")
+        olmSASMutex.withLock {
+            // this does a double release check already so we don't re-check ourselves
+            sas.releaseSas()
+            olmSAS = null
         }
         transport.close()
     }

From 6d6d2de08b918bebf101adb8521914d5204b700a Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 18 Oct 2022 10:00:18 +0200
Subject: [PATCH 292/400] Fix message send even if the step `ui-tests` is
 successful. Jobs `codecov-units` and `integration-tests` do not exist
 anymore.

---
 .github/workflows/post-pr.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/post-pr.yml b/.github/workflows/post-pr.yml
index bf948064ed..b618519b0d 100644
--- a/.github/workflows/post-pr.yml
+++ b/.github/workflows/post-pr.yml
@@ -94,7 +94,7 @@ jobs:
     needs:
       - should-i-run
       - ui-tests
-    if: always() && (needs.should-i-run.result == 'success' ) && ((needs.codecov-units.result != 'success' ) || (needs.ui-tests.result != 'success') || (needs.integration-tests.result != 'success'))
+    if: always() && (needs.should-i-run.result == 'success' ) && (needs.ui-tests.result != 'success')
     # No concurrency required, runs every time on a schedule.
     steps:
       - uses: michaelkaye/matrix-hookshot-action@v1.0.0

From a1d2944c324c37bdcf9c92e0ec02864ce6781d71 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 09:08:40 +0100
Subject: [PATCH 293/400] Always check master key when provided by verifying
 device

---
 .../android/sdk/api/rendezvous/Rendezvous.kt  | 32 +++++++++++--------
 1 file changed, 18 insertions(+), 14 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
index e41a72db94..f724ac4b62 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt
@@ -165,21 +165,25 @@ class Rendezvous(
             }
 
             verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice ->
-                // check master key againt what the homeserver told us
-                crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey ->
-                    if (localMasterKey.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
-                        Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
-                        // inform the other side
-                        send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
-                        throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
-                    }
-                    // set other device as verified
-                    Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
-                    crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+                // verifying device provided us with a master key, so use it to check integrity
 
-                    Timber.tag(TAG).i("Setting master key as trusted")
-                    crypto.crossSigningService().markMyMasterKeyAsTrusted()
-                } ?: Timber.tag(TAG).w("No local master key so not verifying")
+                // see what the homeserver told us
+                val localMasterKey = crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()
+
+                // n.b. if no local master key this is a problem, as well as it not matching
+                if (localMasterKey?.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) {
+                    Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey")
+                    // inform the other side
+                    send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR))
+                    throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue)
+                }
+
+                // set other device as verified
+                Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")
+                crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId)
+
+                Timber.tag(TAG).i("Setting master key as trusted")
+                crypto.crossSigningService().markMyMasterKeyAsTrusted()
             } ?: run {
                 // set other device as verified anyway
                 Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified")

From 57a8dd4a1f61b50e180830d96264d5c3b1f1fe72 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 09:31:13 +0100
Subject: [PATCH 294/400] Whitespce

---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt  | 4 ++--
 .../transports/SimpleHttpRendezvousTransport.kt           | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 19c46db34d..f7da0efa92 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -81,7 +81,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     private var aesKey: ByteArray? = null
 
     init {
-        theirPublicKeyBase64 ?.let {
+        theirPublicKeyBase64?.let {
             theirPublicKey = Base64.decode(it, Base64.NO_WRAP)
         }
         olmSAS = OlmSAS()
@@ -142,7 +142,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
     }
 
     private suspend fun receiveAsPayload(): ECDHPayload? {
-        transport.receive()?.toString(Charsets.UTF_8) ?.let {
+        transport.receive()?.toString(Charsets.UTF_8)?.let {
             return ecdhAdapter.fromJson(it)
         } ?: return null
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 50cebae12d..3f7cfe786e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -69,7 +69,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
                 .method(method, data.toRequestBody())
                 .header("content-type", contentType.toString())
 
-        etag ?.let {
+        etag?.let {
             request.header("if-match", it)
         }
 
@@ -85,7 +85,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         if (method == "POST") {
             val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
 
-            response.header("expires") ?.let {
+            response.header("expires")?.let {
                 val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
                 expiresAt = format.parse(it)
             }
@@ -108,7 +108,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
                     .url(uri)
                     .get()
 
-            etag ?.let {
+            etag?.let {
                 request.header("if-none-match", it)
             }
 
@@ -154,7 +154,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         cancelled = true
         ready = false
 
-        uri ?.let {
+        uri?.let {
             try {
                 val httpClient = okhttp3.OkHttpClient.Builder().build()
                 val request = Request.Builder()

From 376cd1cb367c8fa3d3b9f9f2da898a56e0f3636e Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 09:34:28 +0100
Subject: [PATCH 295/400] Missing throws

---
 .../api/rendezvous/transports/SimpleHttpRendezvousTransport.kt  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 3f7cfe786e..7a5cbe5424 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -54,6 +54,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         return SimpleHttpRendezvousTransportDetails(uri)
     }
 
+    @Throws(RendezvousError::class)
     override suspend fun send(contentType: MediaType, data: ByteArray) {
         if (cancelled) {
             throw IllegalStateException("Rendezvous cancelled")
@@ -96,6 +97,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
         }
     }
 
+    @Throws(RendezvousError::class)
     override suspend fun receive(): ByteArray? {
         if (cancelled) {
             throw IllegalStateException("Rendezvous cancelled")

From 9fb0db3129ff6521ac86aba4bee29c12e6d45d5b Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:07:12 +0100
Subject: [PATCH 296/400] Update
 library/ui-strings/src/main/res/values/strings.xml

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 library/ui-strings/src/main/res/values/strings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 7fe8dd8d08..7b6b9b914d 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3383,7 +3383,7 @@
     <string name="qr_code_login_header_failed_timeout_description">The linking wasn’t completed in the required time.</string>
     <string name="qr_code_login_header_failed_denied_description">The request was denied on the other device.</string>
     <string name="qr_code_login_header_failed_other_description">The request failed.</string>
-    <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your intent connection(s); Your device(s);</string>
+    <string name="qr_code_login_header_failed_e2ee_security_issue_description">A security issue was encountered setting up secure messaging. One of the following may be compromised: Your homeserver; Your internet connection(s); Your device(s);</string>
     <string name="qr_code_login_header_failed_other_device_already_signed_in_description">The other device is already signed in.</string>
     <string name="qr_code_login_header_failed_other_device_not_signed_in_description">The other device must be signed in.</string>
     <string name="qr_code_login_header_failed_invalid_qr_code_description">That QR code is invalid.</string>

From 0d1df3f66e5587ba299b909183fa464673694489 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:08:09 +0100
Subject: [PATCH 297/400] Update
 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 .../org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index be79569164..9976c7a8e4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -45,7 +45,7 @@ interface RendezvousChannel {
     suspend fun receive(): ByteArray?
 
     /**
-     * @returns closes the channel and cleans up
+     * Closes the channel and cleans up.
      */
     suspend fun close()
 }

From 8530f8f28049dae1be38c2cf0503e8cfac04f2f6 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:09:06 +0100
Subject: [PATCH 298/400] Update
 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index f7da0efa92..b21d89059b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -73,7 +73,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             @Json val iv: String? = null
     )
 
-    private var olmSASMutex = Mutex()
+    private val olmSASMutex = Mutex()
     private var olmSAS: OlmSAS?
     private val ourPublicKey: ByteArray
     private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java)

From a83fb8bf83c10759c1e25e126e1fae5319cc4dad Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:09:17 +0100
Subject: [PATCH 299/400] Update
 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt

Co-authored-by: Benoit Marty <benoitm@matrix.org>
---
 .../org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
index 9976c7a8e4..0956a5b0a0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt
@@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
  * Representation of a rendezvous channel such as that described by MSC3903.
  */
 interface RendezvousChannel {
-    var transport: RendezvousTransport
+    val transport: RendezvousTransport
 
     /**
      * @returns the checksum/confirmation digits to be shown to the user

From 916ae654e790bc001a1f5f86ef30d32657da952d Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:11:41 +0100
Subject: [PATCH 300/400] Don't log whole QR code

---
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 1a8ed945c0..9bf3e955d3 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -76,7 +76,7 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) {
-        Timber.tag(TAG).d("Scanned code: ${action.qrCode}")
+        Timber.tag(TAG).d("Scanned code of length ${action.qrCode.length}")
 
         val rendezvous = try { Rendezvous.buildChannelFromCode(action.qrCode) } catch (t: Throwable) {
             Timber.tag(TAG).e(t, "Error occurred during sign in")

From 811d6d87ae50595726494abe772a8931d27da0c2 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:23:21 +0100
Subject: [PATCH 301/400] Reuse getDecimalCodeRepresentation from SAS instead
 of duplicating code

---
 .../channels/ECDHRendezvousChannel.kt         | 17 ++----
 .../SASDefaultVerificationTransaction.kt      | 54 +++++++++----------
 2 files changed, 30 insertions(+), 41 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index b21d89059b..6c45aacfaa 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.RendezvousTransport
 import org.matrix.android.sdk.api.rendezvous.model.RendezvousError
 import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm
 import org.matrix.android.sdk.api.util.MatrixJsonParser
-import org.matrix.android.sdk.internal.extensions.toUnsignedInt
+import org.matrix.android.sdk.internal.crypto.verification.SASDefaultVerificationTransaction
 import org.matrix.olm.OlmSAS
 import timber.log.Timber
 import java.security.SecureRandom
@@ -48,20 +48,9 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         private const val KEY_SPEC = "AES"
         private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
 
-        // n.b. we are only aver processing byte array that we have generated, so we can make assumptions about the length
+        // this is the same representation as for SAS but we delimit by dashes instead of spaces for readability
         private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-            val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
-            val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
-            val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
-            val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
-            val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
-            // (B0 << 5 | B1 >> 3) + 1000
-            val first = (b0.shl(5) or b1.shr(3)) + 1000
-            // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
-            val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
-            // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
-            val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-            return "$first-$second-$third"
+            return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(byteArray).replace(" ", "-")
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
index 1cbaff059a..b306288c5e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
@@ -82,6 +82,33 @@ internal abstract class SASDefaultVerificationTransaction(
         // older devices have limited support of emoji but SDK offers images for the 64 verification emojis
         // so always send that we support EMOJI
         val KNOWN_SHORT_CODES = listOf(SasMode.EMOJI, SasMode.DECIMAL)
+
+        /**
+         * decimal: generate five bytes by using HKDF.
+         * Take the first 13 bits and convert it to a decimal number (which will be a number between 0 and 8191 inclusive),
+         * and add 1000 (resulting in a number between 1000 and 9191 inclusive).
+         * Do the same with the second 13 bits, and the third 13 bits, giving three 4-digit numbers.
+         * In other words, if the five bytes are B0, B1, B2, B3, and B4, then the first number is (B0 << 5 | B1 >> 3) + 1000,
+         * the second number is ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000, and the third number is ((B3 & 0x3f) << 7 | B4 >> 1) + 1000.
+         * (This method of converting 13 bits at a time is used to avoid requiring 32-bit clients to do big-number arithmetic,
+         * and adding 1000 to the number avoids having clients to worry about properly zero-padding the number when displaying to the user.)
+         * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers,
+         * or with the three numbers on separate lines.
+         */
+        fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+            val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
+            val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
+            val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
+            val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
+            val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
+            // (B0 << 5 | B1 >> 3) + 1000
+            val first = (b0.shl(5) or b1.shr(3)) + 1000
+            // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
+            val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
+            // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
+            val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
+            return "$first $second $third"
+        }
     }
 
     override var state: VerificationTxState = VerificationTxState.None
@@ -371,33 +398,6 @@ internal abstract class SASDefaultVerificationTransaction(
         return getDecimalCodeRepresentation(shortCodeBytes!!)
     }
 
-    /**
-     * decimal: generate five bytes by using HKDF.
-     * Take the first 13 bits and convert it to a decimal number (which will be a number between 0 and 8191 inclusive),
-     * and add 1000 (resulting in a number between 1000 and 9191 inclusive).
-     * Do the same with the second 13 bits, and the third 13 bits, giving three 4-digit numbers.
-     * In other words, if the five bytes are B0, B1, B2, B3, and B4, then the first number is (B0 << 5 | B1 >> 3) + 1000,
-     * the second number is ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000, and the third number is ((B3 & 0x3f) << 7 | B4 >> 1) + 1000.
-     * (This method of converting 13 bits at a time is used to avoid requiring 32-bit clients to do big-number arithmetic,
-     * and adding 1000 to the number avoids having clients to worry about properly zero-padding the number when displaying to the user.)
-     * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers,
-     * or with the three numbers on separate lines.
-     */
-    fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-        val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
-        val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
-        val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
-        val b3 = byteArray[3].toUnsignedInt() // need unsigned byte
-        val b4 = byteArray[4].toUnsignedInt() // need unsigned byte
-        // (B0 << 5 | B1 >> 3) + 1000
-        val first = (b0.shl(5) or b1.shr(3)) + 1000
-        // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000
-        val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
-        // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
-        val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-        return "$first $second $third"
-    }
-
     override fun getEmojiCodeRepresentation(): List<EmojiRepresentation> {
         return getEmojiCodeRepresentation(shortCodeBytes!!)
     }

From f7e0a198335b9830af68404d522c834319adb784 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:29:48 +0100
Subject: [PATCH 302/400] Remove redundant annotations

---
 .../api/rendezvous/channels/ECDHRendezvousChannel.kt |  8 ++++----
 .../sdk/api/rendezvous/model/ECDHRendezvous.kt       |  7 +++----
 .../sdk/api/rendezvous/model/ECDHRendezvousCode.kt   |  5 ++---
 .../android/sdk/api/rendezvous/model/Payload.kt      | 12 ++++++------
 .../rendezvous/model/RendezvousTransportDetails.kt   |  3 +--
 .../model/SimpleHttpRendezvousTransportDetails.kt    |  3 +--
 6 files changed, 17 insertions(+), 21 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 6c45aacfaa..4bbf34280c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -56,10 +56,10 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
 
     @JsonClass(generateAdapter = true)
     internal data class ECDHPayload(
-            @Json val algorithm: SecureRendezvousChannelAlgorithm? = null,
-            @Json val key: String? = null,
-            @Json val ciphertext: String? = null,
-            @Json val iv: String? = null
+            val algorithm: SecureRendezvousChannelAlgorithm? = null,
+            val key: String? = null,
+            val ciphertext: String? = null,
+            val iv: String? = null
     )
 
     private val olmSASMutex = Mutex()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
index 0840e1ca2e..55bac6397e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt
@@ -16,12 +16,11 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvous(
-        @Json val transport: SimpleHttpRendezvousTransportDetails,
-        @Json val algorithm: SecureRendezvousChannelAlgorithm,
-        @Json val key: String
+        val transport: SimpleHttpRendezvousTransportDetails,
+        val algorithm: SecureRendezvousChannelAlgorithm,
+        val key: String
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
index 410c5c1036..575b5d4bfd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt
@@ -16,11 +16,10 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 data class ECDHRendezvousCode(
-        @Json val intent: RendezvousIntent,
-        @Json val rendezvous: ECDHRendezvous
+        val intent: RendezvousIntent,
+        val rendezvous: ECDHRendezvous
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
index 593177e625..04631ce959 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt
@@ -21,12 +21,12 @@ import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 internal data class Payload(
-        @Json val type: PayloadType,
-        @Json val intent: RendezvousIntent? = null,
-        @Json val outcome: Outcome? = null,
-        @Json val protocols: List<Protocol>? = null,
-        @Json val protocol: Protocol? = null,
-        @Json val homeserver: String? = null,
+        val type: PayloadType,
+        val intent: RendezvousIntent? = null,
+        val outcome: Outcome? = null,
+        val protocols: List<Protocol>? = null,
+        val protocol: Protocol? = null,
+        val homeserver: String? = null,
         @Json(name = "login_token") val loginToken: String? = null,
         @Json(name = "device_id") val deviceId: String? = null,
         @Json(name = "device_key") val deviceKey: String? = null,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
index 55b3bbb5d9..1bde43ab7e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt
@@ -16,10 +16,9 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 open class RendezvousTransportDetails(
-    @Json val type: RendezvousTransportType
+    val type: RendezvousTransportType
 )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
index 70a441d760..049aa8b756 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt
@@ -16,10 +16,9 @@
 
 package org.matrix.android.sdk.api.rendezvous.model
 
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 data class SimpleHttpRendezvousTransportDetails(
-        @Json val uri: String
+        val uri: String
 ) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1)

From 400118ed3ec8eb19bb4ba6011f5b3f5c6c0816b7 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 13:03:57 +0200
Subject: [PATCH 303/400] Remove useless Android API checks

---
 .../app/features/voice/AbstractVoiceRecorderQ.kt     | 12 ++----------
 .../usecase/StopVoiceBroadcastUseCase.kt             |  5 +----
 2 files changed, 3 insertions(+), 14 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
index 428b3c578b..0d8373870f 100644
--- a/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voice/AbstractVoiceRecorderQ.kt
@@ -91,11 +91,7 @@ abstract class AbstractVoiceRecorderQ(private val context: Context) : AbstractVo
     fun setNextOutputFile(roomId: String) {
         val mediaRecorder = mediaRecorder ?: return
         nextOutputFile = createOutputFile(roomId)
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            mediaRecorder.setNextOutputFile(nextOutputFile)
-        } else {
-            mediaRecorder.setNextOutputFile(nextOutputFile?.outputStream()?.fd)
-        }
+        mediaRecorder.setNextOutputFile(nextOutputFile)
     }
 
     private fun createMediaRecorder(): MediaRecorder {
@@ -115,10 +111,6 @@ abstract class AbstractVoiceRecorderQ(private val context: Context) : AbstractVo
     private fun setOutputFile(roomId: String) {
         val mediaRecorder = mediaRecorder ?: return
         outputFile = outputFile ?: createOutputFile(roomId)
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            mediaRecorder.setOutputFile(outputFile)
-        } else {
-            mediaRecorder.setOutputFile(outputFile?.outputStream()?.fd)
-        }
+        mediaRecorder.setOutputFile(outputFile)
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
index 85ffde0d02..6eefa06979 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
@@ -16,7 +16,6 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import android.os.Build
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
@@ -68,8 +67,6 @@ class StopVoiceBroadcastUseCase @Inject constructor(
     }
 
     private fun stopRecording() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-            voiceBroadcastRecorder?.stopRecord()
-        }
+        voiceBroadcastRecorder?.stopRecord()
     }
 }

From def9fc07bbd5b8404bd0d70dd03d0bcfaa03c555 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 13:08:17 +0200
Subject: [PATCH 304/400] Revert AudioMessageHelper.pauseRecording

---
 .../room/detail/composer/AudioMessageHelper.kt     | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
index d98240904c..bede02c17f 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt
@@ -114,13 +114,9 @@ class AudioMessageHelper @Inject constructor(
      * When entering in playback mode actually.
      */
     fun pauseRecording() {
-        voiceRecorder.pauseRecord()
-        pauseRecordingAmplitudes()
-    }
-
-    fun resumeRecording() {
-        voiceRecorder.resumeRecord()
-        resumeRecordingAmplitudes()
+        // TODO should we pause instead of stop?
+        voiceRecorder.stopRecord()
+        stopRecordingAmplitudes()
     }
 
     fun deleteRecording() {
@@ -226,10 +222,6 @@ class AudioMessageHelper @Inject constructor(
         }
     }
 
-    private fun pauseRecordingAmplitudes() {
-        amplitudeTicker?.pause()
-    }
-
     private fun resumeRecordingAmplitudes() {
         amplitudeTicker?.resume()
     }

From 92bd8cdcfe71bf01f780f14bf1a41c0765c5b977 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 13:09:48 +0200
Subject: [PATCH 305/400] Voice Broadcast - Remove check on voice message
 minimum duration

---
 .../usecase/StartVoiceBroadcastUseCase.kt          | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 64f1dfb317..81a98f6fce 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -93,13 +93,11 @@ class StartVoiceBroadcastUseCase @Inject constructor(
                 "Voice message.${voiceMessageFile.extension}"
         )
         val audioType = outputFileUri.toMultiPickerAudioType(context) ?: return
-        if (audioType.duration > 1000) {
-            room.sendService().sendMedia(
-                    attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
-                    compressBeforeSending = false,
-                    roomIds = emptySet(),
-                    relatesTo = RelationDefaultContent(RelationType.REFERENCE, referenceEventId)
-            )
-        }
+        room.sendService().sendMedia(
+                attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
+                compressBeforeSending = false,
+                roomIds = emptySet(),
+                relatesTo = RelationDefaultContent(RelationType.REFERENCE, referenceEventId)
+        )
     }
 }

From b9335c60657a114104a62e7a8a59e9ccb0b28c6e Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 13:11:44 +0200
Subject: [PATCH 306/400] Rename const DEFAULT_CHUNK_LENGTH_IN_SECONDS

---
 .../app/features/voicebroadcast/VoiceBroadcastConstants.kt      | 2 +-
 .../voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index bd37251dd4..3a9aac12d5 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -22,5 +22,5 @@ object VoiceBroadcastConstants {
     const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
 
     /** Default voice broadcast chunk duration, in seconds. */
-    const val DEFAULT_CHUNK_LENGTH = 30
+    const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 30
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 81a98f6fce..2a306bcd28 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -65,7 +65,7 @@ class StartVoiceBroadcastUseCase @Inject constructor(
 
     private suspend fun startVoiceBroadcast(room: Room) {
         Timber.d("## StartVoiceBroadcastUseCase: Send new voice broadcast info state event")
-        val chunkLength = VoiceBroadcastConstants.DEFAULT_CHUNK_LENGTH // Todo Get the length from the room settings
+        val chunkLength = VoiceBroadcastConstants.DEFAULT_CHUNK_LENGTH_IN_SECONDS // Todo Get the length from the room settings
         val eventId = room.stateService().sendStateEvent(
                 eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,

From ce14270fab1eb001b8c74ee68bd003bbd790ab27 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 17 Oct 2022 07:18:21 +0200
Subject: [PATCH 307/400] Introduce MessageAudioEvent

---
 .../room/model/message/MessageAudioEvent.kt   | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioEvent.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioEvent.kt
new file mode 100644
index 0000000000..38ced8f385
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioEvent.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.api.session.room.model.message
+
+import org.matrix.android.sdk.api.extensions.tryOrNull
+import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.events.model.toModel
+
+/**
+ * [Event] wrapper for [EventType.MESSAGE] event type.
+ * Provides additional fields and functions related to this event type.
+ */
+@JvmInline
+value class MessageAudioEvent(val root: Event) {
+
+    /**
+     * The mapped [MessageAudioContent] model of the event content.
+     */
+    val content: MessageAudioContent
+        get() = root.getClearContent().toModel<MessageContent>() as MessageAudioContent
+
+    init {
+        require(tryOrNull { content } != null)
+    }
+}
+
+/**
+ * Map a [EventType.MESSAGE] event to a [MessageAudioEvent].
+ */
+fun Event.asMessageAudioEvent() = if (getClearType() == EventType.MESSAGE) {
+    tryOrNull { MessageAudioEvent(this) }
+} else null

From 5f35926ce6cdeca71cca9f3b4c3364ab040d084b Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 17 Oct 2022 11:21:15 +0200
Subject: [PATCH 308/400] Voice Broadcast - Hide related voice message events

---
 .../android/sdk/api/session/events/model/Event.kt     |  5 +++++
 .../detail/timeline/factory/MessageItemFactory.kt     |  7 +++++--
 .../detail/timeline/helper/TimelineEventsGroups.kt    | 11 +++++++++--
 3 files changed, 19 insertions(+), 4 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 f5d2c0d9a0..71daf4cc4f 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
@@ -31,6 +31,7 @@ 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
 import org.matrix.android.sdk.api.session.room.model.message.MessageType
+import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
 import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
 import org.matrix.android.sdk.api.session.room.model.relation.shouldRenderInThread
 import org.matrix.android.sdk.api.session.room.send.SendState
@@ -357,6 +358,10 @@ fun Event.isAudioMessage(): Boolean {
     }
 }
 
+fun Event.isVoiceMessage(): Boolean {
+    return this.asMessageAudioEvent()?.content?.voiceMessageIndicator != null
+}
+
 fun Event.isFileMessage(): Boolean {
     return when (getMsgType()) {
         MessageType.MSGTYPE_FILE -> true
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 06da69fc1a..d39f933363 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
@@ -323,7 +323,10 @@ class MessageItemFactory @Inject constructor(
             informationData: MessageInformationData,
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes
-    ): MessageVoiceItem {
+    ): MessageVoiceItem? {
+        val eventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) }
+        if (eventsGroup != null && eventsGroup.getLastDisplayableEvent().eventId != params.event.eventId) return null
+
         val fileUrl = getAudioFileUrl(messageContent, informationData)
         val playbackControlButtonClickListener = createOnPlaybackButtonClickListener(messageContent, informationData, params)
 
@@ -722,7 +725,7 @@ class MessageItemFactory @Inject constructor(
     ): MessageVoiceBroadcastItem? {
         if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
         val voiceBroadcastEventsGroup = eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
-        val mostRecentEvent = voiceBroadcastEventsGroup.getLastEvent()
+        val mostRecentEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
         val mostRecentMessageContent = (mostRecentEvent.getVectorLastMessageContent() as? MessageVoiceBroadcastInfoContent) ?: return null
         return MessageVoiceBroadcastItem_()
                 .attributes(attributes)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
index 13de456e84..ccfdac8f15 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
@@ -22,6 +22,9 @@ import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.events.model.RelationType
+import org.matrix.android.sdk.api.session.events.model.getRelationContent
+import org.matrix.android.sdk.api.session.events.model.isVoiceMessage
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@@ -61,6 +64,10 @@ class TimelineEventsGroups {
             EventType.isCallEvent(type) -> (content?.get("call_id") as? String)
             type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> root.asVoiceBroadcastEvent()?.reference?.eventId
             type == EventType.STATE_ROOM_WIDGET || type == EventType.STATE_ROOM_WIDGET_LEGACY -> root.stateKey
+            type == EventType.MESSAGE && root.isVoiceMessage() -> {
+                // Group voice messages with a reference to an eventId
+                root.getRelationContent()?.takeIf { it.type == RelationType.REFERENCE }?.eventId
+            }
             else -> {
                 null
             }
@@ -134,8 +141,8 @@ class CallSignalingEventsGroup(private val group: TimelineEventsGroup) {
 }
 
 class VoiceBroadcastEventsGroup(private val group: TimelineEventsGroup) {
-    fun getLastEvent(): TimelineEvent {
+    fun getLastDisplayableEvent(): TimelineEvent {
         return group.events.find { it.root.asVoiceBroadcastEvent()?.content?.voiceBroadcastState == VoiceBroadcastState.STOPPED }
-                ?: group.events.maxBy { it.root.originServerTs ?: 0L }
+                ?: group.events.filter { it.root.type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO }.maxBy { it.root.originServerTs ?: 0L }
     }
 }

From 032c0152e41c856da0ca8e73df0ad8aa33e4207f Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 17 Oct 2022 15:45:07 +0200
Subject: [PATCH 309/400] Voice Broadcast - Move timeline item creation to
 dedicated factory

---
 .../timeline/factory/MessageItemFactory.kt    | 26 +--------
 .../factory/VoiceBroadcastItemFactory.kt      | 53 +++++++++++++++++++
 2 files changed, 55 insertions(+), 24 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt

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 d39f933363..8568f2f342 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
@@ -43,7 +43,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStat
 import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
 import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
 import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
-import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventsGroup
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
 import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
@@ -58,8 +57,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem_
-import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem
-import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem_
 import im.vector.app.features.home.room.detail.timeline.item.PollItem
@@ -83,7 +80,6 @@ import im.vector.app.features.media.VideoContentRenderer
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.voice.AudioWaveformView
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
-import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
 import me.gujun.android.span.span
 import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
@@ -141,6 +137,7 @@ class MessageItemFactory @Inject constructor(
         private val urlMapProvider: UrlMapProvider,
         private val liveLocationShareMessageItemFactory: LiveLocationShareMessageItemFactory,
         private val pollItemViewStateFactory: PollItemViewStateFactory,
+        private val voiceBroadcastItemFactory: VoiceBroadcastItemFactory,
 ) {
 
     // TODO inject this properly?
@@ -203,7 +200,7 @@ class MessageItemFactory @Inject constructor(
             is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes)
             is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, attributes)
             is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes)
-            is MessageVoiceBroadcastInfoContent -> buildVoiceBroadcastItem(messageContent, params.eventsGroup, highlight, callback, attributes)
+            is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(messageContent, params.eventsGroup, highlight, callback, attributes)
             else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
         }
         return messageItem?.apply {
@@ -716,25 +713,6 @@ class MessageItemFactory @Inject constructor(
                 .highlighted(highlight)
     }
 
-    private fun buildVoiceBroadcastItem(
-            messageContent: MessageVoiceBroadcastInfoContent,
-            eventsGroup: TimelineEventsGroup?,
-            highlight: Boolean,
-            callback: TimelineEventController.Callback?,
-            attributes: AbsMessageItem.Attributes,
-    ): MessageVoiceBroadcastItem? {
-        if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
-        val voiceBroadcastEventsGroup = eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
-        val mostRecentEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
-        val mostRecentMessageContent = (mostRecentEvent.getVectorLastMessageContent() as? MessageVoiceBroadcastInfoContent) ?: return null
-        return MessageVoiceBroadcastItem_()
-                .attributes(attributes)
-                .highlighted(highlight)
-                .voiceBroadcastState(mostRecentMessageContent.voiceBroadcastState)
-                .leftGuideline(avatarSizeProvider.leftGuideline)
-                .callback(callback)
-    }
-
     private fun List<Int?>?.toFft(): List<Int>? {
         return this
                 ?.filterNotNull()
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
new file mode 100644
index 0000000000..aae2f67631
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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.factory
+
+import im.vector.app.core.extensions.getVectorLastMessageContent
+import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
+import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventsGroup
+import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
+import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem_
+import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
+import javax.inject.Inject
+
+class VoiceBroadcastItemFactory @Inject constructor(
+        private val avatarSizeProvider: AvatarSizeProvider,
+) {
+
+    fun create(
+            messageContent: MessageVoiceBroadcastInfoContent,
+            eventsGroup: TimelineEventsGroup?,
+            highlight: Boolean,
+            callback: TimelineEventController.Callback?,
+            attributes: AbsMessageItem.Attributes,
+    ): MessageVoiceBroadcastItem? {
+        // Only display item of the initial event with updated data
+        if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
+        val voiceBroadcastEventsGroup = eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
+        val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
+        val mostRecentMessageContent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()?.content ?: return null
+        return MessageVoiceBroadcastItem_()
+                .attributes(attributes)
+                .highlighted(highlight)
+                .voiceBroadcastState(mostRecentMessageContent.voiceBroadcastState)
+                .leftGuideline(avatarSizeProvider.leftGuideline)
+                .callback(callback)
+    }
+}

From eb44b022288e2a897d87768db59f52a374c35af0 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 17 Oct 2022 06:49:01 +0200
Subject: [PATCH 310/400] Create VoiceBroadcastMediaPlayer

---
 .../voicebroadcast/VoiceBroadcastPlayer.kt    | 163 ++++++++++++++++++
 1 file changed, 163 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
new file mode 100644
index 0000000000..7e30347d18
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.voicebroadcast
+
+import android.media.AudioAttributes
+import android.media.MediaPlayer
+import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
+import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State
+import im.vector.app.features.voice.VoiceFailure
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.events.model.getRelationContent
+import org.matrix.android.sdk.api.session.getRoom
+import org.matrix.android.sdk.api.session.room.Room
+import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
+import timber.log.Timber
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class VoiceBroadcastPlayer @Inject constructor(
+        private val session: Session,
+        private val playbackTracker: AudioMessagePlaybackTracker,
+) {
+
+    private val mediaPlayerScope = CoroutineScope(Dispatchers.IO)
+
+    private var currentMediaPlayer: MediaPlayer? = null
+    private var currentPlayingIndex: Int = -1
+    private var playlist = emptyList<MessageAudioEvent>()
+    private val currentVoiceBroadcastEventId
+        get() = playlist.firstOrNull()?.root?.getRelationContent()?.eventId
+
+    private val mediaPlayerListener = MediaPlayerListener()
+
+    fun play(roomId: String, eventId: String) {
+        val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
+
+        when {
+            currentVoiceBroadcastEventId != eventId -> {
+                stop()
+                updatePlaylist(room, eventId)
+                startPlayback()
+            }
+            playbackTracker.getPlaybackState(eventId) is State.Playing -> pause()
+            else -> resumePlayback()
+        }
+    }
+
+    fun pause() {
+        currentMediaPlayer?.pause()
+        currentVoiceBroadcastEventId?.let { playbackTracker.pausePlayback(it) }
+    }
+
+    fun stop() {
+        currentMediaPlayer?.stop()
+        currentMediaPlayer?.release()
+        currentMediaPlayer?.setOnInfoListener(null)
+        currentMediaPlayer = null
+        currentVoiceBroadcastEventId?.let { playbackTracker.stopPlayback(it) }
+        playlist = emptyList()
+        currentPlayingIndex = -1
+    }
+
+    @Suppress("UNUSED_PARAMETER")
+    private fun updatePlaylist(room: Room, eventId: String) {
+        // TODO get the list of voice messages
+    }
+
+    private fun startPlayback() {
+        val content = playlist.firstOrNull()?.content ?: run { Timber.w("## VoiceBroadcastPlayer: No content to play"); return }
+        mediaPlayerScope.launch {
+            try {
+                currentMediaPlayer = prepareMediaPlayer(content)
+                currentMediaPlayer?.start()
+                currentPlayingIndex = 0
+                currentVoiceBroadcastEventId?.let { playbackTracker.startPlayback(it) }
+                prepareNextFile()
+            } catch (failure: Throwable) {
+                Timber.e(failure, "Unable to start playback")
+                throw VoiceFailure.UnableToPlay(failure)
+            }
+        }
+    }
+
+    private fun resumePlayback() {
+        currentMediaPlayer?.start()
+        currentVoiceBroadcastEventId?.let { playbackTracker.startPlayback(it) }
+    }
+
+    private suspend fun prepareNextFile() {
+        val nextContent = playlist.getOrNull(currentPlayingIndex + 1)?.content
+        if (nextContent == null) {
+            currentMediaPlayer?.setOnCompletionListener(mediaPlayerListener)
+        } else {
+            val nextMediaPlayer = prepareMediaPlayer(nextContent)
+            currentMediaPlayer?.setNextMediaPlayer(nextMediaPlayer)
+        }
+    }
+
+    private suspend fun prepareMediaPlayer(messageAudioContent: MessageAudioContent): MediaPlayer {
+        val audioFile = session.fileService().downloadFile(messageAudioContent)
+        return audioFile.inputStream().use { fis ->
+            MediaPlayer().apply {
+                setAudioAttributes(
+                        AudioAttributes.Builder()
+                                // Do not use CONTENT_TYPE_SPEECH / USAGE_VOICE_COMMUNICATION because we want to play loud here
+                                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+                                .setUsage(AudioAttributes.USAGE_MEDIA)
+                                .build()
+                )
+                setDataSource(fis.fd)
+                setOnInfoListener(mediaPlayerListener)
+                setOnErrorListener(mediaPlayerListener)
+                prepare()
+            }
+        }
+    }
+
+    inner class MediaPlayerListener : MediaPlayer.OnInfoListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
+
+        override fun onInfo(mp: MediaPlayer, what: Int, extra: Int): Boolean {
+            when (what) {
+                MediaPlayer.MEDIA_INFO_STARTED_AS_NEXT -> {
+                    currentMediaPlayer = mp
+                    currentPlayingIndex++
+                    mediaPlayerScope.launch { prepareNextFile() }
+                }
+            }
+            return false
+        }
+
+        override fun onCompletion(mp: MediaPlayer) {
+            // Verify that a new media has not been set in the mean time
+            if (!currentMediaPlayer?.isPlaying.orFalse()) {
+                stop()
+            }
+        }
+
+        override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
+            stop()
+            return true
+        }
+    }
+}

From 2760781f0af6e766beb52fc9ce5c78e8fd4087bc Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 17 Oct 2022 22:53:16 +0200
Subject: [PATCH 311/400] Voice Broadcast - Introduce listening actions

---
 .../home/room/detail/RoomDetailAction.kt         | 16 ++++++++++++----
 .../home/room/detail/TimelineViewModel.kt        | 11 +++++++----
 .../detail/composer/MessageComposerFragment.kt   |  4 ++--
 .../voicebroadcast/VoiceBroadcastHelper.kt       |  9 ++++++++-
 4 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
index 3e828f62b7..f773671694 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
@@ -121,9 +121,17 @@ sealed class RoomDetailAction : VectorViewModelAction {
     object OpenElementCallWidget : RoomDetailAction()
 
     sealed class VoiceBroadcastAction : RoomDetailAction() {
-        object Start : VoiceBroadcastAction()
-        object Pause : VoiceBroadcastAction()
-        object Resume : VoiceBroadcastAction()
-        object Stop : VoiceBroadcastAction()
+        sealed class Recording : VoiceBroadcastAction() {
+            object Start : Recording()
+            object Pause : Recording()
+            object Resume : Recording()
+            object Stop : Recording()
+        }
+
+        sealed class Listening : VoiceBroadcastAction() {
+            data class PlayOrResume(val eventId: String) : Listening()
+            object Pause : Listening()
+            object Stop : Listening()
+        }
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
index 511fd597fe..82ad96d645 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
@@ -604,10 +604,13 @@ class TimelineViewModel @AssistedInject constructor(
         if (room == null) return
         viewModelScope.launch {
             when (action) {
-                RoomDetailAction.VoiceBroadcastAction.Start -> voiceBroadcastHelper.startVoiceBroadcast(room.roomId)
-                RoomDetailAction.VoiceBroadcastAction.Pause -> voiceBroadcastHelper.pauseVoiceBroadcast(room.roomId)
-                RoomDetailAction.VoiceBroadcastAction.Resume -> voiceBroadcastHelper.resumeVoiceBroadcast(room.roomId)
-                RoomDetailAction.VoiceBroadcastAction.Stop -> voiceBroadcastHelper.stopVoiceBroadcast(room.roomId)
+                RoomDetailAction.VoiceBroadcastAction.Recording.Start -> voiceBroadcastHelper.startVoiceBroadcast(room.roomId)
+                RoomDetailAction.VoiceBroadcastAction.Recording.Pause -> voiceBroadcastHelper.pauseVoiceBroadcast(room.roomId)
+                RoomDetailAction.VoiceBroadcastAction.Recording.Resume -> voiceBroadcastHelper.resumeVoiceBroadcast(room.roomId)
+                RoomDetailAction.VoiceBroadcastAction.Recording.Stop -> voiceBroadcastHelper.stopVoiceBroadcast(room.roomId)
+                is RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume -> voiceBroadcastHelper.playOrResumePlayback(room.roomId, action.eventId)
+                RoomDetailAction.VoiceBroadcastAction.Listening.Pause -> voiceBroadcastHelper.pausePlayback()
+                RoomDetailAction.VoiceBroadcastAction.Listening.Stop -> voiceBroadcastHelper.stopPlayback()
             }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
index 4721b81571..9954470e48 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
@@ -234,7 +234,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
                 }
                 // TODO remove this when there will be a recording indicator outside of the timeline
                 // Pause voice broadcast if the timeline is not shown anymore
-                it.isVoiceBroadcasting && !requireActivity().isChangingConfigurations -> timelineViewModel.handle(VoiceBroadcastAction.Pause)
+                it.isVoiceBroadcasting && !requireActivity().isChangingConfigurations -> timelineViewModel.handle(VoiceBroadcastAction.Recording.Pause)
                 else -> {
                     messageComposerViewModel.handle(MessageComposerAction.OnEntersBackground(composer.text.toString()))
                 }
@@ -684,7 +684,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
                                 locationOwnerId = session.myUserId
                         )
             }
-            AttachmentTypeSelectorView.Type.VOICE_BROADCAST -> timelineViewModel.handle(VoiceBroadcastAction.Start)
+            AttachmentTypeSelectorView.Type.VOICE_BROADCAST -> timelineViewModel.handle(VoiceBroadcastAction.Recording.Start)
         }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt
index f682cd2f5e..b967afa9cb 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.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.
@@ -30,6 +30,7 @@ class VoiceBroadcastHelper @Inject constructor(
         private val pauseVoiceBroadcastUseCase: PauseVoiceBroadcastUseCase,
         private val resumeVoiceBroadcastUseCase: ResumeVoiceBroadcastUseCase,
         private val stopVoiceBroadcastUseCase: StopVoiceBroadcastUseCase,
+        private val voiceBroadcastPlayer: VoiceBroadcastPlayer,
 ) {
     suspend fun startVoiceBroadcast(roomId: String) = startVoiceBroadcastUseCase.execute(roomId)
 
@@ -38,4 +39,10 @@ class VoiceBroadcastHelper @Inject constructor(
     suspend fun resumeVoiceBroadcast(roomId: String) = resumeVoiceBroadcastUseCase.execute(roomId)
 
     suspend fun stopVoiceBroadcast(roomId: String) = stopVoiceBroadcastUseCase.execute(roomId)
+
+    fun playOrResumePlayback(roomId: String, eventId: String) = voiceBroadcastPlayer.play(roomId, eventId)
+
+    fun pausePlayback() = voiceBroadcastPlayer.pause()
+
+    fun stopPlayback() = voiceBroadcastPlayer.stop()
 }

From 215128c213e94d3e8a9e85271d26df133f1753cf Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 17 Oct 2022 22:58:12 +0200
Subject: [PATCH 312/400] Voice Broadcast - Add timeline item listening state

---
 .../factory/VoiceBroadcastItemFactory.kt      | 12 +++++-
 .../item/MessageVoiceBroadcastItem.kt         | 42 ++++++++++++++++---
 2 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index aae2f67631..f2dfb020a1 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -15,8 +15,8 @@
  */
 package im.vector.app.features.home.room.detail.timeline.factory
 
-import im.vector.app.core.extensions.getVectorLastMessageContent
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
 import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventsGroup
 import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
@@ -25,10 +25,14 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadca
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem_
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
+import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
+import org.matrix.android.sdk.api.session.Session
 import javax.inject.Inject
 
 class VoiceBroadcastItemFactory @Inject constructor(
+        private val session: Session,
         private val avatarSizeProvider: AvatarSizeProvider,
+        private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker,
 ) {
 
     fun create(
@@ -42,11 +46,15 @@ class VoiceBroadcastItemFactory @Inject constructor(
         if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
         val voiceBroadcastEventsGroup = eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
         val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
-        val mostRecentMessageContent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()?.content ?: return null
+        val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()
+        val mostRecentMessageContent = mostRecentEvent?.content ?: return null
+        val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId
         return MessageVoiceBroadcastItem_()
                 .attributes(attributes)
                 .highlighted(highlight)
                 .voiceBroadcastState(mostRecentMessageContent.voiceBroadcastState)
+                .recording(isRecording)
+                .audioMessagePlaybackTracker(audioMessagePlaybackTracker)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .callback(callback)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt
index 14a4fc6b07..1927024a36 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt
@@ -22,8 +22,10 @@ import android.widget.TextView
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.app.R
-import im.vector.app.features.home.room.detail.RoomDetailAction
+import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
+import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 
 @EpoxyModelClass
@@ -35,6 +37,15 @@ abstract class MessageVoiceBroadcastItem : AbsMessageItem<MessageVoiceBroadcastI
     @EpoxyAttribute
     var voiceBroadcastState: VoiceBroadcastState? = null
 
+    @EpoxyAttribute
+    var recording: Boolean = false
+
+    @EpoxyAttribute
+    lateinit var audioMessagePlaybackTracker: AudioMessagePlaybackTracker
+
+    private val voiceBroadcastEventId
+        get() = attributes.informationData.eventId
+
     override fun isCacheable(): Boolean = false
 
     override fun bind(holder: Holder) {
@@ -44,16 +55,37 @@ abstract class MessageVoiceBroadcastItem : AbsMessageItem<MessageVoiceBroadcastI
 
     @SuppressLint("SetTextI18n") // Temporary text
     private fun bindVoiceBroadcastItem(holder: Holder) {
+        holder.currentStateText.text = "Voice Broadcast state: ${voiceBroadcastState?.value ?: "None"}"
+        if (recording) {
+            renderRecording(holder)
+        } else {
+            renderListening(holder)
+        }
+    }
+
+    private fun renderListening(holder: Holder) {
+        audioMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener {
+            override fun onUpdate(state: State) {
+                holder.playButton.isEnabled = state !is State.Playing
+                holder.pauseButton.isEnabled = state is State.Playing
+                holder.stopButton.isEnabled = state !is State.Idle
+            }
+        })
+        holder.playButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastEventId)) }
+        holder.pauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.Pause) }
+        holder.stopButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.Stop) }
+    }
+
+    private fun renderRecording(holder: Holder) {
         with(holder) {
-            currentStateText.text = "Voice Broadcast state: ${voiceBroadcastState?.value ?: "None"}"
             playButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.PAUSED
             pauseButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.STARTED || voiceBroadcastState == VoiceBroadcastState.RESUMED
             stopButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.STARTED ||
                     voiceBroadcastState == VoiceBroadcastState.RESUMED ||
                     voiceBroadcastState == VoiceBroadcastState.PAUSED
-            playButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Resume) }
-            pauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Pause) }
-            stopButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Stop) }
+            playButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
+            pauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
+            stopButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
         }
     }
 

From 16c27ba174bf3eea3a426a4d316f12847ab86e87 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Mon, 17 Oct 2022 23:14:42 +0200
Subject: [PATCH 313/400] Voice Broadcast - Get voice messages events related
 to a given VB

---
 .../api/session/room/timeline/TimelineService.kt  |  5 +++++
 .../room/timeline/DefaultTimelineService.kt       |  4 ++++
 .../room/timeline/TimelineEventDataSource.kt      | 15 +++++++++++++++
 .../voicebroadcast/VoiceBroadcastPlayer.kt        |  7 +++++--
 4 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt
index 46433f387d..aa9afd5c8c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineService.kt
@@ -55,4 +55,9 @@ interface TimelineService {
      * Returns a snapshot list of TimelineEvent with EventType.MESSAGE and MessageType.MSGTYPE_IMAGE or MessageType.MSGTYPE_VIDEO.
      */
     fun getAttachmentMessages(): List<TimelineEvent>
+
+    /**
+     * Returns a snapshot list of TimelineEvent with a content relation of the given type to the given eventId.
+     */
+    fun getTimelineEventsRelatedTo(relationType: String, eventId: String): List<TimelineEvent>
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
index 53c0253876..b1a3d51b36 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt
@@ -96,4 +96,8 @@ internal class DefaultTimelineService @AssistedInject constructor(
     override fun getAttachmentMessages(): List<TimelineEvent> {
         return timelineEventDataSource.getAttachmentMessages(roomId)
     }
+
+    override fun getTimelineEventsRelatedTo(relationType: String, eventId: String): List<TimelineEvent> {
+        return timelineEventDataSource.getTimelineEventsRelatedTo(roomId, relationType, eventId)
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt
index b1b9e4bb22..20094e4be8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt
@@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room.timeline
 import androidx.lifecycle.LiveData
 import com.zhuinden.monarchy.Monarchy
 import io.realm.Sort
+import org.matrix.android.sdk.api.session.events.model.getRelationContent
 import org.matrix.android.sdk.api.session.events.model.isImageMessage
 import org.matrix.android.sdk.api.session.events.model.isVideoMessage
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@@ -63,4 +64,18 @@ internal class TimelineEventDataSource @Inject constructor(
                     .orEmpty()
         }
     }
+
+    fun getTimelineEventsRelatedTo(roomId: String, eventType: String, eventId: String): List<TimelineEvent> {
+        // TODO Remove this trick and call relations API
+        // see https://spec.matrix.org/latest/client-server-api/#get_matrixclientv1roomsroomidrelationseventidreltypeeventtype
+        return realmSessionProvider.withRealm { realm ->
+            TimelineEventEntity.whereRoomId(realm, roomId)
+                    .sort(TimelineEventEntityFields.ROOT.ORIGIN_SERVER_TS, Sort.ASCENDING)
+                    .distinct(TimelineEventEntityFields.EVENT_ID)
+                    .findAll()
+                    .mapNotNull {
+                        timelineEventMapper.map(it).takeIf { it.root.getRelationContent()?.takeIf { it.type == eventType && it.eventId == eventId } != null }
+                    }
+        }
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 7e30347d18..8970a65990 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -26,11 +26,13 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.events.model.RelationType
 import org.matrix.android.sdk.api.session.events.model.getRelationContent
 import org.matrix.android.sdk.api.session.getRoom
 import org.matrix.android.sdk.api.session.room.Room
 import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
+import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -80,9 +82,10 @@ class VoiceBroadcastPlayer @Inject constructor(
         currentPlayingIndex = -1
     }
 
-    @Suppress("UNUSED_PARAMETER")
     private fun updatePlaylist(room: Room, eventId: String) {
-        // TODO get the list of voice messages
+        val timelineEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId)
+        val audioEvents = timelineEvents.mapNotNull { it.root.asMessageAudioEvent() }
+        playlist = audioEvents.sortedBy { it.root.originServerTs }
     }
 
     private fun startPlayback() {

From 402224721b2457547b41e1d6f2ad625b3fb8a33a Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 00:37:12 +0200
Subject: [PATCH 314/400] Voice Broadcast - Add voice message extensions

---
 .../timeline/factory/MessageItemFactory.kt    |  7 +++--
 .../timeline/helper/TimelineEventsGroups.kt   | 10 +++----
 .../VoiceBroadcastExtensions.kt               | 29 +++++++++++++++++++
 3 files changed, 38 insertions(+), 8 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt

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 8568f2f342..cb947a67ce 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
@@ -44,7 +44,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvid
 import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory
 import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
-import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem_
@@ -79,6 +78,7 @@ import im.vector.app.features.media.ImageContentRenderer
 import im.vector.app.features.media.VideoContentRenderer
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.voice.AudioWaveformView
+import im.vector.app.features.voicebroadcast.isVoiceBroadcast
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
 import me.gujun.android.span.span
@@ -103,6 +103,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageType
 import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
+import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
 import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
 import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl
 import org.matrix.android.sdk.api.settings.LightweightSettingsStorage
@@ -321,8 +322,8 @@ class MessageItemFactory @Inject constructor(
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes
     ): MessageVoiceItem? {
-        val eventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) }
-        if (eventsGroup != null && eventsGroup.getLastDisplayableEvent().eventId != params.event.eventId) return null
+        // Do not display voice broadcast messages
+        if (params.event.root.asMessageAudioEvent().isVoiceBroadcast()) return null
 
         val fileUrl = getAudioFileUrl(messageContent, informationData)
         val playbackControlButtonClickListener = createOnPlaybackButtonClickListener(messageContent, informationData, params)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
index ccfdac8f15..d8817c1f44 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventsGroups.kt
@@ -18,15 +18,15 @@ package im.vector.app.features.home.room.detail.timeline.helper
 
 import im.vector.app.core.utils.TextUtils
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.getVoiceBroadcastEventId
+import im.vector.app.features.voicebroadcast.isVoiceBroadcast
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.events.model.EventType
-import org.matrix.android.sdk.api.session.events.model.RelationType
-import org.matrix.android.sdk.api.session.events.model.getRelationContent
-import org.matrix.android.sdk.api.session.events.model.isVoiceMessage
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
+import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
 import org.threeten.bp.Duration
@@ -64,9 +64,9 @@ class TimelineEventsGroups {
             EventType.isCallEvent(type) -> (content?.get("call_id") as? String)
             type == VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO -> root.asVoiceBroadcastEvent()?.reference?.eventId
             type == EventType.STATE_ROOM_WIDGET || type == EventType.STATE_ROOM_WIDGET_LEGACY -> root.stateKey
-            type == EventType.MESSAGE && root.isVoiceMessage() -> {
+            type == EventType.MESSAGE && root.asMessageAudioEvent().isVoiceBroadcast() -> {
                 // Group voice messages with a reference to an eventId
-                root.getRelationContent()?.takeIf { it.type == RelationType.REFERENCE }?.eventId
+                root.asMessageAudioEvent()?.getVoiceBroadcastEventId()
             }
             else -> {
                 null
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
new file mode 100644
index 0000000000..d016703968
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.voicebroadcast
+
+import org.matrix.android.sdk.api.session.events.model.RelationType
+import org.matrix.android.sdk.api.session.events.model.getRelationContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
+
+fun MessageAudioEvent?.isVoiceBroadcast() = this?.getVoiceBroadcastEventId() != null
+
+fun MessageAudioEvent.getVoiceBroadcastEventId(): String? =
+        // TODO Improve this condition by checking the referenced event type
+        root.takeIf { content.voiceMessageIndicator != null }
+                ?.getRelationContent()?.takeIf { it.type == RelationType.REFERENCE }
+                ?.eventId

From 4a32ccecfa26303f218167ed38edd3dc43380b57 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 00:37:38 +0200
Subject: [PATCH 315/400] Voice Broadcast Player - Add missing try catch

---
 .../app/features/voicebroadcast/VoiceBroadcastPlayer.kt  | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 8970a65990..41ed1ff84d 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -120,7 +120,14 @@ class VoiceBroadcastPlayer @Inject constructor(
     }
 
     private suspend fun prepareMediaPlayer(messageAudioContent: MessageAudioContent): MediaPlayer {
-        val audioFile = session.fileService().downloadFile(messageAudioContent)
+        // Download can fail
+        val audioFile = try {
+            session.fileService().downloadFile(messageAudioContent)
+        } catch (failure: Throwable) {
+            Timber.e(failure, "Unable to start playback")
+            throw VoiceFailure.UnableToPlay(failure)
+        }
+
         return audioFile.inputStream().use { fis ->
             MediaPlayer().apply {
                 setAudioAttributes(

From e75ddf763beade39d5f77a5611840dc725726f59 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 00:50:42 +0200
Subject: [PATCH 316/400] Add changelog file

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

diff --git a/changelog.d/7387.wip b/changelog.d/7387.wip
new file mode 100644
index 0000000000..881608829d
--- /dev/null
+++ b/changelog.d/7387.wip
@@ -0,0 +1 @@
+[Voice Broadcast] Start listening to a voice broadcast

From d53ad4328c5d0c356a53548398430e1b90d911aa Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 01:00:50 +0200
Subject: [PATCH 317/400] Voice Broadcast - Pause listening outside of the
 timeline

---
 .../home/room/detail/composer/MessageComposerFragment.kt      | 1 +
 .../home/room/detail/composer/MessageComposerViewModel.kt     | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
index 9954470e48..59f9737542 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
@@ -236,6 +236,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
                 // Pause voice broadcast if the timeline is not shown anymore
                 it.isVoiceBroadcasting && !requireActivity().isChangingConfigurations -> timelineViewModel.handle(VoiceBroadcastAction.Recording.Pause)
                 else -> {
+                    timelineViewModel.handle(VoiceBroadcastAction.Listening.Pause)
                     messageComposerViewModel.handle(MessageComposerAction.OnEntersBackground(composer.text.toString()))
                 }
             }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
index eef06d11b7..1a9f9e6291 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt
@@ -42,6 +42,7 @@ import im.vector.app.features.powerlevel.PowerLevelsFlowFactory
 import im.vector.app.features.session.coroutineScope
 import im.vector.app.features.settings.VectorPreferences
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
+import im.vector.app.features.voicebroadcast.VoiceBroadcastHelper
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.combine
@@ -84,6 +85,7 @@ class MessageComposerViewModel @AssistedInject constructor(
         private val rainbowGenerator: RainbowGenerator,
         private val audioMessageHelper: AudioMessageHelper,
         private val analyticsTracker: AnalyticsTracker,
+        private val voiceBroadcastHelper: VoiceBroadcastHelper,
 ) : VectorViewModel<MessageComposerViewState, MessageComposerAction, MessageComposerViewEvents>(initialState) {
 
     private val room = session.getRoom(initialState.roomId)!!
@@ -981,6 +983,8 @@ class MessageComposerViewModel @AssistedInject constructor(
     private fun handleEntersBackground(composerText: String) {
         // Always stop all voice actions. It may be playing in timeline or active recording
         val playingAudioContent = audioMessageHelper.stopAllVoiceActions(deleteRecord = false)
+        // TODO remove this when there will be a listening indicator outside of the timeline
+        voiceBroadcastHelper.pausePlayback()
 
         val isVoiceRecording = com.airbnb.mvrx.withState(this) { it.isVoiceRecording }
         if (isVoiceRecording) {

From 67be8c3c40f06a88345436abaf129ce3c4e52344 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Tue, 18 Oct 2022 12:44:05 +0100
Subject: [PATCH 318/400] The one that got away

---
 .../android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index 4bbf34280c..e844143889 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -17,7 +17,6 @@
 package org.matrix.android.sdk.api.rendezvous.channels
 
 import android.util.Base64
-import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock

From 0c52a7ed040c5637d81c408e2ec41197de4b2563 Mon Sep 17 00:00:00 2001
From: Onuray Sahin <onurays@element.io>
Date: Tue, 18 Oct 2022 15:45:39 +0300
Subject: [PATCH 319/400] Fix layout after try again button is clicked.

---
 .../im/vector/app/features/login/qr/QrCodeLoginActivity.kt   | 3 ++-
 .../im/vector/app/features/login/qr/QrCodeLoginViewModel.kt  | 5 +++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
index c9b8ae0080..a0c113224d 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt
@@ -24,6 +24,7 @@ import com.airbnb.mvrx.Mavericks
 import com.airbnb.mvrx.viewModel
 import dagger.hilt.android.AndroidEntryPoint
 import im.vector.app.core.extensions.addFragment
+import im.vector.app.core.extensions.replaceFragment
 import im.vector.app.core.platform.SimpleFragmentActivity
 import im.vector.app.features.home.HomeActivity
 import im.vector.lib.core.utils.compat.getParcelableCompat
@@ -66,7 +67,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
     }
 
     private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) {
-        addFragment(
+        replaceFragment(
                 views.container,
                 QrCodeLoginInstructionsFragment::class.java,
                 qrCodeLoginArgs,
diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
index 9bf3e955d3..97cca9d791 100644
--- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt
@@ -59,6 +59,11 @@ class QrCodeLoginViewModel @AssistedInject constructor(
     }
 
     private fun handleTryAgain() {
+        setState {
+            copy(
+                    connectionStatus = null
+            )
+        }
         _viewEvents.post(QrCodeLoginViewEvents.NavigateToInitialScreen)
     }
 

From 03ac0f1f0324db27f02d83f27d97f76b1dfc397d Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 11:00:21 +0200
Subject: [PATCH 320/400] Move Voice Broadcast feature flag to labs settings

---
 library/ui-strings/src/main/res/values/strings.xml         | 2 ++
 .../features/debug/features/DebugFeaturesStateFactory.kt   | 5 -----
 .../app/features/debug/features/DebugVectorFeatures.kt     | 4 ----
 vector-config/src/main/res/values/config-settings.xml      | 2 ++
 .../src/main/java/im/vector/app/features/VectorFeatures.kt | 2 --
 .../home/room/detail/composer/MessageComposerFragment.kt   | 2 +-
 .../im/vector/app/features/settings/VectorPreferences.kt   | 5 +++++
 vector/src/main/res/xml/vector_settings_labs.xml           | 7 +++++++
 8 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 74ec175d17..1fda57180d 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3346,6 +3346,8 @@
     <string name="labs_enable_session_manager_summary">Have greater visibility and control over all your sessions.</string>
     <string name="labs_enable_client_info_recording_title">Enable client info recording</string>
     <string name="labs_enable_client_info_recording_summary">Record the client name, version, and url to recognise sessions more easily in session manager.</string>
+    <string name="labs_enable_voice_broadcast_title">Enable Voice broadcast (under active development)</string>
+    <string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline. Only ended voice broadcast can be listened (listen live broadcast is not correctly supported for the moment)</string>
 
     <!-- Note to translators: %s will be replaces with selected space name -->
     <string name="home_empty_space_no_rooms_title">%s\nis looking a little empty.</string>
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 16e26ff3b5..0ce4416add 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -100,11 +100,6 @@ class DebugFeaturesStateFactory @Inject constructor(
                                 key = DebugFeatureKeys.reciprocateQrCodeLogin,
                                 factory = VectorFeatures::isReciprocateQrCodeLogin
                         ),
-                        createBooleanFeature(
-                                label = "Enable Voice Broadcast",
-                                key = DebugFeatureKeys.voiceBroadcastEnabled,
-                                factory = VectorFeatures::isVoiceBroadcastEnabled
-                        ),
                 )
         )
     }
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index 5c497c24ec..487094b238 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -85,9 +85,6 @@ class DebugVectorFeatures(
     override fun isReciprocateQrCodeLogin() = read(DebugFeatureKeys.reciprocateQrCodeLogin)
             ?: vectorFeatures.isReciprocateQrCodeLogin()
 
-    override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
-            ?: vectorFeatures.isVoiceBroadcastEnabled()
-
     fun <T> override(value: T?, key: Preferences.Key<T>) = updatePreferences {
         if (value == null) {
             it.remove(key)
@@ -150,5 +147,4 @@ object DebugFeatureKeys {
     val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled")
     val qrCodeLoginForAllServers = booleanPreferencesKey("qr-code-login-for-all-servers")
     val reciprocateQrCodeLogin = booleanPreferencesKey("reciprocate-qr-code-login")
-    val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
 }
diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml
index 7b7aac8156..504c587b8d 100755
--- a/vector-config/src/main/res/values/config-settings.xml
+++ b/vector-config/src/main/res/values/config-settings.xml
@@ -49,6 +49,8 @@
     <bool name="settings_timeline_show_live_sender_info_default">false</bool>
     <bool name="settings_labs_rich_text_editor_visible">true</bool>
     <bool name="settings_labs_rich_text_editor_default">false</bool>
+    <bool name="settings_labs_enable_voice_broadcast_visible">true</bool>
+    <bool name="settings_labs_enable_voice_broadcast_default">false</bool>
     <!-- Level 1: Advanced settings -->
 
     <!-- Level 1: Help and about -->
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index 255ac6d188..59bccc25fc 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -43,7 +43,6 @@ interface VectorFeatures {
     fun isQrCodeLoginEnabled(): Boolean
     fun isQrCodeLoginForAllServers(): Boolean
     fun isReciprocateQrCodeLogin(): Boolean
-    fun isVoiceBroadcastEnabled(): Boolean
 }
 
 class DefaultVectorFeatures : VectorFeatures {
@@ -62,5 +61,4 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun isQrCodeLoginEnabled(): Boolean = true
     override fun isQrCodeLoginForAllServers(): Boolean = false
     override fun isReciprocateQrCodeLogin(): Boolean = false
-    override fun isVoiceBroadcastEnabled(): Boolean = false
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
index 59f9737542..55ec922a57 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerFragment.kt
@@ -310,7 +310,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
                     )
                     attachmentTypeSelector.setAttachmentVisibility(
                             AttachmentTypeSelectorView.Type.VOICE_BROADCAST,
-                            vectorFeatures.isVoiceBroadcastEnabled(), // TODO check user permission
+                            vectorPreferences.isVoiceBroadcastEnabled(), // TODO check user permission
                     )
                 }
                 attachmentTypeSelector.show(composer.attachmentButton)
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
index 89fcda142a..5d0f981314 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
@@ -74,6 +74,7 @@ class VectorPreferences @Inject constructor(
         const val SETTINGS_LABS_RICH_TEXT_EDITOR_KEY = "SETTINGS_LABS_RICH_TEXT_EDITOR_KEY"
         const val SETTINGS_LABS_NEW_SESSION_MANAGER_KEY = "SETTINGS_LABS_NEW_SESSION_MANAGER_KEY"
         const val SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY = "SETTINGS_LABS_CLIENT_INFO_RECORDING_KEY"
+        const val SETTINGS_LABS_VOICE_BROADCAST_KEY = "SETTINGS_LABS_VOICE_BROADCAST_KEY"
         const val SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_PREFERENCE_KEY"
         const val SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_DIVIDER_PREFERENCE_KEY"
         const val SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY = "SETTINGS_CRYPTOGRAPHY_MANAGE_PREFERENCE_KEY"
@@ -1203,4 +1204,8 @@ class VectorPreferences @Inject constructor(
     fun isRichTextEditorEnabled(): Boolean {
         return defaultPrefs.getBoolean(SETTINGS_LABS_RICH_TEXT_EDITOR_KEY, getDefault(R.bool.settings_labs_rich_text_editor_default))
     }
+
+    fun isVoiceBroadcastEnabled(): Boolean {
+        return defaultPrefs.getBoolean(SETTINGS_LABS_VOICE_BROADCAST_KEY, getDefault(R.bool.settings_labs_enable_voice_broadcast_default))
+    }
 }
diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml
index 5b519bdd91..15a255753a 100644
--- a/vector/src/main/res/xml/vector_settings_labs.xml
+++ b/vector/src/main/res/xml/vector_settings_labs.xml
@@ -117,4 +117,11 @@
         android:title="@string/labs_enable_client_info_recording_title"
         app:isPreferenceVisible="@bool/settings_labs_client_info_recording_visible" />
 
+    <im.vector.app.core.preference.VectorSwitchPreference
+        android:defaultValue="@bool/settings_labs_enable_voice_broadcast_default"
+        android:key="SETTINGS_LABS_VOICE_BROADCAST_KEY"
+        android:summary="@string/labs_enable_voice_broadcast_summary"
+        android:title="@string/labs_enable_voice_broadcast_title"
+        app:isPreferenceVisible="@bool/settings_labs_enable_voice_broadcast_visible" />
+
 </androidx.preference.PreferenceScreen>

From 050dff6548abfb4c9c3d38c6daa6074ed1d4f985 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 11:05:24 +0200
Subject: [PATCH 321/400] Voice Broadcast - Rename voice message files with
 sequence number

---
 .../features/voicebroadcast/VoiceBroadcastRecorder.kt    | 3 ++-
 .../features/voicebroadcast/VoiceBroadcastRecorderQ.kt   | 6 +++++-
 .../voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt | 9 +++++----
 3 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
index 2668501a8d..37ff920c57 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.voicebroadcast
 
+import androidx.annotation.IntRange
 import im.vector.app.features.voice.VoiceRecorder
 import java.io.File
 
@@ -26,6 +27,6 @@ interface VoiceBroadcastRecorder : VoiceRecorder {
     fun startRecord(roomId: String, chunkLength: Int)
 
     fun interface Listener {
-        fun onVoiceMessageCreated(file: File)
+        fun onVoiceMessageCreated(file: File, @IntRange(from = 1) sequence: Int)
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
index 620db721c9..404b112574 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -29,6 +29,7 @@ class VoiceBroadcastRecorderQ(
 ) : AbstractVoiceRecorderQ(context), VoiceBroadcastRecorder {
 
     private var maxFileSize = 0L // zero or negative for no limit
+    private var currentSequence = 0
 
     override var listener: VoiceBroadcastRecorder.Listener? = null
 
@@ -51,6 +52,7 @@ class VoiceBroadcastRecorderQ(
 
     override fun startRecord(roomId: String, chunkLength: Int) {
         maxFileSize = (chunkLength * audioEncodingBitRate / 8).toLong()
+        currentSequence = 1
         startRecord(roomId)
     }
 
@@ -58,6 +60,7 @@ class VoiceBroadcastRecorderQ(
         super.stopRecord()
         notifyOutputFileCreated()
         listener = null
+        currentSequence = 0
     }
 
     override fun release() {
@@ -71,11 +74,12 @@ class VoiceBroadcastRecorderQ(
 
     private fun onNextOutputFileStarted() {
         notifyOutputFileCreated()
+        currentSequence++
     }
 
     private fun notifyOutputFileCreated() {
         outputFile?.let {
-            listener?.onVoiceMessageCreated(it)
+            listener?.onVoiceMessageCreated(it, currentSequence)
             outputFile = nextOutputFile
             nextOutputFile = null
         }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 2a306bcd28..780150d5e7 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -79,20 +79,21 @@ class StartVoiceBroadcastUseCase @Inject constructor(
     }
 
     private fun startRecording(room: Room, eventId: String, chunkLength: Int) {
-        voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file ->
-            sendVoiceFile(room, file, eventId)
+        voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file, sequence ->
+            sendVoiceFile(room, file, eventId, sequence)
         }
         voiceBroadcastRecorder?.startRecord(room.roomId, chunkLength)
     }
 
-    private fun sendVoiceFile(room: Room, voiceMessageFile: File, referenceEventId: String) {
+    private fun sendVoiceFile(room: Room, voiceMessageFile: File, referenceEventId: String, sequence: Int) {
         val outputFileUri = FileProvider.getUriForFile(
                 context,
                 buildMeta.applicationId + ".fileProvider",
                 voiceMessageFile,
-                "Voice message.${voiceMessageFile.extension}"
+                "Voice Broadcast Part ($sequence).${voiceMessageFile.extension}"
         )
         val audioType = outputFileUri.toMultiPickerAudioType(context) ?: return
+        // TODO put sequence in event content
         room.sendService().sendMedia(
                 attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
                 compressBeforeSending = false,

From 036511400e93af8b217c889609588b9350b3879e Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 11:12:26 +0200
Subject: [PATCH 322/400] Voice Broadcast - Hide labs setting in Android < 10

---
 .../src/main/res/values-v29/config-settings.xml     | 13 +++++++++++++
 .../src/main/res/values/config-settings.xml         |  2 +-
 2 files changed, 14 insertions(+), 1 deletion(-)
 create mode 100755 vector-config/src/main/res/values-v29/config-settings.xml

diff --git a/vector-config/src/main/res/values-v29/config-settings.xml b/vector-config/src/main/res/values-v29/config-settings.xml
new file mode 100755
index 0000000000..051e6e9e81
--- /dev/null
+++ b/vector-config/src/main/res/values-v29/config-settings.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!-- This file contains values to show or hide some settings, and default values for some settings
+     - boolean keys ending with "_visible" set the visibility of the setting
+     - boolean keys ending with "_default" set the default value of the setting
+     When a setting is hidden, the default value still applies
+     -->
+
+    <!-- Level 1: Labs -->
+    <bool name="settings_labs_enable_voice_broadcast_visible">true</bool>
+
+</resources>
diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml
index 504c587b8d..f2169e7e62 100755
--- a/vector-config/src/main/res/values/config-settings.xml
+++ b/vector-config/src/main/res/values/config-settings.xml
@@ -49,7 +49,7 @@
     <bool name="settings_timeline_show_live_sender_info_default">false</bool>
     <bool name="settings_labs_rich_text_editor_visible">true</bool>
     <bool name="settings_labs_rich_text_editor_default">false</bool>
-    <bool name="settings_labs_enable_voice_broadcast_visible">true</bool>
+    <bool name="settings_labs_enable_voice_broadcast_visible">false</bool> <!-- Note: also defined in values-v29 -->
     <bool name="settings_labs_enable_voice_broadcast_default">false</bool>
     <!-- Level 1: Advanced settings -->
 

From ecc22a1401051f063b8c5ff54a7d4cb2d1d0f654 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 11:23:01 +0200
Subject: [PATCH 323/400] Voice Broadcast - Change default chunk duration

---
 .../app/features/voicebroadcast/VoiceBroadcastConstants.kt      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index 3a9aac12d5..d445dfdfbe 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -22,5 +22,5 @@ object VoiceBroadcastConstants {
     const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
 
     /** Default voice broadcast chunk duration, in seconds. */
-    const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 30
+    const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 120
 }

From a03be5d02fface951a4da4cc51bed25e19c26ec2 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 11:29:00 +0200
Subject: [PATCH 324/400] Add changelog

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

diff --git a/changelog.d/7393.wip b/changelog.d/7393.wip
new file mode 100644
index 0000000000..b2112be5e9
--- /dev/null
+++ b/changelog.d/7393.wip
@@ -0,0 +1 @@
+[Voice Broadcast] Move the feature flag to the labs

From 5a749bdccf262aac15485977b8973636af408e2d Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 20:49:26 +0200
Subject: [PATCH 325/400] Voice Broadcast - Update labs description

---
 library/ui-strings/src/main/res/values/strings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 1fda57180d..ba83a4553c 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3347,7 +3347,7 @@
     <string name="labs_enable_client_info_recording_title">Enable client info recording</string>
     <string name="labs_enable_client_info_recording_summary">Record the client name, version, and url to recognise sessions more easily in session manager.</string>
     <string name="labs_enable_voice_broadcast_title">Enable Voice broadcast (under active development)</string>
-    <string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline. Only ended voice broadcast can be listened (listen live broadcast is not correctly supported for the moment)</string>
+    <string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline.‡</string>
 
     <!-- Note to translators: %s will be replaces with selected space name -->
     <string name="home_empty_space_no_rooms_title">%s\nis looking a little empty.</string>

From 63c02c6fef0faa5277273ad5280e8ecd75b43f43 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 21:02:58 +0200
Subject: [PATCH 326/400] Voice Broadcast - Restore feature flag and enable it
 by default

---
 .../app/features/debug/features/DebugFeaturesStateFactory.kt | 5 +++++
 .../app/features/debug/features/DebugVectorFeatures.kt       | 4 ++++
 .../src/main/java/im/vector/app/features/VectorFeatures.kt   | 2 ++
 .../im/vector/app/features/settings/VectorPreferences.kt     | 3 ++-
 .../app/features/settings/labs/VectorSettingsLabsFragment.kt | 4 ++++
 5 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
index 0ce4416add..16e26ff3b5 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt
@@ -100,6 +100,11 @@ class DebugFeaturesStateFactory @Inject constructor(
                                 key = DebugFeatureKeys.reciprocateQrCodeLogin,
                                 factory = VectorFeatures::isReciprocateQrCodeLogin
                         ),
+                        createBooleanFeature(
+                                label = "Enable Voice Broadcast",
+                                key = DebugFeatureKeys.voiceBroadcastEnabled,
+                                factory = VectorFeatures::isVoiceBroadcastEnabled
+                        ),
                 )
         )
     }
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index 487094b238..5c497c24ec 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -85,6 +85,9 @@ class DebugVectorFeatures(
     override fun isReciprocateQrCodeLogin() = read(DebugFeatureKeys.reciprocateQrCodeLogin)
             ?: vectorFeatures.isReciprocateQrCodeLogin()
 
+    override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled)
+            ?: vectorFeatures.isVoiceBroadcastEnabled()
+
     fun <T> override(value: T?, key: Preferences.Key<T>) = updatePreferences {
         if (value == null) {
             it.remove(key)
@@ -147,4 +150,5 @@ object DebugFeatureKeys {
     val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled")
     val qrCodeLoginForAllServers = booleanPreferencesKey("qr-code-login-for-all-servers")
     val reciprocateQrCodeLogin = booleanPreferencesKey("reciprocate-qr-code-login")
+    val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled")
 }
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index 59bccc25fc..95cf272abd 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -43,6 +43,7 @@ interface VectorFeatures {
     fun isQrCodeLoginEnabled(): Boolean
     fun isQrCodeLoginForAllServers(): Boolean
     fun isReciprocateQrCodeLogin(): Boolean
+    fun isVoiceBroadcastEnabled(): Boolean
 }
 
 class DefaultVectorFeatures : VectorFeatures {
@@ -61,4 +62,5 @@ class DefaultVectorFeatures : VectorFeatures {
     override fun isQrCodeLoginEnabled(): Boolean = true
     override fun isQrCodeLoginForAllServers(): Boolean = false
     override fun isReciprocateQrCodeLogin(): Boolean = false
+    override fun isVoiceBroadcastEnabled(): Boolean = true
 }
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
index 5d0f981314..2dc8b12160 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
@@ -1206,6 +1206,7 @@ class VectorPreferences @Inject constructor(
     }
 
     fun isVoiceBroadcastEnabled(): Boolean {
-        return defaultPrefs.getBoolean(SETTINGS_LABS_VOICE_BROADCAST_KEY, getDefault(R.bool.settings_labs_enable_voice_broadcast_default))
+        return vectorFeatures.isVoiceBroadcastEnabled() &&
+                defaultPrefs.getBoolean(SETTINGS_LABS_VOICE_BROADCAST_KEY, getDefault(R.bool.settings_labs_enable_voice_broadcast_default))
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
index 6c31e32567..f9a5a5f9cc 100644
--- a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
@@ -90,6 +90,10 @@ class VectorSettingsLabsFragment :
             }
         }
 
+        findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_VOICE_BROADCAST_KEY)?.let { pref ->
+            pref.isVisible = vectorFeatures.isVoiceBroadcastEnabled()
+        }
+
         configureUnreadNotificationsAsTabPreference()
         configureEnableClientInfoRecordingPreference()
     }

From 90803be3eec728c896b097b8194a649920d54284 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 21:06:59 +0200
Subject: [PATCH 327/400] Voice Broadcast - Move Android API check on fragment

---
 .../src/main/res/values-v29/config-settings.xml     | 13 -------------
 .../src/main/res/values/config-settings.xml         |  2 +-
 .../settings/labs/VectorSettingsLabsFragment.kt     |  4 +++-
 3 files changed, 4 insertions(+), 15 deletions(-)
 delete mode 100755 vector-config/src/main/res/values-v29/config-settings.xml

diff --git a/vector-config/src/main/res/values-v29/config-settings.xml b/vector-config/src/main/res/values-v29/config-settings.xml
deleted file mode 100755
index 051e6e9e81..0000000000
--- a/vector-config/src/main/res/values-v29/config-settings.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
-    <!-- This file contains values to show or hide some settings, and default values for some settings
-     - boolean keys ending with "_visible" set the visibility of the setting
-     - boolean keys ending with "_default" set the default value of the setting
-     When a setting is hidden, the default value still applies
-     -->
-
-    <!-- Level 1: Labs -->
-    <bool name="settings_labs_enable_voice_broadcast_visible">true</bool>
-
-</resources>
diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml
index f2169e7e62..504c587b8d 100755
--- a/vector-config/src/main/res/values/config-settings.xml
+++ b/vector-config/src/main/res/values/config-settings.xml
@@ -49,7 +49,7 @@
     <bool name="settings_timeline_show_live_sender_info_default">false</bool>
     <bool name="settings_labs_rich_text_editor_visible">true</bool>
     <bool name="settings_labs_rich_text_editor_default">false</bool>
-    <bool name="settings_labs_enable_voice_broadcast_visible">false</bool> <!-- Note: also defined in values-v29 -->
+    <bool name="settings_labs_enable_voice_broadcast_visible">true</bool>
     <bool name="settings_labs_enable_voice_broadcast_default">false</bool>
     <!-- Level 1: Advanced settings -->
 
diff --git a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
index f9a5a5f9cc..c10411301f 100644
--- a/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.settings.labs
 
+import android.os.Build
 import android.os.Bundle
 import android.text.method.LinkMovementMethod
 import android.widget.TextView
@@ -91,7 +92,8 @@ class VectorSettingsLabsFragment :
         }
 
         findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_VOICE_BROADCAST_KEY)?.let { pref ->
-            pref.isVisible = vectorFeatures.isVoiceBroadcastEnabled()
+            // Voice Broadcast recording is not available on Android < 10
+            pref.isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && vectorFeatures.isVoiceBroadcastEnabled()
         }
 
         configureUnreadNotificationsAsTabPreference()

From fbf242756ef384326a705c4fcdf7375ca252c7b7 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 14:34:31 +0200
Subject: [PATCH 328/400] Allow additional content when sending an event

---
 .../sdk/api/session/room/send/SendService.kt  |  34 ++++--
 .../session/content/UploadContentWorker.kt    |   8 +-
 .../session/room/send/DefaultSendService.kt   |  36 ++++---
 .../room/send/LocalEchoEventFactory.kt        | 102 ++++++++++++------
 4 files changed, 122 insertions(+), 58 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
index 53b49129c4..6a6fadc95a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
@@ -45,18 +45,30 @@ interface SendService {
      * @param text the text message to send
      * @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
      * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
-    fun sendTextMessage(text: CharSequence, msgType: String = MessageType.MSGTYPE_TEXT, autoMarkdown: Boolean = false): Cancelable
+    fun sendTextMessage(
+            text: CharSequence,
+            msgType: String = MessageType.MSGTYPE_TEXT,
+            autoMarkdown: Boolean = false,
+            additionalContent: Content? = null,
+    ): Cancelable
 
     /**
      * Method to send a text message with a formatted body.
      * @param text the text message to send
      * @param formattedText The formatted body using MessageType#FORMAT_MATRIX_HTML
      * @param msgType the message type: MessageType.MSGTYPE_TEXT (default) or MessageType.MSGTYPE_EMOTE
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
-    fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String = MessageType.MSGTYPE_TEXT): Cancelable
+    fun sendFormattedTextMessage(
+            text: String,
+            formattedText: String,
+            msgType: String = MessageType.MSGTYPE_TEXT,
+            additionalContent: Content? = null,
+    ): Cancelable
 
     /**
      * Method to quote an events content.
@@ -65,6 +77,7 @@ interface SendService {
      * @param formattedText the formatted text message to send
      * @param autoMarkdown If true, the SDK will generate a formatted HTML message from the body text if markdown syntax is present
      * @param rootThreadEventId when this param is not null, the message will be sent in this specific thread
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
     fun sendQuotedTextMessage(
@@ -73,6 +86,7 @@ interface SendService {
             formattedText: String? = null,
             autoMarkdown: Boolean,
             rootThreadEventId: String? = null,
+            additionalContent: Content? = null,
     ): Cancelable
 
     /**
@@ -83,6 +97,7 @@ interface SendService {
      *                It can be useful to send media to multiple room. It's safe to include the current roomId in this set
      * @param rootThreadEventId when this param is not null, the Media will be sent in this specific thread
      * @param relatesTo add a relation content to the media event
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
     fun sendMedia(
@@ -91,6 +106,7 @@ interface SendService {
             roomIds: Set<String>,
             rootThreadEventId: String? = null,
             relatesTo: RelationDefaultContent? = null,
+            additionalContent: Content? = null,
     ): Cancelable
 
     /**
@@ -100,6 +116,7 @@ interface SendService {
      * @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
      *                It can be useful to send media to multiple room. It's safe to include the current roomId in this set
      * @param rootThreadEventId when this param is not null, all the Media will be sent in this specific thread
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
     fun sendMedias(
@@ -107,6 +124,7 @@ interface SendService {
             compressBeforeSending: Boolean,
             roomIds: Set<String>,
             rootThreadEventId: String? = null,
+            additionalContent: Content? = null,
     ): Cancelable
 
     /**
@@ -114,31 +132,35 @@ interface SendService {
      * @param pollType indicates open or closed polls
      * @param question the question
      * @param options list of options
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
-    fun sendPoll(pollType: PollType, question: String, options: List<String>): Cancelable
+    fun sendPoll(pollType: PollType, question: String, options: List<String>, additionalContent: Content? = null): Cancelable
 
     /**
      * Method to send a poll response.
      * @param pollEventId the poll currently replied to
      * @param answerId The id of the answer
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
-    fun voteToPoll(pollEventId: String, answerId: String): Cancelable
+    fun voteToPoll(pollEventId: String, answerId: String, additionalContent: Content? = null): Cancelable
 
     /**
      * End a poll in the room.
      * @param pollEventId event id of the poll
+     * @param additionalContent additional content to put in the event content
      * @return a [Cancelable]
      */
-    fun endPoll(pollEventId: String): Cancelable
+    fun endPoll(pollEventId: String, additionalContent: Content? = null): Cancelable
 
     /**
      * Redact (delete) the given event.
      * @param event The event to redact
      * @param reason Optional reason string
+     * @param additionalContent additional content to put in the event content
      */
-    fun redactEvent(event: Event, reason: String?): Cancelable
+    fun redactEvent(event: Event, reason: String?, additionalContent: Content? = null): Cancelable
 
     /**
      * Schedule this message to be resent.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
index 1e62b5d7f5..db1cd1b33b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.listeners.ProgressListener
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
 import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
+import org.matrix.android.sdk.api.session.events.model.Content
 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.MessageAudioContent
@@ -407,7 +408,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
             newAttachmentAttributes: NewAttachmentAttributes
     ) {
         localEchoRepository.updateEcho(eventId) { _, event ->
-            val messageContent: MessageContent? = event.asDomain().content.toModel()
+            val content: Content? = event.asDomain().content
+            val messageContent: MessageContent? = content.toModel()
+            // Retrieve potential additional content from the original event
+            val additionalContent = content.orEmpty() - messageContent?.toContent().orEmpty().keys
             val updatedContent = when (messageContent) {
                 is MessageImageContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes)
                 is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo, newAttachmentAttributes)
@@ -415,7 +419,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
                 is MessageAudioContent -> messageContent.update(url, encryptedFileInfo, newAttachmentAttributes.newFileSize)
                 else -> messageContent
             }
-            event.content = ContentMapper.map(updatedContent.toContent())
+            event.content = ContentMapper.map(updatedContent.toContent().plus(additionalContent))
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index aa305e6067..9cdbc7ff46 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -27,6 +27,7 @@ import dagger.assisted.AssistedInject
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
+import org.matrix.android.sdk.api.session.events.model.Content
 import org.matrix.android.sdk.api.session.events.model.Event
 import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
 import org.matrix.android.sdk.api.session.events.model.isTextMessage
@@ -88,14 +89,14 @@ internal class DefaultSendService @AssistedInject constructor(
                 .let { sendEvent(it) }
     }
 
-    override fun sendTextMessage(text: CharSequence, msgType: String, autoMarkdown: Boolean): Cancelable {
-        return localEchoEventFactory.createTextEvent(roomId, msgType, text, autoMarkdown)
+    override fun sendTextMessage(text: CharSequence, msgType: String, autoMarkdown: Boolean, additionalContent: Content?): Cancelable {
+        return localEchoEventFactory.createTextEvent(roomId, msgType, text, autoMarkdown, additionalContent)
                 .also { createLocalEcho(it) }
                 .let { sendEvent(it) }
     }
 
-    override fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String): Cancelable {
-        return localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType)
+    override fun sendFormattedTextMessage(text: String, formattedText: String, msgType: String, additionalContent: Content?): Cancelable {
+        return localEchoEventFactory.createFormattedTextEvent(roomId, TextContent(text, formattedText), msgType, additionalContent)
                 .also { createLocalEcho(it) }
                 .let { sendEvent(it) }
     }
@@ -105,7 +106,8 @@ internal class DefaultSendService @AssistedInject constructor(
             text: String,
             formattedText: String?,
             autoMarkdown: Boolean,
-            rootThreadEventId: String?
+            rootThreadEventId: String?,
+            additionalContent: Content?,
     ): Cancelable {
         return localEchoEventFactory.createQuotedTextEvent(
                 roomId = roomId,
@@ -113,33 +115,34 @@ internal class DefaultSendService @AssistedInject constructor(
                 text = text,
                 formattedText = formattedText,
                 autoMarkdown = autoMarkdown,
-                rootThreadEventId = rootThreadEventId
+                rootThreadEventId = rootThreadEventId,
+                additionalContent = additionalContent,
         )
                 .also { createLocalEcho(it) }
                 .let { sendEvent(it) }
     }
 
-    override fun sendPoll(pollType: PollType, question: String, options: List<String>): Cancelable {
-        return localEchoEventFactory.createPollEvent(roomId, pollType, question, options)
+    override fun sendPoll(pollType: PollType, question: String, options: List<String>, additionalContent: Content?): Cancelable {
+        return localEchoEventFactory.createPollEvent(roomId, pollType, question, options, additionalContent)
                 .also { createLocalEcho(it) }
                 .let { sendEvent(it) }
     }
 
-    override fun voteToPoll(pollEventId: String, answerId: String): Cancelable {
-        return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId)
+    override fun voteToPoll(pollEventId: String, answerId: String, additionalContent: Content?): Cancelable {
+        return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId, additionalContent)
                 .also { createLocalEcho(it) }
                 .let { sendEvent(it) }
     }
 
-    override fun endPoll(pollEventId: String): Cancelable {
-        return localEchoEventFactory.createEndPollEvent(roomId, pollEventId)
+    override fun endPoll(pollEventId: String, additionalContent: Content?): Cancelable {
+        return localEchoEventFactory.createEndPollEvent(roomId, pollEventId, additionalContent)
                 .also { createLocalEcho(it) }
                 .let { sendEvent(it) }
     }
 
-    override fun redactEvent(event: Event, reason: String?): Cancelable {
+    override fun redactEvent(event: Event, reason: String?, additionalContent: Content?): Cancelable {
         // TODO manage media/attachements?
-        val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason)
+        val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason, additionalContent)
                 .also { createLocalEcho(it) }
         return eventSenderProcessor.postRedaction(redactionEcho, reason)
     }
@@ -265,7 +268,8 @@ internal class DefaultSendService @AssistedInject constructor(
             attachments: List<ContentAttachmentData>,
             compressBeforeSending: Boolean,
             roomIds: Set<String>,
-            rootThreadEventId: String?
+            rootThreadEventId: String?,
+            additionalContent: Content?,
     ): Cancelable {
         return attachments.mapTo(CancelableBag()) {
             sendMedia(
@@ -283,6 +287,7 @@ internal class DefaultSendService @AssistedInject constructor(
             roomIds: Set<String>,
             rootThreadEventId: String?,
             relatesTo: RelationDefaultContent?,
+            additionalContent: Content?,
     ): Cancelable {
         // Ensure that the event will not be send in a thread if we are a different flow.
         // Like sending files to multiple rooms
@@ -299,6 +304,7 @@ internal class DefaultSendService @AssistedInject constructor(
                     attachment = attachment,
                     rootThreadEventId = rootThreadId,
                     relatesTo,
+                    additionalContent,
             ).also { event ->
                 createLocalEcho(event)
             }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
index 1d7f624eba..7d8605c2bd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt
@@ -95,12 +95,12 @@ internal class LocalEchoEventFactory @Inject constructor(
         private val permalinkFactory: PermalinkFactory,
         private val clock: Clock,
 ) {
-    fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean): Event {
+    fun createTextEvent(roomId: String, msgType: String, text: CharSequence, autoMarkdown: Boolean, additionalContent: Content? = null): Event {
         if (msgType == MessageType.MSGTYPE_TEXT || msgType == MessageType.MSGTYPE_EMOTE) {
-            return createFormattedTextEvent(roomId, createTextContent(text, autoMarkdown), msgType)
+            return createFormattedTextEvent(roomId, createTextContent(text, autoMarkdown), msgType, additionalContent)
         }
         val content = MessageTextContent(msgType = msgType, body = text.toString())
-        return createMessageEvent(roomId, content)
+        return createMessageEvent(roomId, content, additionalContent)
     }
 
     private fun createTextContent(text: CharSequence, autoMarkdown: Boolean): TextContent {
@@ -116,8 +116,8 @@ internal class LocalEchoEventFactory @Inject constructor(
         return TextContent(text.toString())
     }
 
-    fun createFormattedTextEvent(roomId: String, textContent: TextContent, msgType: String): Event {
-        return createMessageEvent(roomId, textContent.toMessageTextContent(msgType))
+    fun createFormattedTextEvent(roomId: String, textContent: TextContent, msgType: String, additionalContent: Content? = null): Event {
+        return createMessageEvent(roomId, textContent.toMessageTextContent(msgType), additionalContent)
     }
 
     fun createReplaceTextEvent(
@@ -128,6 +128,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             newBodyAutoMarkdown: Boolean,
             msgType: String,
             compatibilityText: String,
+            additionalContent: Content? = null,
     ): Event {
         val content = if (newBodyFormattedText != null) {
             TextContent(newBodyText.toString(), newBodyFormattedText.toString()).toMessageTextContent(msgType)
@@ -141,7 +142,8 @@ internal class LocalEchoEventFactory @Inject constructor(
                         body = compatibilityText,
                         relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId),
                         newContent = content,
-                )
+                ),
+                additionalContent,
         )
     }
 
@@ -167,6 +169,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             targetEventId: String,
             question: String,
             options: List<String>,
+            additionalContent: Content? = null,
     ): Event {
         val newContent = MessagePollContent(
                 relatesTo = RelationDefaultContent(RelationType.REPLACE, targetEventId),
@@ -179,7 +182,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 senderId = userId,
                 eventId = localId,
                 type = EventType.POLL_START.first(),
-                content = newContent.toContent()
+                content = newContent.toContent().plus(additionalContent.orEmpty())
         )
     }
 
@@ -187,6 +190,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             roomId: String,
             pollEventId: String,
             answerId: String,
+            additionalContent: Content? = null,
     ): Event {
         val content = MessagePollResponseContent(
                 body = answerId,
@@ -203,7 +207,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 senderId = userId,
                 eventId = localId,
                 type = EventType.POLL_RESPONSE.first(),
-                content = content.toContent(),
+                content = content.toContent().plus(additionalContent.orEmpty()),
                 unsignedData = UnsignedData(age = null, transactionId = localId)
         )
     }
@@ -213,6 +217,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             pollType: PollType,
             question: String,
             options: List<String>,
+            additionalContent: Content? = null,
     ): Event {
         val content = createPollContent(question, options, pollType)
         val localId = LocalEcho.createLocalEchoId()
@@ -222,7 +227,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 senderId = userId,
                 eventId = localId,
                 type = EventType.POLL_START.first(),
-                content = content.toContent(),
+                content = content.toContent().plus(additionalContent.orEmpty()),
                 unsignedData = UnsignedData(age = null, transactionId = localId)
         )
     }
@@ -230,6 +235,7 @@ internal class LocalEchoEventFactory @Inject constructor(
     fun createEndPollEvent(
             roomId: String,
             eventId: String,
+            additionalContent: Content? = null,
     ): Event {
         val content = MessageEndPollContent(
                 relatesTo = RelationDefaultContent(
@@ -244,7 +250,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 senderId = userId,
                 eventId = localId,
                 type = EventType.POLL_END.first(),
-                content = content.toContent(),
+                content = content.toContent().plus(additionalContent.orEmpty()),
                 unsignedData = UnsignedData(age = null, transactionId = localId)
         )
     }
@@ -255,6 +261,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             longitude: Double,
             uncertainty: Double?,
             isUserLocation: Boolean,
+            additionalContent: Content? = null,
     ): Event {
         val geoUri = buildGeoUri(latitude, longitude, uncertainty)
         val assetType = if (isUserLocation) LocationAssetType.SELF else LocationAssetType.PIN
@@ -266,7 +273,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 unstableTimestampMillis = clock.epochMillis(),
                 unstableText = geoUri
         )
-        return createMessageEvent(roomId, content)
+        return createMessageEvent(roomId, content, additionalContent)
     }
 
     fun createLiveLocationEvent(
@@ -275,6 +282,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             latitude: Double,
             longitude: Double,
             uncertainty: Double?,
+            additionalContent: Content? = null,
     ): Event {
         val geoUri = buildGeoUri(latitude, longitude, uncertainty)
         val content = MessageBeaconLocationDataContent(
@@ -293,7 +301,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 senderId = userId,
                 eventId = localId,
                 type = EventType.BEACON_LOCATION_DATA.first(),
-                content = content.toContent(),
+                content = content.toContent().plus(additionalContent.orEmpty()),
                 unsignedData = UnsignedData(age = null, transactionId = localId)
         )
     }
@@ -306,6 +314,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             autoMarkdown: Boolean,
             msgType: String,
             compatibilityText: String,
+            additionalContent: Content? = null,
     ): Event {
         val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false)
         val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: ""
@@ -340,7 +349,8 @@ internal class LocalEchoEventFactory @Inject constructor(
                                 formattedBody = replyFormatted
                         )
                                 .toContent()
-                )
+                ),
+                additionalContent,
         )
     }
 
@@ -349,23 +359,32 @@ internal class LocalEchoEventFactory @Inject constructor(
             attachment: ContentAttachmentData,
             rootThreadEventId: String?,
             relatesTo: RelationDefaultContent?,
+            additionalContent: Content? = null,
     ): Event {
         return when (attachment.type) {
-            ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment, rootThreadEventId, relatesTo)
-            ContentAttachmentData.Type.VIDEO -> createVideoEvent(roomId, attachment, rootThreadEventId, relatesTo)
-            ContentAttachmentData.Type.AUDIO -> createAudioEvent(roomId, attachment, isVoiceMessage = false, rootThreadEventId = rootThreadEventId, relatesTo)
+            ContentAttachmentData.Type.IMAGE -> createImageEvent(roomId, attachment, rootThreadEventId, relatesTo, additionalContent)
+            ContentAttachmentData.Type.VIDEO -> createVideoEvent(roomId, attachment, rootThreadEventId, relatesTo, additionalContent)
+            ContentAttachmentData.Type.AUDIO -> createAudioEvent(
+                    roomId,
+                    attachment,
+                    isVoiceMessage = false,
+                    rootThreadEventId = rootThreadEventId,
+                    relatesTo,
+                    additionalContent
+            )
             ContentAttachmentData.Type.VOICE_MESSAGE -> createAudioEvent(
                     roomId,
                     attachment,
                     isVoiceMessage = true,
                     rootThreadEventId = rootThreadEventId,
                     relatesTo,
+                    additionalContent,
             )
-            ContentAttachmentData.Type.FILE -> createFileEvent(roomId, attachment, rootThreadEventId, relatesTo)
+            ContentAttachmentData.Type.FILE -> createFileEvent(roomId, attachment, rootThreadEventId, relatesTo, additionalContent)
         }
     }
 
-    fun createReactionEvent(roomId: String, targetEventId: String, reaction: String): Event {
+    fun createReactionEvent(roomId: String, targetEventId: String, reaction: String, additionalContent: Content? = null): Event {
         val content = ReactionContent(
                 ReactionInfo(
                         RelationType.ANNOTATION,
@@ -380,7 +399,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 senderId = userId,
                 eventId = localId,
                 type = EventType.REACTION,
-                content = content.toContent(),
+                content = content.toContent().plus(additionalContent.orEmpty()),
                 unsignedData = UnsignedData(age = null, transactionId = localId)
         )
     }
@@ -390,6 +409,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             attachment: ContentAttachmentData,
             rootThreadEventId: String?,
             relatesTo: RelationDefaultContent?,
+            additionalContent: Content?,
     ): Event {
         var width = attachment.width
         var height = attachment.height
@@ -417,7 +437,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 url = attachment.queryUri.toString(),
                 relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
-        return createMessageEvent(roomId, content)
+        return createMessageEvent(roomId, content, additionalContent)
     }
 
     private fun createVideoEvent(
@@ -425,6 +445,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             attachment: ContentAttachmentData,
             rootThreadEventId: String?,
             relatesTo: RelationDefaultContent?,
+            additionalContent: Content?,
     ): Event {
         val mediaDataRetriever = MediaMetadataRetriever()
         mediaDataRetriever.setDataSource(context, attachment.queryUri)
@@ -459,7 +480,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 url = attachment.queryUri.toString(),
                 relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
-        return createMessageEvent(roomId, content)
+        return createMessageEvent(roomId, content, additionalContent)
     }
 
     private fun createAudioEvent(
@@ -468,6 +489,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             isVoiceMessage: Boolean,
             rootThreadEventId: String?,
             relatesTo: RelationDefaultContent?,
+            additionalContent: Content?
     ): Event {
         val content = MessageAudioContent(
                 msgType = MessageType.MSGTYPE_AUDIO,
@@ -485,7 +507,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 voiceMessageIndicator = if (!isVoiceMessage) null else emptyMap(),
                 relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
-        return createMessageEvent(roomId, content)
+        return createMessageEvent(roomId, content, additionalContent)
     }
 
     private fun createFileEvent(
@@ -493,6 +515,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             attachment: ContentAttachmentData,
             rootThreadEventId: String?,
             relatesTo: RelationDefaultContent?,
+            additionalContent: Content?
     ): Event {
         val content = MessageFileContent(
                 msgType = MessageType.MSGTYPE_FILE,
@@ -504,15 +527,16 @@ internal class LocalEchoEventFactory @Inject constructor(
                 url = attachment.queryUri.toString(),
                 relatesTo = relatesTo ?: rootThreadEventId?.let { generateThreadRelationContent(it) }
         )
-        return createMessageEvent(roomId, content)
+        return createMessageEvent(roomId, content, additionalContent)
     }
 
-    private fun createMessageEvent(roomId: String, content: MessageContent? = null): Event {
-        return createEvent(roomId, EventType.MESSAGE, content.toContent())
+    private fun createMessageEvent(roomId: String, content: MessageContent, additionalContent: Content?): Event {
+        return createEvent(roomId, EventType.MESSAGE, content.toContent(), additionalContent)
     }
 
-    fun createEvent(roomId: String, type: String, content: Content?): Event {
+    fun createEvent(roomId: String, type: String, content: Content?, additionalContent: Content? = null): Event {
         val newContent = enhanceStickerIfNeeded(type, content) ?: content
+        val updatedNewContent = newContent?.plus(additionalContent.orEmpty()) ?: additionalContent
         val localId = LocalEcho.createLocalEchoId()
         return Event(
                 roomId = roomId,
@@ -520,7 +544,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 senderId = userId,
                 eventId = localId,
                 type = type,
-                content = newContent,
+                content = updatedNewContent,
                 unsignedData = UnsignedData(age = null, transactionId = localId)
         )
     }
@@ -555,6 +579,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             msgType: String,
             autoMarkdown: Boolean,
             formattedText: String?,
+            additionalContent: Content? = null,
     ): Event {
         val content = formattedText?.let { TextContent(text.toString(), it) } ?: createTextContent(text, autoMarkdown)
         return createEvent(
@@ -564,8 +589,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         rootThreadEventId = rootThreadEventId,
                         latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
                         msgType = msgType
-                )
-                        .toContent()
+                ).toContent().plus(additionalContent.orEmpty())
         )
     }
 
@@ -584,6 +608,7 @@ internal class LocalEchoEventFactory @Inject constructor(
             autoMarkdown: Boolean,
             rootThreadEventId: String? = null,
             showInThread: Boolean,
+            additionalContent: Content? = null
     ): Event? {
         // Fallbacks and event representation
         // TODO Add error/warning logs when any of this is null
@@ -621,7 +646,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                         showInThread = showInThread
                 )
         )
-        return createMessageEvent(roomId, content)
+        return createMessageEvent(roomId, content, additionalContent)
     }
 
     private fun generateThreadRelationContent(rootThreadEventId: String) =
@@ -750,7 +775,7 @@ internal class LocalEchoEventFactory @Inject constructor(
         }
     }
      */
-    fun createRedactEvent(roomId: String, eventId: String, reason: String?): Event {
+    fun createRedactEvent(roomId: String, eventId: String, reason: String?, additionalContent: Content? = null): Event {
         val localId = LocalEcho.createLocalEchoId()
         return Event(
                 roomId = roomId,
@@ -759,7 +784,7 @@ internal class LocalEchoEventFactory @Inject constructor(
                 eventId = localId,
                 type = EventType.REDACTION,
                 redacts = eventId,
-                content = reason?.let { mapOf("reason" to it).toContent() },
+                content = reason?.let { mapOf("reason" to it).toContent().plus(additionalContent.orEmpty()) } ?: additionalContent,
                 unsignedData = UnsignedData(age = null, transactionId = localId)
         )
     }
@@ -776,9 +801,14 @@ internal class LocalEchoEventFactory @Inject constructor(
             formattedText: String?,
             autoMarkdown: Boolean,
             rootThreadEventId: String?,
+            additionalContent: Content? = null,
     ): Event {
         val messageContent = quotedEvent.getLastMessageContent()
-        val textMsg = if (messageContent is MessageContentWithFormattedBody) { messageContent.formattedBody } else { messageContent?.body }
+        val textMsg = if (messageContent is MessageContentWithFormattedBody) {
+            messageContent.formattedBody
+        } else {
+            messageContent?.body
+        }
         val quoteText = legacyRiotQuoteText(textMsg, text)
         val quoteFormattedText = "<blockquote>$textMsg</blockquote>$formattedText"
 
@@ -791,13 +821,15 @@ internal class LocalEchoEventFactory @Inject constructor(
                                     rootThreadEventId = rootThreadEventId,
                                     latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId),
                                     msgType = MessageType.MSGTYPE_TEXT
-                            )
+                            ),
+                    additionalContent,
             )
         } else {
             createFormattedTextEvent(
                     roomId,
                     markdownParser.parse(quoteText, force = true, advanced = autoMarkdown).copy(formattedText = quoteFormattedText),
-                    MessageType.MSGTYPE_TEXT
+                    MessageType.MSGTYPE_TEXT,
+                    additionalContent,
             )
         }
     }

From 1647fe233f5211457d6f5d872fbd40cc33def2ff Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 15:45:44 +0200
Subject: [PATCH 329/400] Voice Broadcast - Introduce
 io.element.voice_broadcast_chunk key in voice messages

---
 .../voicebroadcast/VoiceBroadcastConstants.kt |  7 +++++-
 .../VoiceBroadcastExtensions.kt               | 18 +++++++------
 .../voicebroadcast/VoiceBroadcastPlayer.kt    |  2 +-
 .../model/VoiceBroadcastChunk.kt              | 25 +++++++++++++++++++
 .../usecase/StartVoiceBroadcastUseCase.kt     |  6 ++++-
 5 files changed, 48 insertions(+), 10 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastChunk.kt

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index d445dfdfbe..7b937f740e 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -16,11 +16,16 @@
 
 package im.vector.app.features.voicebroadcast
 
+import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
+
 object VoiceBroadcastConstants {
 
     /** Voice Broadcast State Event. */
     const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info"
 
+    /** Custom key passed to the [MessageAudioContent] with Voice Broadcast information. */
+    const val VOICE_BROADCAST_CHUNK_KEY = "io.element.voice_broadcast_chunk"
+
     /** Default voice broadcast chunk duration, in seconds. */
-    const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 120
+    const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 10
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
index d016703968..fe449c679e 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
@@ -16,14 +16,18 @@
 
 package im.vector.app.features.voicebroadcast
 
-import org.matrix.android.sdk.api.session.events.model.RelationType
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastChunk
+import org.matrix.android.sdk.api.session.events.model.Content
 import org.matrix.android.sdk.api.session.events.model.getRelationContent
+import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
 
-fun MessageAudioEvent?.isVoiceBroadcast() = this?.getVoiceBroadcastEventId() != null
+fun MessageAudioEvent?.isVoiceBroadcast() = this?.root?.getClearContent()?.get(VoiceBroadcastConstants.VOICE_BROADCAST_CHUNK_KEY) != null
+fun MessageAudioEvent.getVoiceBroadcastEventId(): String? = if (isVoiceBroadcast()) root.getRelationContent()?.eventId else null
 
-fun MessageAudioEvent.getVoiceBroadcastEventId(): String? =
-        // TODO Improve this condition by checking the referenced event type
-        root.takeIf { content.voiceMessageIndicator != null }
-                ?.getRelationContent()?.takeIf { it.type == RelationType.REFERENCE }
-                ?.eventId
+fun MessageAudioEvent.getVoiceBroadcastChunk(): VoiceBroadcastChunk? {
+    @Suppress("UNCHECKED_CAST")
+    return (root.getClearContent()?.get(VoiceBroadcastConstants.VOICE_BROADCAST_CHUNK_KEY) as? Content).toModel<VoiceBroadcastChunk>()
+}
+
+val MessageAudioEvent.sequence: Int? get() = getVoiceBroadcastChunk()?.sequence
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 41ed1ff84d..dfd50ea5cb 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -85,7 +85,7 @@ class VoiceBroadcastPlayer @Inject constructor(
     private fun updatePlaylist(room: Room, eventId: String) {
         val timelineEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId)
         val audioEvents = timelineEvents.mapNotNull { it.root.asMessageAudioEvent() }
-        playlist = audioEvents.sortedBy { it.root.originServerTs }
+        playlist = audioEvents.sortedBy { it.getVoiceBroadcastChunk()?.sequence?.toLong() ?: it.root.originServerTs }
     }
 
     private fun startPlayback() {
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastChunk.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastChunk.kt
new file mode 100644
index 0000000000..e0f6e6e7b1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/VoiceBroadcastChunk.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.voicebroadcast.model
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class VoiceBroadcastChunk(
+        @Json(name = "sequence") val sequence: Int? = null
+)
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 780150d5e7..ba1799c520 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -23,6 +23,7 @@ import im.vector.app.features.attachments.toContentAttachmentData
 import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastChunk
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import im.vector.lib.multipicker.utils.toMultiPickerAudioType
@@ -98,7 +99,10 @@ class StartVoiceBroadcastUseCase @Inject constructor(
                 attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
                 compressBeforeSending = false,
                 roomIds = emptySet(),
-                relatesTo = RelationDefaultContent(RelationType.REFERENCE, referenceEventId)
+                relatesTo = RelationDefaultContent(RelationType.REFERENCE, referenceEventId),
+                additionalContent = mapOf(
+                        VoiceBroadcastConstants.VOICE_BROADCAST_CHUNK_KEY to VoiceBroadcastChunk(sequence = sequence).toContent()
+                )
         )
     }
 }

From 64456860e239529f71f944f670c4e7c4a58f87bd Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 16:00:23 +0200
Subject: [PATCH 330/400] Voice Broadcast - Add deviceId in state event content

---
 .../voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt    | 2 ++
 .../voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt        | 1 +
 2 files changed, 3 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
index 5044bb5c34..a9db63c538 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
@@ -38,6 +38,8 @@ data class MessageVoiceBroadcastInfoContent(
         @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
         @Json(name = "m.new_content") override val newContent: Content? = null,
 
+        /** The device from which the broadcast has been started. */
+        @Json(name = "device_id") val deviceId: String? = null,
         /** The [VoiceBroadcastState] value. **/
         @Json(name = "state") val voiceBroadcastStateStr: String = "",
         /** The length of the voice chunks in seconds. **/
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index ba1799c520..7e43439b45 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -71,6 +71,7 @@ class StartVoiceBroadcastUseCase @Inject constructor(
                 eventType = VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
                 stateKey = session.myUserId,
                 body = MessageVoiceBroadcastInfoContent(
+                        deviceId = session.sessionParams.deviceId,
                         voiceBroadcastStateStr = VoiceBroadcastState.STARTED.value,
                         chunkLength = chunkLength,
                 ).toContent()

From 5438c7e089e724675ed6dffca5678641e934c2dd Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 16:39:13 +0200
Subject: [PATCH 331/400] Add changelog

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

diff --git a/changelog.d/7397.wip b/changelog.d/7397.wip
new file mode 100644
index 0000000000..1a7d1866a6
--- /dev/null
+++ b/changelog.d/7397.wip
@@ -0,0 +1 @@
+[Voice Broadcast] Add additional data in events

From 5004db07fb49548a8166fd749f94d73a6780d526 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 17:22:44 +0200
Subject: [PATCH 332/400] Remove legacy comment

---
 .../voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt         | 1 -
 1 file changed, 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 7e43439b45..7cb66cd9e5 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -95,7 +95,6 @@ class StartVoiceBroadcastUseCase @Inject constructor(
                 "Voice Broadcast Part ($sequence).${voiceMessageFile.extension}"
         )
         val audioType = outputFileUri.toMultiPickerAudioType(context) ?: return
-        // TODO put sequence in event content
         room.sendService().sendMedia(
                 attachment = audioType.toContentAttachmentData(isVoiceMessage = true),
                 compressBeforeSending = false,

From a658e7727afd556fe266d2e4d7f143b440a5ae22 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 17:39:06 +0200
Subject: [PATCH 333/400] Voice Broadcast - Update chunk length to 120 sec

---
 .../app/features/voicebroadcast/VoiceBroadcastConstants.kt      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
index 7b937f740e..551eaa4dac 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt
@@ -27,5 +27,5 @@ object VoiceBroadcastConstants {
     const val VOICE_BROADCAST_CHUNK_KEY = "io.element.voice_broadcast_chunk"
 
     /** Default voice broadcast chunk duration, in seconds. */
-    const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 10
+    const val DEFAULT_CHUNK_LENGTH_IN_SECONDS = 120
 }

From 0781ee84d96c117b8904df01aafac4a88ed2a5b1 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 17:39:55 +0200
Subject: [PATCH 334/400] Reformat file

---
 .../app/features/voicebroadcast/VoiceBroadcastExtensions.kt      | 1 +
 1 file changed, 1 insertion(+)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
index fe449c679e..f9da2e76b1 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastExtensions.kt
@@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
 
 fun MessageAudioEvent?.isVoiceBroadcast() = this?.root?.getClearContent()?.get(VoiceBroadcastConstants.VOICE_BROADCAST_CHUNK_KEY) != null
+
 fun MessageAudioEvent.getVoiceBroadcastEventId(): String? = if (isVoiceBroadcast()) root.getRelationContent()?.eventId else null
 
 fun MessageAudioEvent.getVoiceBroadcastChunk(): VoiceBroadcastChunk? {

From e4a98378a1c12886998eb6b8d3ee1304e8a3db4f Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 21:11:03 +0200
Subject: [PATCH 335/400] Fix unit test

---
 .../src/test/java/im/vector/app/test/fakes/FakeSendService.kt  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSendService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSendService.kt
index 04b9b95ce1..425a485561 100644
--- a/vector/src/test/java/im/vector/app/test/fakes/FakeSendService.kt
+++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSendService.kt
@@ -17,6 +17,7 @@
 package im.vector.app.test.fakes
 
 import io.mockk.mockk
+import org.matrix.android.sdk.api.session.events.model.Content
 import org.matrix.android.sdk.api.session.room.model.message.PollType
 import org.matrix.android.sdk.api.session.room.send.SendService
 import org.matrix.android.sdk.api.util.Cancelable
@@ -25,5 +26,5 @@ class FakeSendService : SendService by mockk() {
 
     private val cancelable = mockk<Cancelable>()
 
-    override fun sendPoll(pollType: PollType, question: String, options: List<String>): Cancelable = cancelable
+    override fun sendPoll(pollType: PollType, question: String, options: List<String>, additionalContent: Content?): Cancelable = cancelable
 }

From 2e4034ffc105d000f1efc5976c08fdb76d9ed116 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 22:05:45 +0200
Subject: [PATCH 336/400] Remove extra character

---
 library/ui-strings/src/main/res/values/strings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index ba83a4553c..61779036be 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3347,7 +3347,7 @@
     <string name="labs_enable_client_info_recording_title">Enable client info recording</string>
     <string name="labs_enable_client_info_recording_summary">Record the client name, version, and url to recognise sessions more easily in session manager.</string>
     <string name="labs_enable_voice_broadcast_title">Enable Voice broadcast (under active development)</string>
-    <string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline.‡</string>
+    <string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline.</string>
 
     <!-- Note to translators: %s will be replaces with selected space name -->
     <string name="home_empty_space_no_rooms_title">%s\nis looking a little empty.</string>

From 096b423cc1ebd3774c40ae8cc9604aa50da57d24 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 22:10:18 +0200
Subject: [PATCH 337/400] Update changelog wording

---
 changelog.d/7393.wip | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/changelog.d/7393.wip b/changelog.d/7393.wip
index b2112be5e9..7d82dc5769 100644
--- a/changelog.d/7393.wip
+++ b/changelog.d/7393.wip
@@ -1 +1 @@
-[Voice Broadcast] Move the feature flag to the labs
+[Voice Broadcast] Enable the feature (behind a lab flag and only for Android 10 and up)

From cafbb17caaa9b5b1799b88bd679829e1fca05a03 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Tue, 18 Oct 2022 22:37:37 +0200
Subject: [PATCH 338/400] Change wording to lowercase

---
 library/ui-strings/src/main/res/values/strings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 61779036be..fac36ffa52 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3346,7 +3346,7 @@
     <string name="labs_enable_session_manager_summary">Have greater visibility and control over all your sessions.</string>
     <string name="labs_enable_client_info_recording_title">Enable client info recording</string>
     <string name="labs_enable_client_info_recording_summary">Record the client name, version, and url to recognise sessions more easily in session manager.</string>
-    <string name="labs_enable_voice_broadcast_title">Enable Voice broadcast (under active development)</string>
+    <string name="labs_enable_voice_broadcast_title">Enable voice broadcast (under active development)</string>
     <string name="labs_enable_voice_broadcast_summary">Be able to record and send voice broadcast in room timeline.</string>
 
     <!-- Note to translators: %s will be replaces with selected space name -->

From d0c5c3bc43664ac35a8e2441ce2fed8bcfeb2230 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 18 Oct 2022 23:07:29 +0000
Subject: [PATCH 339/400] Bump flipper from 0.171.0 to 0.171.1

Bumps `flipper` from 0.171.0 to 0.171.1.

Updates `flipper` from 0.171.0 to 0.171.1
- [Release notes](https://github.com/facebook/flipper/releases)
- [Commits](https://github.com/facebook/flipper/compare/v0.171.0...v0.171.1)

Updates `flipper-network-plugin` from 0.171.0 to 0.171.1
- [Release notes](https://github.com/facebook/flipper/releases)
- [Commits](https://github.com/facebook/flipper/compare/v0.171.0...v0.171.1)

---
updated-dependencies:
- dependency-name: com.facebook.flipper:flipper
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: com.facebook.flipper:flipper-network-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index 80380c2be2..f081e0a874 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -18,7 +18,7 @@ def markwon = "4.6.2"
 def moshi = "1.14.0"
 def lifecycle = "2.5.1"
 def flowBinding = "1.2.0"
-def flipper = "0.171.0"
+def flipper = "0.171.1"
 def epoxy = "5.0.0"
 def mavericks = "3.0.1"
 def glide = "4.14.2"

From ff4ec3f5838d57356026849af5f8ff6117704a19 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Wed, 19 Oct 2022 10:00:34 +0200
Subject: [PATCH 340/400] Fix typo

---
 changelog.d/7363.wip | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/changelog.d/7363.wip b/changelog.d/7363.wip
index b5a5f4c352..ee71e799fa 100644
--- a/changelog.d/7363.wip
+++ b/changelog.d/7363.wip
@@ -1 +1 @@
-[Voice Broadcast] Record and send not aggregated voice messages to the room
+[Voice Broadcast] Record and send non aggregated voice messages to the room

From 4f652f102676e8bd2364149d00009f9d0f1ea656 Mon Sep 17 00:00:00 2001
From: Hugh Nimmo-Smith <hughns@element.io>
Date: Wed, 19 Oct 2022 09:12:09 +0100
Subject: [PATCH 341/400] Request changes from review

---
 .../sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt   | 7 +------
 .../rendezvous/transports/SimpleHttpRendezvousTransport.kt | 3 ++-
 .../verification/SASDefaultVerificationTransaction.kt      | 4 ++--
 3 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
index e844143889..c1d6b1b70e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt
@@ -46,11 +46,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
         private const val ALGORITHM_SPEC = "AES/GCM/NoPadding"
         private const val KEY_SPEC = "AES"
         private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value
-
-        // this is the same representation as for SAS but we delimit by dashes instead of spaces for readability
-        private fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
-            return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(byteArray).replace(" ", "-")
-        }
     }
 
     @JsonClass(generateAdapter = true)
@@ -114,7 +109,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu
             aesKey = sas.generateShortCode(aesInfo, 32)
 
             val rawChecksum = sas.generateShortCode(aesInfo, 5)
-            return getDecimalCodeRepresentation(rawChecksum)
+            return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(rawChecksum, separator = "-")
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
index 7a5cbe5424..620b599e3d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt
@@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransport
 import timber.log.Timber
 import java.text.SimpleDateFormat
 import java.util.Date
+import java.util.Locale
 
 /**
  * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886
@@ -87,7 +88,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor
             val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response")
 
             response.header("expires")?.let {
-                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz")
+                val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US)
                 expiresAt = format.parse(it)
             }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
index b306288c5e..29b416bb82 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt
@@ -95,7 +95,7 @@ internal abstract class SASDefaultVerificationTransaction(
          * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers,
          * or with the three numbers on separate lines.
          */
-        fun getDecimalCodeRepresentation(byteArray: ByteArray): String {
+        fun getDecimalCodeRepresentation(byteArray: ByteArray, separator: String = " "): String {
             val b0 = byteArray[0].toUnsignedInt() // need unsigned byte
             val b1 = byteArray[1].toUnsignedInt() // need unsigned byte
             val b2 = byteArray[2].toUnsignedInt() // need unsigned byte
@@ -107,7 +107,7 @@ internal abstract class SASDefaultVerificationTransaction(
             val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000
             // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000
             val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000
-            return "$first $second $third"
+            return "$first$separator$second$separator$third"
         }
     }
 

From 62d4c811ddd05c73a657628c984a37d93cfb7525 Mon Sep 17 00:00:00 2001
From: Nizami <nizamismidov4@gmail.com>
Date: Tue, 18 Oct 2022 09:37:48 +0000
Subject: [PATCH 342/400] Translated using Weblate (Azerbaijani)

Currently translated at 2.4% (62 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/az/
---
 .../src/main/res/values-az/strings.xml        | 21 ++-----------------
 1 file changed, 2 insertions(+), 19 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-az/strings.xml b/library/ui-strings/src/main/res/values-az/strings.xml
index 84f2772950..53100db285 100644
--- a/library/ui-strings/src/main/res/values-az/strings.xml
+++ b/library/ui-strings/src/main/res/values-az/strings.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-
     <string name="notice_room_invite_no_invitee">%s-nin dəvəti</string>
     <string name="notice_room_invite">%1$s dəvət etdi %2$s</string>
     <string name="notice_room_invite_you">%1$s sizi dəvət etdi</string>
@@ -27,37 +26,22 @@
     <string name="notice_room_visibility_shared">bütün otaq üzvləri.</string>
     <string name="notice_room_visibility_world_readable">hər kəs.</string>
     <string name="notice_room_update">%s bu otağı təkmilləşdirdi.</string>
-
-
     <string name="notice_avatar_changed_too">(avatar da dəyişdirilib)</string>
     <string name="notice_room_name_removed">%1$s otaq adını sildi</string>
     <string name="notice_room_topic_removed">%1$s otaq mövzusunu sildi</string>
     <string name="notice_room_third_party_invite">%1$s otağa qoşulmaq üçün %2$s dəvətnamə göndərdi</string>
     <string name="notice_room_third_party_revoked_invite">%1$s otağa qoşulmaq üçün %2$s dəvətini ləğv etdi</string>
     <string name="notice_room_third_party_registered_invite">%1$s %2$s üçün dəvəti qəbul etdi</string>
-
     <string name="notice_crypto_unable_to_decrypt">** Şifrəni aça bilmir: %s **</string>
     <string name="notice_crypto_error_unknown_inbound_session_id">Göndərənin cihazı bu mesaj üçün açarları bizə göndərməyib.</string>
-
     <string name="unable_to_send_message">Mesaj göndərmək olmur</string>
-
-
     <string name="matrix_error">Matris xətası</string>
-
-
     <string name="encrypted_message">Şifrəli mesaj</string>
-
     <string name="medium_email">Elektron poçt ünvanı</string>
     <string name="medium_phone_number">Telefon nömrəsi</string>
-
     <string name="room_displayname_room_invite">Otağa dəvət</string>
-
     <string name="room_displayname_two_members">%1$s və %2$s</string>
-
-
-
     <string name="room_displayname_empty_room">Boş otaq</string>
-
     <string name="initial_sync_start_importing_account">İlkin sinxronizasiya:
 \nHesab idxal olunur…</string>
     <string name="initial_sync_start_importing_account_crypto">İlkin sinxronizasiya:
@@ -72,9 +56,7 @@
 \nTərk olunmuş otaqların idxalı</string>
     <string name="initial_sync_start_importing_account_data">İlkin sinxronizasiya:
 \nHesab məlumatlarının idxalı</string>
-
     <string name="event_status_sending_message">Mesaj göndərilir…</string>
-
     <string name="notice_room_invite_no_invitee_with_reason">%1$s-nin dəvəti. Səbəb: %2$s</string>
     <string name="notice_room_invite_with_reason">%1$s dəvət olunmuş %2$s. Səbəb: %3$s</string>
     <string name="notice_room_invite_you_with_reason">%1$s sizi dəvət etdi. Səbəb: %2$s</string>
@@ -86,4 +68,5 @@
     <string name="notice_room_ban_with_reason">%1$s blokladı %2$s. Səbəb: %3$s</string>
     <string name="notice_room_third_party_registered_invite_with_reason">%1$s %2$s üçün dəvəti qəbul etdi. Səbəb: %3$s</string>
     <string name="notice_room_withdraw_with_reason">%1$s %2$s dəvətini geri götürdü. Səbəb: %3$s</string>
-</resources>
+    <string name="notice_room_created_by_you">Otağ yaratdınız</string>
+</resources>
\ No newline at end of file

From 48e9066df4c996092a61e02629547bf95210aebf Mon Sep 17 00:00:00 2001
From: "Auri B. P" <auri97@gmail.com>
Date: Mon, 17 Oct 2022 18:52:54 +0000
Subject: [PATCH 343/400] Translated using Weblate (Catalan)

Currently translated at 97.6% (2443 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ca/
---
 library/ui-strings/src/main/res/values-ca/strings.xml | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ca/strings.xml b/library/ui-strings/src/main/res/values-ca/strings.xml
index eddae8297e..fa363cee8c 100644
--- a/library/ui-strings/src/main/res/values-ca/strings.xml
+++ b/library/ui-strings/src/main/res/values-ca/strings.xml
@@ -1200,7 +1200,7 @@
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_device_name">session_name:</string>
     <string name="push_gateway_item_push_key">push_key:</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
+    <string name="push_gateway_item_app_id">ID d\'aplicació:</string>
     <string name="alert_push_are_disabled_description">Revisa la configuració per activar les notificacions</string>
     <string name="settings_troubleshoot_test_push_notification_content">Estàs veient la notificació! Clica\'m!</string>
     <string name="member_banned_by">Vetat per %1$s</string>
@@ -2742,4 +2742,7 @@
     <string name="settings_security_incognito_keyboard_summary">Sol·licita que no es desi cap dada personalitzada del teclat en funció del que escrius a les converses (per exemple l\'historial d\'escriptura o el diccionari). Tingues en compte que alguns teclats poden no respectar aquesta configuració.</string>
     <string name="settings_security_incognito_keyboard_title">Teclat incògnit</string>
     <string name="room_settings_global_block_unverified_info_text">🔒 Has activat el xifrat a només en sessions verificades a totes les sales, a Configuració &gt; Seguretat.</string>
-</resources>
+    <string name="device_manager_verification_status_unknown">Estat de verificació desconegut</string>
+    <string name="login_scan_qr_code">Escaneja codi QR</string>
+    <string name="push_gateway_item_device_id">ID de sessió:</string>
+</resources>
\ No newline at end of file

From 67344ff40e3cdf8aa7ec5deab3a07d9662fe7b50 Mon Sep 17 00:00:00 2001
From: waclaw66 <waclaw66@seznam.cz>
Date: Tue, 18 Oct 2022 09:21:45 +0000
Subject: [PATCH 344/400] Translated using Weblate (Czech)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/
---
 .../src/main/res/values-cs/strings.xml        | 35 ++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values-cs/strings.xml b/library/ui-strings/src/main/res/values-cs/strings.xml
index bc88530c3d..3410858988 100644
--- a/library/ui-strings/src/main/res/values-cs/strings.xml
+++ b/library/ui-strings/src/main/res/values-cs/strings.xml
@@ -1305,7 +1305,7 @@
 \nKlíče nejsou důvěryhodné</string>
     <string name="encryption_information_dg_xsigning_disabled">Křížové podpisování není zapnuto</string>
     <string name="settings_active_sessions_list">Aktivní relace</string>
-    <string name="settings_active_sessions_show_all">Ukázat všechny relace</string>
+    <string name="settings_active_sessions_show_all">Zobrazit všechny relace</string>
     <string name="settings_active_sessions_manage">Správa relací</string>
     <string name="settings_active_sessions_signout_device">Odhlásit se z této relace</string>
     <string name="settings_failed_to_get_crypto_device_info">Žádná kryptografická informace není k dispozici</string>
@@ -2826,4 +2826,37 @@
 \nPro zobrazování oznámení povolte přístup na dalších vyskakovacích oknech.</string>
     <string name="labs_enable_rich_text_editor_summary">Vyzkoušejte rozšířený textový editor (textový režim již brzy)</string>
     <string name="labs_enable_rich_text_editor_title">Povolit rozšířený textový editor</string>
+    <string name="qr_code_login_confirm_security_code_description">Ujistěte se, že znáte původ tohoto kódu. Propojením zařízení poskytnete někomu plný přístup ke svému účtu.</string>
+    <string name="qr_code_login_confirm_security_code">Potvrdit</string>
+    <string name="qr_code_login_try_again">Zkuste to znovu</string>
+    <string name="qr_code_login_status_no_match">Neshoduje se\?</string>
+    <string name="qr_code_login_signing_in">Probíhá přihlašování</string>
+    <string name="qr_code_login_connecting_to_device">Připojování k zařízení</string>
+    <string name="qr_code_login_scan_qr_code_button">Naskenujte QR kód</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Přihlašování na mobilním zařízením\?</string>
+    <string name="qr_code_login_show_qr_code_button">Zobrazit QR kód na tomto zařízení</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Vyberte možnost \"Naskenovat QR kód\"</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Začněte na přihlašovací obrazovce</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Vyberte možnost \"Přihlásit se pomocí QR kódu\"</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Začněte na přihlašovací obrazovce</string>
+    <string name="qr_code_login_new_device_instruction_3">Vyberte možnost \"Zobrazit QR kód na tomto zařízení\"</string>
+    <string name="qr_code_login_new_device_instruction_2">Přejděte do Nastavení -&gt; Zabezpečení a soukromí -&gt; Zobrazit všechny relace</string>
+    <string name="qr_code_login_new_device_instruction_1">Otevřete ${app_name} na vašem druhém zařízení</string>
+    <string name="qr_code_login_header_failed_denied_description">Žádost byla na druhém zařízení zamítnuta.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Propojení nebylo dokončeno v požadovaném čase.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Propojení s tímto zařízením není podporováno.</string>
+    <string name="qr_code_login_header_failed_title">Neúspěšné připojení</string>
+    <string name="qr_code_login_header_connected_description">Zkontrolujte vaše přihlášené zařízení, měl by se zobrazit níže uvedený kód. Zkontrolujte, zda níže uvedený kód odpovídá danému zařízení:</string>
+    <string name="qr_code_login_header_connected_title">Zabezpečené připojení navázáno</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Pomocí odhlášeného zařízení naskenujte níže uvedený QR kód.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Pomocí přihlášeného zařízení naskenujte níže uvedený QR kód:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Přihlásit se pomocí QR kódu</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Pomocí fotoaparátu na tomto zařízení naskenujte QR kód zobrazený na druhém zařízení:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">Naskenovat QR kód</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Pomocí tohoto zařízení se můžete přihlásit do mobilního nebo webového zařízení pomocí QR kódu. Můžete to provést dvěma způsoby:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Přihlásit se pomocí QR kódu</string>
+    <string name="login_scan_qr_code">Naskenovat QR kód</string>
 </resources>
\ No newline at end of file

From c1470618da23eae6be0ad3a3d1b420d3e9d81b43 Mon Sep 17 00:00:00 2001
From: Vri <element@vrifox.cc>
Date: Tue, 18 Oct 2022 04:05:52 +0000
Subject: [PATCH 345/400] Translated using Weblate (German)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/de/
---
 .../src/main/res/values-de/strings.xml           | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-de/strings.xml b/library/ui-strings/src/main/res/values-de/strings.xml
index 837a00e30b..fe849a1ddf 100644
--- a/library/ui-strings/src/main/res/values-de/strings.xml
+++ b/library/ui-strings/src/main/res/values-de/strings.xml
@@ -2771,18 +2771,18 @@
     <string name="permissions_rationale_msg_notification">${app_name} braucht die Berechtigung, um Benachrichtigungen anzuzeigen. Benachrichtigungen können deine Nachrichten, Einladungen etc. anzeigen.
 \n
 \nBitte erlaube den Zugriff im nächsten Dialog, damit Benachrichtigungen angezeigt werden können.</string>
-    <string name="qr_code_login_confirm_security_code_description">Bitte vergewissere dich, dass du den Ursprung dieses Codes kennst. Durch die Verbindung beider Geräte, gewährst du Zugriff auf deinen gesamten Account.</string>
+    <string name="qr_code_login_confirm_security_code_description">Bitte vergewissere dich, dass du den Ursprung dieses Codes kennst. Durch Verbindung neuer Geräte gewährst du vollen Zugriff auf dein Konto.</string>
     <string name="qr_code_login_confirm_security_code">Bestätigen</string>
-    <string name="qr_code_login_try_again">Nochmal versuchen</string>
+    <string name="qr_code_login_try_again">Erneut versuchen</string>
     <string name="qr_code_login_status_no_match">Keine Übereinstimmung\?</string>
     <string name="qr_code_login_signing_in">Du wirst angemeldet</string>
-    <string name="qr_code_login_connecting_to_device">Stelle Verbindung zum Gerät her</string>
-    <string name="qr_code_login_scan_qr_code_button">QR-Code scannen</string>
+    <string name="qr_code_login_connecting_to_device">Verbinde mit Gerät</string>
+    <string name="qr_code_login_scan_qr_code_button">QR-Code einlesen</string>
     <string name="qr_code_login_signing_in_a_mobile_device">Mobiles Gerät anmelden\?</string>
     <string name="qr_code_login_show_qr_code_button">QR-Code auf diesem Gerät anzeigen</string>
-    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Wähle \'QR-Code scannen\'</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Wähle „QR-Code einlesen“</string>
     <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Beginne auf dem Anmeldebildschirm</string>
-    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Wähle \'Mit QR-Code anmelden\'</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Wähle „Mit QR-Code anmelden“</string>
     <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Beginne auf dem Anmeldebildschirm</string>
     <string name="qr_code_login_new_device_instruction_3">Wähle \'QR-Code auf diesem Gerät anzeigen\'</string>
     <string name="qr_code_login_new_device_instruction_2">Gehe zu Einstellungen -&gt; Sicherheit und Privatsphäre -&gt; Alle Sitzungen anzeigen</string>
@@ -2793,8 +2793,8 @@
     <string name="qr_code_login_header_failed_title">Verbindung fehlgeschlagen</string>
     <string name="qr_code_login_header_connected_description">Überprüfe dein angemeldetes Gerät. Der unten gezeigte Code sollte angezeigt werden. Bestätige, dass beide Codes übereinstimmen:</string>
     <string name="qr_code_login_header_connected_title">Sichere Verbindung hergestellt</string>
-    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Scanne den unten angezeigten QR-Code mit deinem nicht angemeldeten Gerät.</string>
-    <string name="qr_code_login_header_show_qr_code_new_device_description">Benutze dein angemeldetes Gerät um den unten angezeigten QR-Code zu scannen:</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Lese den unten angezeigten QR-Code mit deinem nicht angemeldeten Gerät ein.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Benutze dein angemeldetes Gerät um den unten angezeigten QR-Code einzulesen:</string>
     <string name="qr_code_login_header_show_qr_code_title">Mit QR-Code anmelden</string>
     <string name="qr_code_login_header_scan_qr_code_description">Benutze die Kamera auf diesem Gerät um den vom anderen Gerät angezeigten QR-Code zu scannen:</string>
     <string name="qr_code_login_header_scan_qr_code_title">QR-Code scannen</string>

From ef0e2ccaca2161516ec07fceb2f3d2ecb36507b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com>
Date: Tue, 18 Oct 2022 20:57:46 +0000
Subject: [PATCH 346/400] Translated using Weblate (Estonian)

Currently translated at 99.6% (2495 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/et/
---
 .../src/main/res/values-et/strings.xml        | 61 +++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-et/strings.xml b/library/ui-strings/src/main/res/values-et/strings.xml
index ecb7b9757c..7ead21394c 100644
--- a/library/ui-strings/src/main/res/values-et/strings.xml
+++ b/library/ui-strings/src/main/res/values-et/strings.xml
@@ -2735,4 +2735,65 @@
     <string name="action_got_it">Selge lugu</string>
     <string name="labs_enable_rich_text_editor_summary">Proovi vormindatud teksti alusel töötavat tekstitoimetit (varsti lisandub ka vormindamata teksti režiim)</string>
     <string name="labs_enable_rich_text_editor_title">Võta kasutusele vormindatud teksti pruukiv tekstitoimeti</string>
+    <string name="qr_code_login_header_connected_description">Vaata seadet, kus sa oled Matrix\'i võtku loginud - seal peaks nüüd kuvatama QR-koodi. Kinnita, et allpool toodud QR-kood on sama kui tolles seadmes kuvatav kood:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Sa võid seda seadet kasutada nutiseadme või veebirakenduse sisselogimiseks QR-koodi alusel. Sa saad seda teha kahel moel:</string>
+    <string name="rich_text_editor_format_underline">Kasuta allajoonitud kirja</string>
+    <string name="rich_text_editor_format_strikethrough">Kasuta läbijoonitud kirja</string>
+    <string name="rich_text_editor_format_italic">Kasuta kaldkirja</string>
+    <string name="rich_text_editor_format_bold">Kasuta paksu kirja</string>
+    <string name="qr_code_login_confirm_security_code_description">Palun vaata, et sa kindlasti tead, kust see QR-kood kuvatakse. Sellisel viisil seadmete sidumisel sa annad oma kasutajakontole täiemahulise ligipääsu.</string>
+    <string name="qr_code_login_confirm_security_code">Kinnita</string>
+    <string name="qr_code_login_try_again">Proovi uuesti</string>
+    <string name="qr_code_login_status_no_match">Ei klapi\?</string>
+    <string name="qr_code_login_signing_in">Logime sind võrku</string>
+    <string name="qr_code_login_connecting_to_device">Loon ühendust seadmega</string>
+    <string name="qr_code_login_scan_qr_code_button">Loe QR-koodi</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Kas logid sisse nutiseadmest\?</string>
+    <string name="qr_code_login_show_qr_code_button">Näita selles seadmes QR-koodi</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Vali „Loe QR-koodi“</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Alusta sisselogimisvaatest</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Vali „Logi võrku QR-koodi abil“</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Alusta sisselogimisvaatest</string>
+    <string name="qr_code_login_new_device_instruction_3">Vali „Näita selles seadmes QR-koodi“</string>
+    <string name="qr_code_login_new_device_instruction_2">Ava Seadistused -&gt; Turvalisus ja privaatsus -&gt; Näita kõiki sessioone</string>
+    <string name="qr_code_login_new_device_instruction_1">Ava ${app_name} oma teises seades</string>
+    <string name="qr_code_login_header_failed_denied_description">Teine seade lükkas päringu tagasi.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Sidumine ei lõppenud etteantud aja jooksul.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Sidumine selle seadmega ei ole toetatud.</string>
+    <string name="qr_code_login_header_failed_title">Seoste loomine ei õnnestunud</string>
+    <string name="qr_code_login_header_connected_title">Turvaline ühendus on olemas</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Loe QR-koodi seadmega, kus sa oled Matrix\'i võrgust välja loginud.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Järgneva QR-koodi skaneerimiseks kasuta seadet, kus sa oled Matrix\'i võrku loginud:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Logi sisse QR-koodi abil</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Kasuta selle seadme kaamerat ja logi sisse teises seadmes kuvatud QR-koodi alusel:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">Loe QR-koodi</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="labs_enable_client_info_recording_summary">Sessioonide paremaks tuvastamiseks saad nüüd sessioonihalduris salvestada klientrakenduse nime, versiooni ja aadressi.</string>
+    <string name="labs_enable_client_info_recording_title">Luba klientrakenduse teabe salvestamine</string>
+    <string name="labs_enable_session_manager_summary">Sellega saad parema ülevaate oma sessioonidest ja võimaluse neid mugavasti hallata.</string>
+    <string name="labs_enable_session_manager_title">Kasuta uut sessioonihaldurit</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Logi sisse QR-koodi abil</string>
+    <string name="device_manager_session_details_device_operating_system">Operatsioonisüsteem</string>
+    <string name="device_manager_session_details_device_model">Mudel</string>
+    <string name="device_manager_session_details_device_browser">Brauser</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">Versioon</string>
+    <string name="device_manager_session_details_application_name">Nimi</string>
+    <string name="device_manager_session_details_application">Rakendus</string>
+    <string name="device_manager_push_notifications_description">Luba selles sessioonis tõuketeavitused.</string>
+    <string name="device_manager_push_notifications_title">Tõuketeavitused</string>
+    <string name="device_manager_verification_status_detail_other_session_unknown">Selle sessiooni olekut ei saa tuvastada enne kui oled ta verifitseerinud.</string>
+    <string name="device_manager_verification_status_unknown">Verifitseerimise olek on määratlemata</string>
+    <string name="login_scan_qr_code">Loe QR-koodi</string>
+    <string name="push_gateway_item_enabled">Kasutusel:</string>
+    <string name="push_gateway_item_device_id">Sessiooni tunnus:</string>
+    <string name="error_check_network">Midagi läks nüüd sassi. Palun kontrolli oma seadme võrguühendust ja proovi uuesti.</string>
+    <string name="grant_permission">Anna õigused</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} vajab teavituste näitamiseks õigusi.
+\nPalun luba vastavad õigused.</string>
+    <string name="permissions_rationale_msg_notification">${app_name} vajab teavituste näitamiseks õigusi. Teavituste sisuks võivad olla sulle saadetud sõnumid, kutsed ja muud olulist.
+\n
+\nJärgmistes vaadetes palun anna sellele rakendusele teavituste kuvamiseks vajalikud õigused.</string>
 </resources>
\ No newline at end of file

From ae2793e0c84b016b3d2811231ee8f9e940f2faa7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20Mesk=C3=B3?= <meskobalazs@gmail.com>
Date: Tue, 18 Oct 2022 18:04:45 +0000
Subject: [PATCH 347/400] Translated using Weblate (Hungarian)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/hu/
---
 library/ui-strings/src/main/res/values-hu/strings.xml | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-hu/strings.xml b/library/ui-strings/src/main/res/values-hu/strings.xml
index 57510e55a9..93015388f4 100644
--- a/library/ui-strings/src/main/res/values-hu/strings.xml
+++ b/library/ui-strings/src/main/res/values-hu/strings.xml
@@ -569,7 +569,7 @@ Matrixban az üzenetek láthatósága hasonlít az e-mailre. Az üzenet törlés
     <string name="settings_troubleshoot_diagnostic_success_status">Alapszintű diagnosztika nem talált hibát. Ha még mindig nem kapsz értesítéseket, kérlek küldj egy hiba jegyet amivel segítheted a hibakeresésünket.</string>
     <string name="settings_troubleshoot_diagnostic_failure_status_with_quickfix">Egy vagy több teszt is sikertelen volt, próbáld ki a javasolt javítást, javításokat.</string>
     <string name="settings_troubleshoot_diagnostic_failure_status_no_quickfix">Egy vagy több teszt sikertelenül végződött, kérlek küldj egy hibabejelentést ami segít nekünk a problémát kivizsgálni.</string>
-    <string name="settings_troubleshoot_test_system_settings_title">Rendszer beállítások.</string>
+    <string name="settings_troubleshoot_test_system_settings_title">Rendszerbeállítások.</string>
     <string name="settings_troubleshoot_test_system_settings_success">Az értesítések engedélyezve vannak a rendszerbeállításokban.</string>
     <string name="settings_troubleshoot_test_system_settings_failed">Az értesítések tiltva vannak a rendszerbeállításokban.
 Kérlek ellenőrizd a rendszerbeállításokat.</string>
@@ -582,7 +582,7 @@ Kérlek ellenőrizd a fiókbeállításokat.</string>
     <string name="settings_troubleshoot_test_device_settings_title">Munkamenet beállítások.</string>
     <string name="settings_troubleshoot_test_device_settings_success">Az értesítések engedélyezve vannak ezen az munkameneten.</string>
     <string name="settings_troubleshoot_test_device_settings_failed">Az értesítések tiltva vannak ezen a munkameneten. Kérlek ellenőrizd a ${app_name} beállításokat.</string>
-    <string name="settings_troubleshoot_test_device_settings_quickfix">Engedélyez</string>
+    <string name="settings_troubleshoot_test_device_settings_quickfix">Engedélyezés</string>
     <string name="settings_troubleshoot_test_play_services_title">Play Szolgáltatások ellenőrzése</string>
     <string name="settings_troubleshoot_test_play_services_success">Google Play Services APK elérhető és a legújabb verziójú.</string>
     <string name="settings_troubleshoot_test_play_services_failed">"${app_name} a Google Play Services-t használja  a „push” értesítések fogadásához, de úgy tűnik az nincs megfelelően beállítva:
@@ -824,7 +824,7 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="navigate_to_room_when_already_in_the_room">Már nézed ezt a szobát!</string>
     <string name="settings_general_title">Általános</string>
     <string name="settings_preferences">Beállítások</string>
-    <string name="settings_security_and_privacy">Biztonság &amp; Adatvédelem</string>
+    <string name="settings_security_and_privacy">Biztonság és adatvédelem</string>
     <string name="settings_push_rules">„Push” szabályok</string>
     <string name="settings_push_rules_no_rules">„Push” szabályok nincsenek</string>
     <string name="settings_push_gateway_no_pushers">„Push” átjárók nincsenek regisztrálva</string>
@@ -834,8 +834,8 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="push_gateway_item_device_name">Munkamenet képernyő név:</string>
     <string name="push_gateway_item_url">Url:</string>
     <string name="push_gateway_item_format">Formátum:</string>
-    <string name="preference_voice_and_video">Hang &amp; Videó</string>
-    <string name="preference_root_help_about">Segítség &amp; Névjegy</string>
+    <string name="preference_voice_and_video">Hang és videó</string>
+    <string name="preference_root_help_about">Súgó és névjegy</string>
     <string name="settings_troubleshoot_test_token_registration_quick_fix">Token regisztrálása</string>
     <string name="send_suggestion">Javaslat tétel</string>
     <string name="send_suggestion_content">A javaslatodat kérlek ír le alulra.</string>

From ee655a38402f0b796c1e5e86b841f1d7f061766b Mon Sep 17 00:00:00 2001
From: Szimszon <github@oregpreshaz.eu>
Date: Mon, 17 Oct 2022 18:43:51 +0000
Subject: [PATCH 348/400] Translated using Weblate (Hungarian)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/hu/
---
 .../src/main/res/values-hu/strings.xml        | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-hu/strings.xml b/library/ui-strings/src/main/res/values-hu/strings.xml
index 93015388f4..59792a9218 100644
--- a/library/ui-strings/src/main/res/values-hu/strings.xml
+++ b/library/ui-strings/src/main/res/values-hu/strings.xml
@@ -2772,4 +2772,37 @@ A Visszaállítási Kulcsot tartsd biztonságos helyen, mint pl. egy jelszókeze
     <string name="labs_enable_rich_text_editor_summary">Próbálja ki az új szövegbevitelt (hamarosan érkezik a sima szöveges üzemmód)</string>
     <string name="labs_enable_rich_text_editor_title">Vizuális szerkesztő engedélyezése</string>
     <string name="action_got_it">Értem</string>
+    <string name="qr_code_login_status_no_match">Nem egyezik\?</string>
+    <string name="qr_code_login_signing_in">Bejelentkeztetés</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Mobil eszközzel jelentkezel be\?</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Kezd a bejelentkező képernyőn</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Kezd a bejelentkező képernyőn</string>
+    <string name="qr_code_login_header_connected_description">Nézd meg a már bejelentkezett eszközödet, az alábbi kódot kell megjelenítenie. Erősítsd meg, hogy az alábbi kód megegyezik a másik eszközön láthatóval:</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Használd a már belépett eszközt az alábbi QR kód beolvasásához:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Ezzel az eszközzel, QR kód segítségével, bejelentkezhetsz mobil és webes munkamenetbe. Két lehetőséged is van:</string>
+    <string name="qr_code_login_confirm_security_code_description">Győződj meg a kód eredetéről. Az eszközök összekötésével esetleg valakinek teljes hozzáférést adhatsz a fiókodhoz.</string>
+    <string name="qr_code_login_confirm_security_code">Megerősítés</string>
+    <string name="qr_code_login_try_again">Próbáld újra</string>
+    <string name="qr_code_login_connecting_to_device">Csatlakozás az eszközhöz</string>
+    <string name="qr_code_login_scan_qr_code_button">QR kód beolvasása</string>
+    <string name="qr_code_login_show_qr_code_button">QR kód megjelenítése ezen az eszközön</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Válaszd ezt: „QR kód beolvasása”</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Válaszd ezt: „Belépés QR kóddal”</string>
+    <string name="qr_code_login_new_device_instruction_3">Válaszd ezt: „QR kód megjelenítése ezen az eszközön”</string>
+    <string name="qr_code_login_new_device_instruction_2">Menj a Beállítások -&gt; Biztonság és Adatvédelem -&gt; Minden munkamenet megjelenítése menübe</string>
+    <string name="qr_code_login_new_device_instruction_1">Nyisd meg a(z) ${app_name} alkalmazást a másik eszközön</string>
+    <string name="qr_code_login_header_failed_denied_description">A kérést elutasították a másik eszközön.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Az összekötés az elvárt időn belül nem fejeződött be.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Összekötés ezzel az eszközzel nem támogatott.</string>
+    <string name="qr_code_login_header_failed_title">Kapcsolat sikertelen</string>
+    <string name="qr_code_login_header_connected_title">Biztonságos kapcsolat beállítva</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">A kijelentkezett eszközzel olvasd be a QR kódot alább.</string>
+    <string name="qr_code_login_header_show_qr_code_title">Belépés QR kóddal</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Használd a kamerát ezen az eszközön a másik eszközödön megjelenő QR kód beolvasására:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">QR kód beolvasása</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Belépés QR kóddal</string>
+    <string name="login_scan_qr_code">QR kód beolvasása</string>
 </resources>
\ No newline at end of file

From f1f70c939f1511b44ef610c824a1760ae1fe10d8 Mon Sep 17 00:00:00 2001
From: Linerly <linerly@protonmail.com>
Date: Tue, 18 Oct 2022 07:01:50 +0000
Subject: [PATCH 349/400] Translated using Weblate (Indonesian)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/id/
---
 .../src/main/res/values-in/strings.xml        | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-in/strings.xml b/library/ui-strings/src/main/res/values-in/strings.xml
index 137805f8b0..6d20563d55 100644
--- a/library/ui-strings/src/main/res/values-in/strings.xml
+++ b/library/ui-strings/src/main/res/values-in/strings.xml
@@ -2720,4 +2720,37 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan.</string>
 \nMohon perbolehkan akses di munculan berikutnya untuk dapat melihat notifikasi.</string>
     <string name="labs_enable_rich_text_editor_summary">Coba editor teks kaya (mode teks biasa akan datang)</string>
     <string name="labs_enable_rich_text_editor_title">Aktifkan editor teks kaya</string>
+    <string name="qr_code_login_confirm_security_code_description">Pastikan Anda tahu asal kode ini. Dengan menautkan perangkat, Anda akan memberikan seseorang akses penuh ke akun Anda.</string>
+    <string name="qr_code_login_confirm_security_code">Konfirmasi</string>
+    <string name="qr_code_login_try_again">Coba lagi</string>
+    <string name="qr_code_login_status_no_match">Tidak cocok\?</string>
+    <string name="qr_code_login_signing_in">Memasukkan Anda</string>
+    <string name="qr_code_login_connecting_to_device">Menghubungkan ke perangkat</string>
+    <string name="qr_code_login_scan_qr_code_button">Pindai kode QR</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Ingin masuk di perangkat ponsel\?</string>
+    <string name="qr_code_login_show_qr_code_button">Tampilkan kode QR di perangkat ini</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Pilih \'Pindai dengan kode QR\'</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Mulai dari layar masuk</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Pilih \'Masuk dengan kode QR\'</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Mulai dari layar masuk</string>
+    <string name="qr_code_login_new_device_instruction_3">Pilih \'Tampilkan kode QR di perangkat ini\'</string>
+    <string name="qr_code_login_new_device_instruction_2">Pergi ke Pengaturan → Keamanan &amp; Privasi → Tampilkan Semua Sesi</string>
+    <string name="qr_code_login_new_device_instruction_1">Buka ${app_name} di perangkat Anda yang lain</string>
+    <string name="qr_code_login_header_failed_denied_description">Permintaan ditolak di perangkat lain.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Penautan tidak selesai dalam waktu yang dibutuhkan.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Penautan dengan perangkat ini tidak didukung.</string>
+    <string name="qr_code_login_header_failed_title">Koneksi tidak berhasil</string>
+    <string name="qr_code_login_header_connected_description">Periksa perangkat yang masuk, kode di bawah seharusnya ditampilkan. Konfirmasi bahwa kode di bawah cocok dengan perangkat itu:</string>
+    <string name="qr_code_login_header_connected_title">Koneksi aman dibuat</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Pindai kode QR di bawah dengan perangkat Anda yang telah keluar dari akun.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Gunakan perangkat yang sudah masuk untuk memindai kode QR di bawah:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Masuk dengan kode QR</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Gunakan kamera pada perangkat ini untuk memindai kode QR yang ditampilkan pada perangkat Anda yang lain:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">Pindai kode QR</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Anda dapat menggunakan perangkat ini untuk masuk ke perangkat ponsel atau web dengan sebuah kode QR. Ada dua cara untuk melalukan ini:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Masuk dengan Kode QR</string>
+    <string name="login_scan_qr_code">Pindai kode QR</string>
 </resources>
\ No newline at end of file

From c12b21fb8c91ac6acf42842aafffa32893cabdc8 Mon Sep 17 00:00:00 2001
From: random <dictionary@tutamail.com>
Date: Tue, 18 Oct 2022 10:07:30 +0000
Subject: [PATCH 350/400] Translated using Weblate (Italian)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/it/
---
 .../src/main/res/values-it/strings.xml        | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-it/strings.xml b/library/ui-strings/src/main/res/values-it/strings.xml
index 4bd4a5401c..cea69030bc 100644
--- a/library/ui-strings/src/main/res/values-it/strings.xml
+++ b/library/ui-strings/src/main/res/values-it/strings.xml
@@ -2763,4 +2763,37 @@
 \nConsenti l\'accesso nelle prossime schermate per potere vedere la notifica.</string>
     <string name="labs_enable_rich_text_editor_summary">Prova l\'editor in rich text (il testo semplice è in arrivo)</string>
     <string name="labs_enable_rich_text_editor_title">Attiva editor in rich text</string>
+    <string name="qr_code_login_confirm_security_code_description">Assicurati di conoscere l\'origine di questo codice. Collegando i dispositivi, fornirai a qualcuno l\'accesso totale al tuo account.</string>
+    <string name="qr_code_login_confirm_security_code">Conferma</string>
+    <string name="qr_code_login_try_again">Riprova</string>
+    <string name="qr_code_login_status_no_match">Non corrisponde\?</string>
+    <string name="qr_code_login_signing_in">Accesso in corso</string>
+    <string name="qr_code_login_connecting_to_device">Connessione al dispositivo</string>
+    <string name="qr_code_login_scan_qr_code_button">Scansiona codice QR</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Effettuare l\'accesso in un dispositivo mobile\?</string>
+    <string name="qr_code_login_show_qr_code_button">Mostra codice QR in questo dispositivo</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Seleziona \'Scansiona codice QR\'</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Inizia nella schermata di accesso</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Seleziona ‘Accedi con codice QR’</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Inizia nella schermata di accesso</string>
+    <string name="qr_code_login_new_device_instruction_3">Seleziona ‘Mostra codice QR in questo dispositivo’</string>
+    <string name="qr_code_login_new_device_instruction_2">Vai in Impostazioni -&gt; Sicurezza e privacy -&gt; Mostra tutte le sessioni</string>
+    <string name="qr_code_login_new_device_instruction_1">Apri ${app_name} sull\'altro dispositivo</string>
+    <string name="qr_code_login_header_failed_denied_description">La richiesta è stata negata sull\'altro dispositivo.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Il collegamento non è stato completato nel tempo previsto.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Il collegamento con questo dispositivo non è supportato.</string>
+    <string name="qr_code_login_header_failed_title">Connessione non riuscita</string>
+    <string name="qr_code_login_header_connected_description">Controlla il dispositivo che ha l\'accesso, dovresti vedere il codice sotto. Conferma che il codice corrisponda con quel dispositivo:</string>
+    <string name="qr_code_login_header_connected_title">Connessione sicura stabilita</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Scansiona il codice QR sottostante con il dispositivo che è disconnesso.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Usa il dispositivo che ha l\'accesso per scansionare il codice QR sotto:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Accedi con codice QR</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Usa la fotocamera di questo dispositivo per scansionare il codice QR mostrato nell\'altro dispositivo:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">Scansiona codice QR</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Puoi usare questo dispositivo per accedere in un dispositivo mobile o web con un codice QR. Ci sono due modi:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Accedi con codice QR</string>
+    <string name="login_scan_qr_code">Scansiona codice QR</string>
 </resources>
\ No newline at end of file

From 19164c235289e5de47d36d8c4a03cc4968bfc727 Mon Sep 17 00:00:00 2001
From: Suguru Hirahara <ovestekona@protonmail.com>
Date: Wed, 19 Oct 2022 09:30:27 +0000
Subject: [PATCH 351/400] Translated using Weblate (Japanese)

Currently translated at 89.2% (2233 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ja/
---
 .../src/main/res/values-ja/strings.xml        | 120 ++++++++++++++++--
 1 file changed, 110 insertions(+), 10 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-ja/strings.xml b/library/ui-strings/src/main/res/values-ja/strings.xml
index 3e817e398c..37c0bca52f 100644
--- a/library/ui-strings/src/main/res/values-ja/strings.xml
+++ b/library/ui-strings/src/main/res/values-ja/strings.xml
@@ -987,7 +987,7 @@
     <string name="settings_troubleshoot_test_push_loop_title">プッシュ通知のテスト</string>
     <string name="settings_troubleshoot_test_token_registration_failed">FCMトークンのホームサーバーへの登録に失敗しました:
 \n%1$s</string>
-    <string name="settings_troubleshoot_test_token_registration_success">FCMトークンのホームサーバーへの登録が成功しました。</string>
+    <string name="settings_troubleshoot_test_token_registration_success">FCMトークンがホームサーバーに登録されました。</string>
     <string name="settings_troubleshoot_test_token_registration_title">トークンの登録</string>
     <string name="settings_troubleshoot_test_fcm_failed_account_missing_quick_fix">アカウントを追加</string>
     <string name="settings_troubleshoot_test_fcm_failed_account_missing">[%1$s]
@@ -1233,7 +1233,7 @@
     <string name="login_terms_title">続行するには利用規約を承認してください</string>
     <string name="error_terms_not_accepted">ホームサーバーの利用規約を承認したら、再試行してください。</string>
     <string name="login_signup_submit">次に</string>
-    <string name="login_msisdn_confirm_submit">次に</string>
+    <string name="login_msisdn_confirm_submit">次へ</string>
     <string name="login_set_msisdn_submit">次に</string>
     <string name="login_set_email_submit">次に</string>
     <string name="login_reset_password_submit">次に</string>
@@ -1282,9 +1282,9 @@
     <string name="send_suggestion_failed">提案の送信に失敗しました(%s)</string>
     <string name="send_suggestion_sent">ありがとうございます、提案は正常に送信されました</string>
     <string name="settings_troubleshoot_test_token_registration_quick_fix">トークンの登録</string>
-    <string name="push_gateway_item_app_display_name">app_display_name:</string>
-    <string name="push_gateway_item_app_id">app_id:</string>
-    <string name="push_gateway_item_push_key">push_key:</string>
+    <string name="push_gateway_item_app_display_name">アプリケーションの表示名:</string>
+    <string name="push_gateway_item_app_id">App ID:</string>
+    <string name="push_gateway_item_push_key">Push Key:</string>
     <string name="settings_push_gateway_no_pushers">登録されたプッシュゲートウェイはありません</string>
     <string name="settings_push_rules_no_rules">プッシュ通知に関するルールが定義されていません</string>
     <string name="settings_push_rules">プッシュ通知に関するルール</string>
@@ -1383,8 +1383,8 @@
     <string name="settings_discovery_consent_action_revoke">同意を撤回</string>
     <string name="settings_discovery_consent_notice_on">あなたの連絡先から他のユーザーを発見するために、メールアドレスや電話番号をこのIDサーバーに送信することに同意しています。</string>
     <string name="settings_discovery_consent_title">メールと電話番号を送信</string>
-    <string name="settings_discovery_confirm_mail_not_clicked">%sに確認メールを送りました。まず、メールを確認してリンクをクリックしてください</string>
-    <string name="settings_discovery_confirm_mail">%sに確認のためのメールを送りました。メールにて確認リンクをクリックしてください</string>
+    <string name="settings_discovery_confirm_mail_not_clicked">%sにメールを送りました。メールを確認してリンクをクリックしてください</string>
+    <string name="settings_discovery_confirm_mail">%sにメールを送りました。メールの確認リンクをクリックしてください</string>
     <string name="settings_discovery_msisdn_title">発見可能な電話番号</string>
     <string name="settings_discovery_disconnect_identity_server_info">IDサーバーとの接続を解除すると、他のユーザーによって発見されなくなり、また、メールアドレスや電話で他のユーザーを招待することができなくなります。</string>
     <string name="settings_discovery_no_msisdn">電話番号を追加すると、発見可能に設定する電話番号を選択できるようになります。</string>
@@ -1412,7 +1412,7 @@
     <string name="send_suggestion">提案する</string>
     <string name="push_gateway_item_format">フォーマット:</string>
     <string name="push_gateway_item_url">URL:</string>
-    <string name="push_gateway_item_device_name">セッション名:</string>
+    <string name="push_gateway_item_device_name">セッションの表示名:</string>
     <string name="verify_not_me_self_verification">以下のうちいずれかが流出、あるいはハッキングされた恐れがあります。
 \n
 \n- あなたのパスワード
@@ -1696,7 +1696,7 @@
     <string name="room_message_placeholder">メッセージを送る…</string>
     <string name="error_file_too_big_simple">このファイルは大きすぎてアップロードできません。</string>
     <string name="identity_server_consent_dialog_content_question">この情報の送信に同意しますか?</string>
-    <string name="identity_server_consent_dialog_content_3">連絡先を発見するには、連絡先のデータ(電話番号や電子メール)をあなたのIDサーバーに送信する必要があります。プライバシーの保護のため、データは送信前にハッシュ化されます。</string>
+    <string name="identity_server_consent_dialog_content_3">連絡先を発見するには、連絡先のデータ(メールアドレスと電話番号)をあなたのIDサーバーに送信する必要があります。プライバシーの保護のため、データは送信前にハッシュ化されます。</string>
     <string name="identity_server_consent_dialog_title_2">メールアドレスと電話番号を%sに送信</string>
     <string name="settings_discovery_no_policy_provided">このIDサーバーは運営方針を提供していません</string>
     <string name="settings_discovery_hide_identity_server_policy_title">IDサーバーの運営方針を隠す</string>
@@ -2359,4 +2359,104 @@
     <string name="beta_title_bottom_sheet_action">ベータ版</string>
     <string name="beta">ベータ版</string>
     <string name="action_try_it_out">試す</string>
-</resources>
+    <string name="settings_presence_user_always_appears_offline">オフラインモード</string>
+    <string name="invites_empty_title">新着はありません。</string>
+    <string name="initial_sync_request_reason_unignored_users">- ユーザーの無視が解除されました</string>
+    <string name="onboarding_new_app_layout_button_try">試してみる</string>
+    <string name="onboarding_new_app_layout_feedback_message">右上をタップするとフィードバックを送信するオプションが表示されます。</string>
+    <string name="onboarding_new_app_layout_feedback_title">フィードバックを送信</string>
+    <string name="onboarding_new_app_layout_spaces_message">右下からスペースにより早く簡単にアクセスできます。</string>
+    <string name="onboarding_new_app_layout_spaces_title">スペースにアクセス</string>
+    <string name="onboarding_new_app_layout_welcome_message">${app_name}をシンプルにするために、タブはオプションになりました。右上のメニューから管理できます。</string>
+    <string name="onboarding_new_app_layout_welcome_title">新しいレイアウトにようこそ!</string>
+    <string name="settings_autoplay_animated_images_title">アニメーション画像を自動再生</string>
+    <string name="settings_troubleshoot_test_endpoint_registration_failed">エンドポイントのホームサーバーへの登録に失敗しました:
+\n%1$s</string>
+    <string name="settings_troubleshoot_test_endpoint_registration_success">エンドポイントがホームサーバーに登録されました。</string>
+    <string name="settings_troubleshoot_test_endpoint_registration_title">エンドポイントの登録</string>
+    <string name="grant_permission">権限を与える</string>
+    <string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name}は通知の表示に権限が必要です。
+\n権限を与えてください。</string>
+    <plurals name="search_space_multiple_parents">
+        <item quantity="other">%1$sと他%2$d名</item>
+    </plurals>
+    <string name="search_space_two_parents">%1$sと%2$s</string>
+    <string name="threads_labs_enable_notice_message">ホームサーバーがサポートしていないため、スレッド機能は不安定かもしれません。スレッドのメッセージは安定して表示されないおそれがあります。%sスレッド機能を有効にしてよろしいですか?</string>
+    <string name="threads_labs_enable_notice_title">スレッド(ベータ版)</string>
+    <string name="threads_beta_enable_notice_message">スレッドを用いると、会話のテーマを保ったり、会話を追跡したりするのが容易になります。%sスレッドを有効にするとアプリケーションが再起動します。再起動には時間がかかる可能性があります。</string>
+    <string name="threads_beta_enable_notice_title">スレッド(ベータ版)</string>
+    <string name="permissions_rationale_msg_notification">${app_name}は通知を表示するために許可を必要としています。通知にはメッセージや招待などが表示されます。
+\n
+\n通知を表示するには、次のポップアップでアクセスを許可してください。</string>
+    <string name="auth_reset_password_error_unverified">メールアドレスが認証されていません。メールボックスを確認してください</string>
+    <string name="call_stop_screen_sharing">画面共有を停止</string>
+    <string name="call_start_screen_sharing">画面を共有</string>
+    <string name="invites_title">招待</string>
+    <string name="device_manager_push_notifications_title">プッシュ通知</string>
+    <string name="device_manager_session_rename_edit_hint">セッション名</string>
+    <string name="device_manager_session_rename">セッションを改名</string>
+    <string name="device_manager_session_details_device_ip_address">IPアドレス</string>
+    <string name="device_manager_session_details_device_operating_system">オペレーティングシステム</string>
+    <string name="device_manager_session_details_device_model">形式</string>
+    <string name="device_manager_session_details_device_browser">ブラウザー</string>
+    <string name="device_manager_session_details_application_url">URL</string>
+    <string name="device_manager_session_details_application_version">バージョン</string>
+    <string name="device_manager_session_details_application_name">名称</string>
+    <string name="device_manager_session_details_application">アプリケーション</string>
+    <string name="ftue_personalize_skip_this_step">このステップをスキップ</string>
+    <string name="ftue_personalize_complete_title">問題ありません!</string>
+    <string name="ftue_personalize_lets_go">進みましょう</string>
+    <string name="ftue_auth_login_username_entry">ユーザー名 / メールアドレス / 電話番号</string>
+    <string name="ftue_auth_captcha_title">あなたは人間ですか?</string>
+    <string name="ftue_auth_password_reset_email_confirmation_subtitle">%sに送信された手順に従ってください</string>
+    <string name="ftue_auth_password_reset_confirmation">パスワードを再設定</string>
+    <string name="ftue_auth_forgot_password">パスワードを忘れた場合</string>
+    <string name="ftue_auth_email_resend_email">電子メールを再送信</string>
+    <string name="ftue_auth_email_verification_footer">電子メールが届いていませんか?</string>
+    <string name="ftue_auth_email_verification_subtitle">%sに送信された手順に従ってください</string>
+    <string name="ftue_auth_email_verification_title">メールアドレスを認証</string>
+    <string name="ftue_auth_phone_confirmation_resend_code">コードを再送信</string>
+    <string name="ftue_auth_phone_confirmation_subtitle">コードが%sに送信されました</string>
+    <string name="ftue_auth_phone_confirmation_title">電話番号を確認してください</string>
+    <string name="ftue_auth_sign_out_all_devices">全ての端末からサインアウト</string>
+    <string name="ftue_auth_reset_password">パスワードを再設定</string>
+    <string name="ftue_auth_new_password_subtitle">パスワードは8文字以上に設定してください。</string>
+    <string name="ftue_auth_new_password_title">パスワードを選択</string>
+    <string name="ftue_auth_new_password_entry_title">新しいパスワード</string>
+    <string name="ftue_auth_reset_password_breaker_title">電子メールを確認してください。</string>
+    <string name="ftue_auth_reset_password_email_subtitle">%sは認証リンクを送信します</string>
+    <string name="ftue_auth_phone_confirmation_entry_title">確認コード</string>
+    <string name="ftue_auth_phone_entry_title">電話番号</string>
+    <string name="ftue_auth_phone_subtitle">%sはアカウントの認証が必要です</string>
+    <string name="ftue_auth_phone_title">電話番号を入力してください</string>
+    <string name="ftue_auth_email_entry_title">メールアドレス</string>
+    <string name="ftue_auth_email_subtitle">%sはアカウントの認証が必要です</string>
+    <string name="labs_enable_rich_text_editor_title">リッチテキストエディターを有効にする</string>
+    <string name="labs_enable_deferred_dm_summary">最初のメッセージを送信する際にダイレクトメッセージを作成</string>
+    <string name="labs_enable_deferred_dm_title">遅延DMを有効にする</string>
+    <string name="space_list_empty_title">スペースがありません。</string>
+    <string name="labs_enable_new_app_layout_title">新しいレイアウトを有効にする</string>
+    <string name="home_layout_preferences_sort_activity">アクティビティー順</string>
+    <string name="home_layout_preferences_sort_name">アルファベット順</string>
+    <string name="home_layout_preferences_sort_by">並び替え</string>
+    <string name="home_layout_preferences_filters">フィルターを表示</string>
+    <string name="home_layout_preferences">レイアウトの設定</string>
+    <string name="action_got_it">了解</string>
+    <string name="action_next">次へ</string>
+    <string name="action_learn_more">詳しく知る</string>
+    <string name="time_unit_second_short">秒</string>
+    <string name="time_unit_minute_short">分</string>
+    <string name="time_unit_hour_short">時</string>
+    <string name="initial_sync_request_content">${app_name}は以下の理由で、キャッシュを消去して最新の状態にする必要があります。
+\n%s
+\n
+\nアプリケーションが再起動します。再起動には時間がかかる可能性があります。</string>
+    <string name="initial_sync_request_title">初期同期のリクエスト</string>
+    <string name="a11y_collapse_space_children">%sの子スペースを折りたたむ</string>
+    <string name="a11y_expand_space_children">%sの子スペースを展開</string>
+    <string name="explore_rooms">ルームを探索</string>
+    <string name="change_space">スペースを変更</string>
+    <string name="create_room">ルームを作成</string>
+    <string name="start_chat">チャットを開始</string>
+    <string name="all_chats">全ての会話</string>
+</resources>
\ No newline at end of file

From 225627fbfc3dcdfc419f50377279c5e39d24cd74 Mon Sep 17 00:00:00 2001
From: lvre <7uu3qrbvm@relay.firefox.com>
Date: Tue, 18 Oct 2022 23:42:10 +0000
Subject: [PATCH 352/400] Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/
---
 .../src/main/res/values-pt-rBR/strings.xml    | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
index 7a709757c9..97141a9765 100644
--- a/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
+++ b/library/ui-strings/src/main/res/values-pt-rBR/strings.xml
@@ -2772,4 +2772,37 @@
 \nPor favor permita acesso nos próximos pop-ups para ser capaz de visualizar notificação.</string>
     <string name="labs_enable_rich_text_editor_summary">Experimente o editor de texto rico (modo de texto puro vindo em breve)</string>
     <string name="labs_enable_rich_text_editor_title">Habilitar editor de texto rico</string>
+    <string name="qr_code_login_confirm_security_code_description">Por favor assegure que você sabe a origem deste código. Ao linkar dispositivos, você vai prover alguém com acesso completo a sua conta.</string>
+    <string name="qr_code_login_confirm_security_code">Confirmar</string>
+    <string name="qr_code_login_try_again">Tentar de novo</string>
+    <string name="qr_code_login_status_no_match">Nenhuma correspondência\?</string>
+    <string name="qr_code_login_signing_in">Fazendo-lhe signin</string>
+    <string name="qr_code_login_connecting_to_device">Conectando a dispositivo</string>
+    <string name="qr_code_login_scan_qr_code_button">Scannar QR code</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Fazendo signin com um dispositivo móvel\?</string>
+    <string name="qr_code_login_show_qr_code_button">Mostrar QR code neste dispositivo</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Selecione \'Scannar QR code\'</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Comece na tela de signin</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Selecione \'Fazer signin com QR code\'</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Comece na tela de signin</string>
+    <string name="qr_code_login_new_device_instruction_3">Selecione \'Mostrar QR code neste dispositivo\'</string>
+    <string name="qr_code_login_new_device_instruction_2">Vá para Configurações -&gt; Segurança &amp; Privacidade -&gt; Mostrar Todas as Sessões</string>
+    <string name="qr_code_login_new_device_instruction_1">Obra ${app_name} em seu outro dispositivo</string>
+    <string name="qr_code_login_header_failed_denied_description">A requisição foi negada no outro dispositivo.</string>
+    <string name="qr_code_login_header_failed_timeout_description">A linkagem não foi completada no tempo requerido.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Linkagem com este dispositivo não é suportado.</string>
+    <string name="qr_code_login_header_failed_title">Conexão malsucedida</string>
+    <string name="qr_code_login_header_connected_description">Cheque seu dispositivo feito signin, o código abaixo deveria ser exibido. Confirme que o código abaixo corresponde com esse dispositivo:</string>
+    <string name="qr_code_login_header_connected_title">Conexão segura estabelecida</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Scanne o QR code abaixo com seu dispositivo que está feito signout.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Use seu dispositivo feito signin para scannar o QR code abaixo:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Fazer signin com QR code</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Use a câmera neste dispositivo para scannar o QR code mostrado em seu outro dispositivo:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">Scannar QR code</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Você pode usar este dispositivo para fazer signin com um dispositivo móvel ou web com um QR code. Existem duas maneiras de fazer isto:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Fazer signin com QR Code</string>
+    <string name="login_scan_qr_code">Scannar QR code</string>
 </resources>
\ No newline at end of file

From 27b07e8b5ce2a1dc224e146f4fb69666fb37bbdb Mon Sep 17 00:00:00 2001
From: Nui Harime <harime.nui@yandex.ru>
Date: Wed, 19 Oct 2022 08:55:14 +0000
Subject: [PATCH 353/400] Translated using Weblate (Russian)

Currently translated at 95.7% (2396 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/ru/
---
 .../src/main/res/values-ru/strings.xml          | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values-ru/strings.xml b/library/ui-strings/src/main/res/values-ru/strings.xml
index 1953e46698..0349acedd1 100644
--- a/library/ui-strings/src/main/res/values-ru/strings.xml
+++ b/library/ui-strings/src/main/res/values-ru/strings.xml
@@ -1701,7 +1701,7 @@
     <string name="auth_msisdn_already_defined">Этот номер телефона уже используется.</string>
     <string name="settings_phone_number_empty">В ваш аккаунт не добавлен номер телефона</string>
     <string name="settings_emails">Адрес электронной почты</string>
-    <string name="settings_emails_empty">В ваш аккаунт не добавлен адрес электронной почты</string>
+    <string name="settings_emails_empty">В вашу учётную запись не добавлен адрес электронной почты</string>
     <string name="settings_phone_numbers">Телефонные номера</string>
     <string name="settings_remove_three_pid_confirmation_content">Удалить %s\?</string>
     <string name="error_threepid_auth_failed">Убедитесь, что вы перешли по ссылке в электронном письме, которое мы вам отправили.</string>
@@ -2777,4 +2777,19 @@
     <string name="device_manager_session_details_device_model">Модель</string>
     <string name="device_manager_session_details_device_operating_system">Операционная система</string>
     <string name="labs_enable_session_manager_title">Новый менеджер сессий</string>
+    <plurals name="device_manager_other_sessions_recommendation_description_inactive">
+        <item quantity="one">Рассмотрите возможность выхода из старых сессий (%1$d день или дольше), которые вы более не используете.</item>
+        <item quantity="few">Рассмотрите возможность выхода из старых сессий (%1$d дня или дольше), которые вы более не используете.</item>
+        <item quantity="many">Рассмотрите возможность выхода из старых сессий (%1$d дней или дольше), которые вы более не используете.</item>
+        <item quantity="other">Рассмотрите возможность выхода из старых сессий (%1$d дней или дольше), которые вы более не используете.</item>
+    </plurals>
+    <string name="poll_undisclosed_not_ended">Результаты будут видны после завершения опроса</string>
+    <string name="onboarding_new_app_layout_spaces_message">Доступ к пространствам (внизу справа) быстрее и проще, чем когда-либо прежде.</string>
+    <string name="onboarding_new_app_layout_spaces_title">Доступ к пространствам</string>
+    <plurals name="device_manager_inactive_sessions_description">
+        <item quantity="one">Рассмотрите возможность выхода из старых сессий (%1$d день или дольше), которые вы более не используете.</item>
+        <item quantity="few">Рассмотрите возможность выхода из старых сессий (%1$d дня или дольше), которые вы более не используете.</item>
+        <item quantity="many">Рассмотрите возможность выхода из старых сессий (%1$d дней или дольше), которые вы более не используете.</item>
+        <item quantity="other">Рассмотрите возможность выхода из старых сессий (%1$d дней или дольше), которые вы более не используете.</item>
+    </plurals>
 </resources>
\ No newline at end of file

From 5869c273bc550c814ea7f2bb5cb0bf6b92bfb99a Mon Sep 17 00:00:00 2001
From: Jozef Gaal <preklady@mayday.sk>
Date: Mon, 17 Oct 2022 21:16:47 +0000
Subject: [PATCH 354/400] Translated using Weblate (Slovak)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/
---
 .../src/main/res/values-sk/strings.xml        | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-sk/strings.xml b/library/ui-strings/src/main/res/values-sk/strings.xml
index ac2c4bea4a..43a8301d58 100644
--- a/library/ui-strings/src/main/res/values-sk/strings.xml
+++ b/library/ui-strings/src/main/res/values-sk/strings.xml
@@ -2826,4 +2826,37 @@
 \nPovoľte prístup na ďalších vyskakovacích oknách, aby ste mohli zobrazovať oznámenia.</string>
     <string name="labs_enable_rich_text_editor_summary">Vyskúšajte rozšírený textový editor (čistý textový režim sa objaví čoskoro)</string>
     <string name="labs_enable_rich_text_editor_title">Povoliť rozšírený textový editor</string>
+    <string name="qr_code_login_confirm_security_code_description">Uistite sa prosím, že poznáte pôvod tohto kódu. Prepojením zariadení poskytnete niekomu plný prístup k svojmu účtu.</string>
+    <string name="qr_code_login_confirm_security_code">Potvrdiť</string>
+    <string name="qr_code_login_try_again">Skúste to znova</string>
+    <string name="qr_code_login_status_no_match">Nezhoduje sa\?</string>
+    <string name="qr_code_login_signing_in">Prebieha prihlasovanie</string>
+    <string name="qr_code_login_connecting_to_device">Pripájanie k zariadeniu</string>
+    <string name="qr_code_login_scan_qr_code_button">Skenovať QR kód</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Prihlasovanie do mobilného zariadenia\?</string>
+    <string name="qr_code_login_show_qr_code_button">Zobraziť QR kód na tomto zariadení</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Vyberte možnosť \"Skenovať QR kód\"</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Začnite na prihlasovacej obrazovke</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Vyberte možnosť \"Prihlásiť sa pomocou QR kódu\"</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Začnite na prihlasovacej obrazovke</string>
+    <string name="qr_code_login_new_device_instruction_3">Vyberte možnosť \"Zobraziť QR kód na tomto zariadení\"</string>
+    <string name="qr_code_login_new_device_instruction_2">Prejdite do Nastavenia -&gt; Zabezpečenie a súkromie -&gt; Zobraziť všetky relácie</string>
+    <string name="qr_code_login_new_device_instruction_1">Otvorte ${app_name} na vašom druhom zariadení</string>
+    <string name="qr_code_login_header_failed_denied_description">Žiadosť bola na druhom zariadení zamietnutá.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Prepojenie nebolo dokončené v požadovanom čase.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Prepojenie s týmto zariadením nie je podporované.</string>
+    <string name="qr_code_login_header_failed_title">Neúspešné pripojenie</string>
+    <string name="qr_code_login_header_connected_description">Skontrolujte svoje prihlásené zariadenie, mal by sa zobraziť nasledujúci kód. Skontrolujte, či sa nižšie uvedený kód zhoduje s daným zariadením:</string>
+    <string name="qr_code_login_header_connected_title">Zabezpečené pripojenie bolo vytvorené</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Naskenujte nižšie uvedený QR kód pomocou zariadenia, ktoré je odhlásené.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Pomocou prihláseného zariadenia naskenujte nižšie uvedený QR kód:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Prihlásiť sa pomocou QR kódu</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Pomocou fotoaparátu na tomto zariadení naskenujte QR kód zobrazený na vašom druhom zariadení:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">Skenovať QR kód</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">Pomocou tohto zariadenia sa môžete prihlásiť do mobilného alebo webového zariadenia pomocou QR kódu. Môžete to urobiť dvoma spôsobmi:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Prihlásiť sa pomocou QR kódu</string>
+    <string name="login_scan_qr_code">Skenovať QR kód</string>
 </resources>
\ No newline at end of file

From c2600dc5356fc767bc81ba2c6d3d42f45c854c6e Mon Sep 17 00:00:00 2001
From: Ihor Hordiichuk <igor_ck@outlook.com>
Date: Mon, 17 Oct 2022 19:21:42 +0000
Subject: [PATCH 355/400] Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/
---
 .../src/main/res/values-uk/strings.xml        | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-uk/strings.xml b/library/ui-strings/src/main/res/values-uk/strings.xml
index 20b7348bdf..2a04e58f41 100644
--- a/library/ui-strings/src/main/res/values-uk/strings.xml
+++ b/library/ui-strings/src/main/res/values-uk/strings.xml
@@ -2880,4 +2880,37 @@
 \nДозвольте доступ до наступних спливних вікон, щоб мати змогу переглядати сповіщення.</string>
     <string name="labs_enable_rich_text_editor_summary">Спробуйте розширений текстовий редактор (незабаром з\'явиться режим звичайного тексту)</string>
     <string name="labs_enable_rich_text_editor_title">Увімкнути розширений текстовий редактор</string>
+    <string name="qr_code_login_confirm_security_code_description">Переконайтеся, що ви знаєте походження цього коду. Пов\'язавши пристрої, ви надасте будь-кому повний доступ до свого облікового запису.</string>
+    <string name="qr_code_login_confirm_security_code">Підтвердити</string>
+    <string name="qr_code_login_try_again">Повторити спробу</string>
+    <string name="qr_code_login_status_no_match">Не збігається\?</string>
+    <string name="qr_code_login_signing_in">Вхід</string>
+    <string name="qr_code_login_connecting_to_device">Під\'єднання до пристрою</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">Входите на мобільному пристрої\?</string>
+    <string name="qr_code_login_show_qr_code_button">Показати QR-код на цьому пристрої</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">Виберіть «Сканувати QR-код»</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">Виберіть «Увійти за допомогою QR-коду»</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">Почніть з екрана входу</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">Почніть з екрана входу</string>
+    <string name="qr_code_login_new_device_instruction_3">Виберіть «Показати QR-код на цьому пристрої»</string>
+    <string name="qr_code_login_new_device_instruction_2">Перейдіть до Налаштування -&gt; Безпека й приватність -&gt; Показати всі сеанси</string>
+    <string name="qr_code_login_new_device_instruction_1">Відкрийте ${app_name} на іншому своєму пристрої</string>
+    <string name="qr_code_login_header_failed_denied_description">Запит на іншому пристрої було відхилено.</string>
+    <string name="qr_code_login_header_failed_timeout_description">Пов\'язування не було завершено у встановлені терміни.</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">Пов\'язування з цим пристроєм не підтримується.</string>
+    <string name="qr_code_login_header_failed_title">Невдале з\'єднання</string>
+    <string name="qr_code_login_header_connected_description">Перевірте свій пристрій, на якому ви ввійшли. На екрані повинен з\'явитися код, наведений нижче. Переконайтеся, що наведений код збігається з кодом на вашому пристрої:</string>
+    <string name="qr_code_login_header_connected_title">Безпечне з\'єднання встановлено</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">Зіскануйте QR-код нижче своїм пристроєм, з якого ви вийшли.</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">Скануйте QR-код нижче за допомогою свого пристрою для входу:</string>
+    <string name="qr_code_login_header_show_qr_code_title">Увійти за допомогою QR-коду</string>
+    <string name="qr_code_login_header_scan_qr_code_description">Використовуйте камеру цього пристрою, щоб зісканувати QR-код, показаний на іншому пристрої:</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">За допомогою цього пристрою ви можете ввійти на мобільному або вебпристрої за допомогою QR-коду. Зробити це можна двома способами:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">Увійти за допомогою QR-коду</string>
+    <string name="qr_code_login_scan_qr_code_button">Сканувати QR-код</string>
+    <string name="qr_code_login_header_scan_qr_code_title">Сканувати QR-код</string>
+    <string name="login_scan_qr_code">Сканувати QR-код</string>
 </resources>
\ No newline at end of file

From 098aaf9d5c1e995839137603de142d9b796c4c80 Mon Sep 17 00:00:00 2001
From: phardyle <bradney_ccea@aleeas.com>
Date: Tue, 18 Oct 2022 01:51:18 +0000
Subject: [PATCH 356/400] Translated using Weblate (Chinese (Simplified))

Currently translated at 94.7% (2371 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hans/
---
 library/ui-strings/src/main/res/values-zh-rCN/strings.xml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
index 39992ff418..ae29132c91 100644
--- a/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rCN/strings.xml
@@ -2623,4 +2623,6 @@
     <string name="labs_enable_deferred_dm_summary">仅在首条消息创建私聊消息</string>
     <string name="labs_enable_deferred_dm_title">启用延迟的私聊消息</string>
     <string name="labs_enable_new_app_layout_summary">简化的Element,带有可选的标签</string>
-</resources>
+    <string name="settings_security_incognito_keyboard_title">无痕键盘</string>
+    <string name="settings_security_incognito_keyboard_summary">要求键盘不要基于你在对话中的输入更新任何个性化数据,如输入历史和字典。请注意,某些键盘可能不会遵守此设置。</string>
+</resources>
\ No newline at end of file

From b6a33d1b7a1ed0c734607b9a6c17a7a2e9db92cd Mon Sep 17 00:00:00 2001
From: Jeff Huang <s8321414@gmail.com>
Date: Tue, 18 Oct 2022 03:25:50 +0000
Subject: [PATCH 357/400] Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (2503 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/
---
 .../src/main/res/values-zh-rTW/strings.xml    | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
index b72f9bc5c6..c9057cd289 100644
--- a/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
+++ b/library/ui-strings/src/main/res/values-zh-rTW/strings.xml
@@ -2718,4 +2718,37 @@
 \n請在下一個彈出式視窗允許存取以檢視通知。</string>
     <string name="labs_enable_rich_text_editor_summary">試用格式化文字編輯器(純文字模式即將推出)</string>
     <string name="labs_enable_rich_text_editor_title">啟用格式化文字編輯器</string>
+    <string name="qr_code_login_confirm_security_code_description">請確保您知道此驗證碼的來源。透過連結裝置,您將為某人提供對您帳號的完整存取權限。</string>
+    <string name="qr_code_login_confirm_security_code">確認</string>
+    <string name="qr_code_login_try_again">再試一次</string>
+    <string name="qr_code_login_status_no_match">不相符?</string>
+    <string name="qr_code_login_signing_in">登入</string>
+    <string name="qr_code_login_connecting_to_device">連線至裝置</string>
+    <string name="qr_code_login_scan_qr_code_button">掃描 QR code</string>
+    <string name="qr_code_login_signing_in_a_mobile_device">正在使用行動裝置登入?</string>
+    <string name="qr_code_login_show_qr_code_button">在此裝置顯示 QR code</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_2">選取「掃描 QR code」</string>
+    <string name="qr_code_login_link_a_device_show_qr_code_instruction_1">從登入畫面開始</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_2">選取「使用 QR code 登入」</string>
+    <string name="qr_code_login_link_a_device_scan_qr_code_instruction_1">從登入畫面開始</string>
+    <string name="qr_code_login_new_device_instruction_3">選取「在此裝置上顯示 QR code」</string>
+    <string name="qr_code_login_new_device_instruction_2">到「設定」→「安全與隱私」→「顯示所有工作階段」</string>
+    <string name="qr_code_login_new_device_instruction_1">在您的其他裝置上開啟 ${app_name}</string>
+    <string name="qr_code_login_header_failed_denied_description">請求在另一台裝置上被拒絕。</string>
+    <string name="qr_code_login_header_failed_timeout_description">連結未在規定時間內完成。</string>
+    <string name="qr_code_login_header_failed_device_is_not_supported_description">不支援與其裝置連結。</string>
+    <string name="qr_code_login_header_failed_title">連線不成功</string>
+    <string name="qr_code_login_header_connected_description">請檢查您已登入的裝置,應該會顯示以下驗證碼。請確認以下驗證碼與該裝置相符:</string>
+    <string name="qr_code_login_header_connected_title">已建立安全連線</string>
+    <string name="qr_code_login_header_show_qr_code_link_a_device_description">使用您已登出的裝置掃描以下 QR code。</string>
+    <string name="qr_code_login_header_show_qr_code_new_device_description">使用您已登入的裝置來掃描下方的 QR code:</string>
+    <string name="qr_code_login_header_show_qr_code_title">使用 QR code 登入</string>
+    <string name="qr_code_login_header_scan_qr_code_description">使用此裝置的相機掃描您其他裝置上顯示的 QR code:</string>
+    <string name="qr_code_login_header_scan_qr_code_title">掃描 QR code</string>
+    <string name="three">3</string>
+    <string name="two">2</string>
+    <string name="one">1</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_description">您可以使用此裝置透過 QR code 登入移動裝置或網路裝置。有兩種方法可以作到:</string>
+    <string name="device_manager_sessions_sign_in_with_qr_code_title">使用 QR code 登入</string>
+    <string name="login_scan_qr_code">掃描 QR code</string>
 </resources>
\ No newline at end of file

From 4b63f1db82768deba93456af9a47d6852e959518 Mon Sep 17 00:00:00 2001
From: Yaron Shahrabani <sh.yaron@gmail.com>
Date: Tue, 18 Oct 2022 18:07:36 +0000
Subject: [PATCH 358/400] Translated using Weblate (Hebrew)

Currently translated at 84.7% (2121 of 2503 strings)

Translation: Element Android/Element Android App
Translate-URL: https://translate.element.io/projects/element-android/element-app/he/
---
 library/ui-strings/src/main/res/values-iw/strings.xml | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/library/ui-strings/src/main/res/values-iw/strings.xml b/library/ui-strings/src/main/res/values-iw/strings.xml
index ff19310c8e..b9f81ae446 100644
--- a/library/ui-strings/src/main/res/values-iw/strings.xml
+++ b/library/ui-strings/src/main/res/values-iw/strings.xml
@@ -861,9 +861,7 @@
     <string name="login_a11y_choose_other">בחר שרת בית מותאם אישית</string>
     <string name="login_a11y_choose_modular">בחר שירותי מטריקס אלמנט</string>
     <string name="login_a11y_choose_matrix_org">בחר matrix.org</string>
-    <string name="login_signup_cancel_confirmation_content">חשבונך טרם נוצר.
-\n
-\nלהפסיק את תהליך ההרשמה\?</string>
+    <string name="login_signup_cancel_confirmation_content">חשבונך טרם נוצר. להפסיק את תהליך ההרשמה\?</string>
     <string name="login_signup_cancel_confirmation_title">אזהרה</string>
     <string name="login_signup_error_user_in_use">שם המשתמש הזה תפוס</string>
     <string name="login_signup_submit">הבא</string>
@@ -2304,7 +2302,7 @@
     <string name="ftue_auth_use_case_option_three">קהילות</string>
     <string name="ftue_auth_use_case_option_two">צוותים</string>
     <string name="ftue_auth_use_case_option_one">חברים ומשפחה</string>
-    <string name="ftue_auth_use_case_subtitle">נעזור לך להתחבר.</string>
+    <string name="ftue_auth_use_case_subtitle">נעזור לך להתחבר</string>
     <string name="ftue_auth_use_case_title">עם מי תדברו הכי הרבה\?</string>
     <string name="ftue_auth_carousel_encrypted_body">מוצפן מקצה לקצה ואין צורך במספר טלפון. ללא פרסומות או עיבוד נתונים.</string>
     <string name="ftue_auth_carousel_control_body">בחר היכן השיחות שלך נשמרות, נותן לך שליטה ועצמאות. מחובר דרך Matrix.</string>
@@ -2508,4 +2506,4 @@
 \nזה יהיה מעבר חד פעמי שכן שרשורים הם כעת חלק ממפרט Matrix.</string>
     <string name="screen_sharing_notification_title">שיתוף מסך של ${app_name}</string>
     <string name="screen_sharing_notification_description">המסך משותף כרגע</string>
-</resources>
+</resources>
\ No newline at end of file

From 21c30c488c245bfc7441c127159369a7c4116cc8 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 19 Oct 2022 17:07:11 +0200
Subject: [PATCH 359/400] Towncrier

---
 CHANGES.md               | 61 ++++++++++++++++++++++++++++++++++++++++
 changelog.d/5968.bugfix  |  1 -
 changelog.d/7217.wip     |  1 -
 changelog.d/7257.wip     |  1 -
 changelog.d/7261.wip     |  1 -
 changelog.d/7273.wip     |  1 -
 changelog.d/7277.wip     |  1 -
 changelog.d/7281.wip     |  1 -
 changelog.d/7282.sdk     |  1 -
 changelog.d/7283.wip     |  1 -
 changelog.d/7285.misc    |  1 -
 changelog.d/7288.feature |  1 -
 changelog.d/7288.sdk     | 10 -------
 changelog.d/7294.wip     |  1 -
 changelog.d/7300.wip     |  1 -
 changelog.d/7310.bugfix  |  1 -
 changelog.d/7321.wip     |  1 -
 changelog.d/7324.wip     |  1 -
 changelog.d/7327.wip     |  1 -
 changelog.d/7335.misc    |  1 -
 changelog.d/7336.feature |  1 -
 changelog.d/7338.wip     |  1 -
 changelog.d/7344.feature |  1 -
 changelog.d/7354.misc    |  1 -
 changelog.d/7358.sdk     |  1 -
 changelog.d/7359.bugfix  |  1 -
 changelog.d/7359.sdk     |  1 -
 changelog.d/7363.wip     |  1 -
 changelog.d/7372.bugfix  |  1 -
 changelog.d/7374.feature |  1 -
 changelog.d/7384.misc    |  1 -
 changelog.d/7387.wip     |  1 -
 changelog.d/7393.wip     |  1 -
 changelog.d/7397.wip     |  1 -
 34 files changed, 61 insertions(+), 42 deletions(-)
 delete mode 100644 changelog.d/5968.bugfix
 delete mode 100644 changelog.d/7217.wip
 delete mode 100644 changelog.d/7257.wip
 delete mode 100644 changelog.d/7261.wip
 delete mode 100644 changelog.d/7273.wip
 delete mode 100644 changelog.d/7277.wip
 delete mode 100644 changelog.d/7281.wip
 delete mode 100644 changelog.d/7282.sdk
 delete mode 100644 changelog.d/7283.wip
 delete mode 100644 changelog.d/7285.misc
 delete mode 100644 changelog.d/7288.feature
 delete mode 100644 changelog.d/7288.sdk
 delete mode 100644 changelog.d/7294.wip
 delete mode 100644 changelog.d/7300.wip
 delete mode 100644 changelog.d/7310.bugfix
 delete mode 100644 changelog.d/7321.wip
 delete mode 100644 changelog.d/7324.wip
 delete mode 100644 changelog.d/7327.wip
 delete mode 100644 changelog.d/7335.misc
 delete mode 100644 changelog.d/7336.feature
 delete mode 100644 changelog.d/7338.wip
 delete mode 100644 changelog.d/7344.feature
 delete mode 100644 changelog.d/7354.misc
 delete mode 100644 changelog.d/7358.sdk
 delete mode 100644 changelog.d/7359.bugfix
 delete mode 100644 changelog.d/7359.sdk
 delete mode 100644 changelog.d/7363.wip
 delete mode 100644 changelog.d/7372.bugfix
 delete mode 100644 changelog.d/7374.feature
 delete mode 100644 changelog.d/7384.misc
 delete mode 100644 changelog.d/7387.wip
 delete mode 100644 changelog.d/7393.wip
 delete mode 100644 changelog.d/7397.wip

diff --git a/CHANGES.md b/CHANGES.md
index d1e4834988..21d58026e5 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,64 @@
+Changes in Element v1.5.4 (2022-10-19)
+======================================
+
+Features ✨
+----------
+ - Add WYSIWYG editor. ([#7288](https://github.com/vector-im/element-android/issues/7288))
+ - [Device management] Add lab flag for the feature ([#7336](https://github.com/vector-im/element-android/issues/7336))
+ - [Device management] Add lab flag for matrix client info account data event ([#7344](https://github.com/vector-im/element-android/issues/7344))
+ - [Device Management] Redirect to the new screen everywhere when lab flag is on ([#7374](https://github.com/vector-im/element-android/issues/7374))
+
+Bugfixes 🐛
+----------
+ - Fix wrong mic button direction to cancel on RTL languages ([#5968](https://github.com/vector-im/element-android/issues/5968))
+ - [Device Management] Long session names not handled well ([#7310](https://github.com/vector-im/element-android/issues/7310))
+ - Fix editing formatted messages with plain text editor ([#7359](https://github.com/vector-im/element-android/issues/7359))
+ - Handle properly when getUser returns null - prefer using getUserOrDefault ([#7372](https://github.com/vector-im/element-android/issues/7372))
+
+In development 🚧
+----------------
+ - Implements MSC3881: Parses `enabled` and `device_id` fields from updated Pusher API ([#7217](https://github.com/vector-im/element-android/issues/7217))
+ - [Device Management] Save "matrix_client_information" events on login/registration ([#7257](https://github.com/vector-im/element-android/issues/7257))
+ - Adds pusher toggle setting to device manager v2 ([#7261](https://github.com/vector-im/element-android/issues/7261))
+ - [Voice Broadcast] Add the "io.element.voice_broadcast_info" state event with a minimalist timeline widget ([#7273](https://github.com/vector-im/element-android/issues/7273))
+ - [Device Management] Show correct device type icons ([#7277](https://github.com/vector-im/element-android/issues/7277))
+ - Links "Enable Notifications for this session" setting to enabled value in pusher ([#7281](https://github.com/vector-im/element-android/issues/7281))
+ - [Voice Broadcast] Aggregate state events in the timeline ([#7283](https://github.com/vector-im/element-android/issues/7283))
+ - [Device Management] Render extended device info ([#7294](https://github.com/vector-im/element-android/issues/7294))
+ - Implements client-side of local notification settings event ([#7300](https://github.com/vector-im/element-android/issues/7300))
+ - [Device management] Improve the parsing for OS of Desktop/Web sessions ([#7321](https://github.com/vector-im/element-android/issues/7321))
+ - [Device management] Hide the IP address and last activity date on current session ([#7324](https://github.com/vector-im/element-android/issues/7324))
+ - [Device management] Update the unknown verification status icon ([#7327](https://github.com/vector-im/element-android/issues/7327))
+ - Implement QR Code Login UI ([#7338](https://github.com/vector-im/element-android/issues/7338))
+ - [Voice Broadcast] Record and send non aggregated voice messages to the room ([#7363](https://github.com/vector-im/element-android/issues/7363))
+ - [Voice Broadcast] Start listening to a voice broadcast ([#7387](https://github.com/vector-im/element-android/issues/7387))
+ - [Voice Broadcast] Enable the feature (behind a lab flag and only for Android 10 and up) ([#7393](https://github.com/vector-im/element-android/issues/7393))
+ - [Voice Broadcast] Add additional data in events ([#7397](https://github.com/vector-im/element-android/issues/7397))
+
+SDK API changes ⚠️
+------------------
+ - Stop using `original_event` field from `/relations` endpoint ([#7282](https://github.com/vector-im/element-android/issues/7282))
+ - Add `formattedText` or similar optional parameters in several methods:
+
+  * RelationService:
+  	* editTextMessage
+  	* editReply
+  	* replyToMessage
+  * SendService:
+  	* sendQuotedTextMessage
+
+  This allows us to send any HTML formatted text message without needing to rely on automatic Markdown > HTML translation. All these new parameters have a `null` value by default, so previous calls to these API methods remain compatible. ([#7288](https://github.com/vector-im/element-android/issues/7288))
+ - Add support for `m.login.token` auth during QR code based sign in ([#7358](https://github.com/vector-im/element-android/issues/7358))
+ - Allow getting the formatted or plain text body of a message for the fun `TimelineEvent.getTextEditableContent()`. ([#7359](https://github.com/vector-im/element-android/issues/7359))
+
+Other changes
+-------------
+ - Refactor TimelineFragment, split it into MessageComposerFragment and VoiceRecorderFragment. ([#7285](https://github.com/vector-im/element-android/issues/7285))
+ - Dependency to arrow has been removed. Please use `org.matrix.android.sdk.api.util.Optional` instead. ([#7335](https://github.com/vector-im/element-android/issues/7335))
+ - Update WYSIWYG editor designs. ([#7354](https://github.com/vector-im/element-android/issues/7354))
+ - Update WYSIWYG library to v0.2.1. ([#7384](https://github.com/vector-im/element-android/issues/7384))
+
+
 Changes in Element v1.5.2 (2022-10-05)
 ======================================
 
diff --git a/changelog.d/5968.bugfix b/changelog.d/5968.bugfix
deleted file mode 100644
index 05cf5cea60..0000000000
--- a/changelog.d/5968.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix wrong mic button direction to cancel on RTL languages
diff --git a/changelog.d/7217.wip b/changelog.d/7217.wip
deleted file mode 100644
index a8cc2a3ef3..0000000000
--- a/changelog.d/7217.wip
+++ /dev/null
@@ -1 +0,0 @@
-Implements MSC3881: Parses `enabled` and `device_id` fields from updated Pusher API
diff --git a/changelog.d/7257.wip b/changelog.d/7257.wip
deleted file mode 100644
index c6f9aefbd8..0000000000
--- a/changelog.d/7257.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Device Management] Save "matrix_client_information" events on login/registration
diff --git a/changelog.d/7261.wip b/changelog.d/7261.wip
deleted file mode 100644
index f7063fcc1b..0000000000
--- a/changelog.d/7261.wip
+++ /dev/null
@@ -1 +0,0 @@
-Adds pusher toggle setting to device manager v2
diff --git a/changelog.d/7273.wip b/changelog.d/7273.wip
deleted file mode 100644
index c480a79a43..0000000000
--- a/changelog.d/7273.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Voice Broadcast] Add the "io.element.voice_broadcast_info" state event with a minimalist timeline widget
diff --git a/changelog.d/7277.wip b/changelog.d/7277.wip
deleted file mode 100644
index 168d10b809..0000000000
--- a/changelog.d/7277.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Device Management] Show correct device type icons
diff --git a/changelog.d/7281.wip b/changelog.d/7281.wip
deleted file mode 100644
index c457ffbdb9..0000000000
--- a/changelog.d/7281.wip
+++ /dev/null
@@ -1 +0,0 @@
-Links "Enable Notifications for this session" setting to enabled value in pusher
diff --git a/changelog.d/7282.sdk b/changelog.d/7282.sdk
deleted file mode 100644
index 14b71045cf..0000000000
--- a/changelog.d/7282.sdk
+++ /dev/null
@@ -1 +0,0 @@
-Stop using `original_event` field from `/relations` endpoint
diff --git a/changelog.d/7283.wip b/changelog.d/7283.wip
deleted file mode 100644
index f7cbd323f1..0000000000
--- a/changelog.d/7283.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Voice Broadcast] Aggregate state events in the timeline
diff --git a/changelog.d/7285.misc b/changelog.d/7285.misc
deleted file mode 100644
index ce94383146..0000000000
--- a/changelog.d/7285.misc
+++ /dev/null
@@ -1 +0,0 @@
-Refactor TimelineFragment, split it into MessageComposerFragment and VoiceRecorderFragment.
diff --git a/changelog.d/7288.feature b/changelog.d/7288.feature
deleted file mode 100644
index be00e26179..0000000000
--- a/changelog.d/7288.feature
+++ /dev/null
@@ -1 +0,0 @@
-Add WYSIWYG editor.
diff --git a/changelog.d/7288.sdk b/changelog.d/7288.sdk
deleted file mode 100644
index 9c4a33ad22..0000000000
--- a/changelog.d/7288.sdk
+++ /dev/null
@@ -1,10 +0,0 @@
-Add `formattedText` or similar optional parameters in several methods:
-
-* RelationService:
-	* editTextMessage
-	* editReply
-	* replyToMessage
-* SendService:
-	* sendQuotedTextMessage
-
-This allows us to send any HTML formatted text message without needing to rely on automatic Markdown > HTML translation. All these new parameters have a `null` value by default, so previous calls to these API methods remain compatible.
diff --git a/changelog.d/7294.wip b/changelog.d/7294.wip
deleted file mode 100644
index f163f6b680..0000000000
--- a/changelog.d/7294.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Device Management] Render extended device info
diff --git a/changelog.d/7300.wip b/changelog.d/7300.wip
deleted file mode 100644
index 0a1777e651..0000000000
--- a/changelog.d/7300.wip
+++ /dev/null
@@ -1 +0,0 @@
-Implements client-side of local notification settings event
diff --git a/changelog.d/7310.bugfix b/changelog.d/7310.bugfix
deleted file mode 100644
index 3570b2d3ad..0000000000
--- a/changelog.d/7310.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-[Device Management] Long session names not handled well
diff --git a/changelog.d/7321.wip b/changelog.d/7321.wip
deleted file mode 100644
index 2a539503b7..0000000000
--- a/changelog.d/7321.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Device management] Improve the parsing for OS of Desktop/Web sessions
diff --git a/changelog.d/7324.wip b/changelog.d/7324.wip
deleted file mode 100644
index 6602ef3c85..0000000000
--- a/changelog.d/7324.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Device management] Hide the IP address and last activity date on current session
diff --git a/changelog.d/7327.wip b/changelog.d/7327.wip
deleted file mode 100644
index 8f0191f948..0000000000
--- a/changelog.d/7327.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Device management] Update the unknown verification status icon
diff --git a/changelog.d/7335.misc b/changelog.d/7335.misc
deleted file mode 100644
index 3b14aa1339..0000000000
--- a/changelog.d/7335.misc
+++ /dev/null
@@ -1 +0,0 @@
-Dependency to arrow has been removed. Please use `org.matrix.android.sdk.api.util.Optional` instead.
diff --git a/changelog.d/7336.feature b/changelog.d/7336.feature
deleted file mode 100644
index fb2d165b57..0000000000
--- a/changelog.d/7336.feature
+++ /dev/null
@@ -1 +0,0 @@
-[Device management] Add lab flag for the feature
diff --git a/changelog.d/7338.wip b/changelog.d/7338.wip
deleted file mode 100644
index fc47ecb2f9..0000000000
--- a/changelog.d/7338.wip
+++ /dev/null
@@ -1 +0,0 @@
-Implement QR Code Login UI
diff --git a/changelog.d/7344.feature b/changelog.d/7344.feature
deleted file mode 100644
index a6deb4a23a..0000000000
--- a/changelog.d/7344.feature
+++ /dev/null
@@ -1 +0,0 @@
-[Device management] Add lab flag for matrix client info account data event
diff --git a/changelog.d/7354.misc b/changelog.d/7354.misc
deleted file mode 100644
index 0e146a8e02..0000000000
--- a/changelog.d/7354.misc
+++ /dev/null
@@ -1 +0,0 @@
-Update WYSIWYG editor designs.
diff --git a/changelog.d/7358.sdk b/changelog.d/7358.sdk
deleted file mode 100644
index 3d17076a44..0000000000
--- a/changelog.d/7358.sdk
+++ /dev/null
@@ -1 +0,0 @@
-Add support for `m.login.token` auth during QR code based sign in
diff --git a/changelog.d/7359.bugfix b/changelog.d/7359.bugfix
deleted file mode 100644
index 98e29fb697..0000000000
--- a/changelog.d/7359.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Fix editing formatted messages with plain text editor
diff --git a/changelog.d/7359.sdk b/changelog.d/7359.sdk
deleted file mode 100644
index c78c591d67..0000000000
--- a/changelog.d/7359.sdk
+++ /dev/null
@@ -1 +0,0 @@
-Allow getting the formatted or plain text body of a message for the fun `TimelineEvent.getTextEditableContent()`.
diff --git a/changelog.d/7363.wip b/changelog.d/7363.wip
deleted file mode 100644
index ee71e799fa..0000000000
--- a/changelog.d/7363.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Voice Broadcast] Record and send non aggregated voice messages to the room
diff --git a/changelog.d/7372.bugfix b/changelog.d/7372.bugfix
deleted file mode 100644
index e63e00035b..0000000000
--- a/changelog.d/7372.bugfix
+++ /dev/null
@@ -1 +0,0 @@
-Handle properly when getUser returns null - prefer using getUserOrDefault
diff --git a/changelog.d/7374.feature b/changelog.d/7374.feature
deleted file mode 100644
index aa10696dca..0000000000
--- a/changelog.d/7374.feature
+++ /dev/null
@@ -1 +0,0 @@
-[Device Management] Redirect to the new screen everywhere when lab flag is on
diff --git a/changelog.d/7384.misc b/changelog.d/7384.misc
deleted file mode 100644
index 3994dc0fa1..0000000000
--- a/changelog.d/7384.misc
+++ /dev/null
@@ -1 +0,0 @@
-Update WYSIWYG library to v0.2.1.
diff --git a/changelog.d/7387.wip b/changelog.d/7387.wip
deleted file mode 100644
index 881608829d..0000000000
--- a/changelog.d/7387.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Voice Broadcast] Start listening to a voice broadcast
diff --git a/changelog.d/7393.wip b/changelog.d/7393.wip
deleted file mode 100644
index 7d82dc5769..0000000000
--- a/changelog.d/7393.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Voice Broadcast] Enable the feature (behind a lab flag and only for Android 10 and up)
diff --git a/changelog.d/7397.wip b/changelog.d/7397.wip
deleted file mode 100644
index 1a7d1866a6..0000000000
--- a/changelog.d/7397.wip
+++ /dev/null
@@ -1 +0,0 @@
-[Voice Broadcast] Add additional data in events

From e2a6a1942945612e22da7ffaae172566ee6bd6fc Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 19 Oct 2022 17:12:29 +0200
Subject: [PATCH 360/400] Rewrite the Changelog

---
 CHANGES.md | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 21d58026e5..7e2df7716b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -3,50 +3,50 @@ Changes in Element v1.5.4 (2022-10-19)
 
 Features ✨
 ----------
- - Add WYSIWYG editor. ([#7288](https://github.com/vector-im/element-android/issues/7288))
- - [Device management] Add lab flag for the feature ([#7336](https://github.com/vector-im/element-android/issues/7336))
- - [Device management] Add lab flag for matrix client info account data event ([#7344](https://github.com/vector-im/element-android/issues/7344))
- - [Device Management] Redirect to the new screen everywhere when lab flag is on ([#7374](https://github.com/vector-im/element-android/issues/7374))
+ - Add WYSIWYG editor, under a lab flag. ([#7288](https://github.com/vector-im/element-android/issues/7288))
+ - New Device management, can be enabled in the labs settings.
+ - Voice broadcast can be enabled in the labs settings (recording is possible only on Android 10 and up).
 
 Bugfixes 🐛
 ----------
  - Fix wrong mic button direction to cancel on RTL languages ([#5968](https://github.com/vector-im/element-android/issues/5968))
+ - Handle properly when getUser returns null - prefer using getUserOrDefault ([#7372](https://github.com/vector-im/element-android/issues/7372))
  - [Device Management] Long session names not handled well ([#7310](https://github.com/vector-im/element-android/issues/7310))
  - Fix editing formatted messages with plain text editor ([#7359](https://github.com/vector-im/element-android/issues/7359))
- - Handle properly when getUser returns null - prefer using getUserOrDefault ([#7372](https://github.com/vector-im/element-android/issues/7372))
 
 In development 🚧
 ----------------
- - Implements MSC3881: Parses `enabled` and `device_id` fields from updated Pusher API ([#7217](https://github.com/vector-im/element-android/issues/7217))
  - [Device Management] Save "matrix_client_information" events on login/registration ([#7257](https://github.com/vector-im/element-android/issues/7257))
- - Adds pusher toggle setting to device manager v2 ([#7261](https://github.com/vector-im/element-android/issues/7261))
- - [Voice Broadcast] Add the "io.element.voice_broadcast_info" state event with a minimalist timeline widget ([#7273](https://github.com/vector-im/element-android/issues/7273))
+ - [Device management] Add lab flag for the feature ([#7336](https://github.com/vector-im/element-android/issues/7336))
+ - [Device management] Add lab flag for matrix client info account data event ([#7344](https://github.com/vector-im/element-android/issues/7344))
+ - [Device Management] Redirect to the new screen everywhere when lab flag is on ([#7374](https://github.com/vector-im/element-android/issues/7374))
  - [Device Management] Show correct device type icons ([#7277](https://github.com/vector-im/element-android/issues/7277))
- - Links "Enable Notifications for this session" setting to enabled value in pusher ([#7281](https://github.com/vector-im/element-android/issues/7281))
- - [Voice Broadcast] Aggregate state events in the timeline ([#7283](https://github.com/vector-im/element-android/issues/7283))
  - [Device Management] Render extended device info ([#7294](https://github.com/vector-im/element-android/issues/7294))
- - Implements client-side of local notification settings event ([#7300](https://github.com/vector-im/element-android/issues/7300))
  - [Device management] Improve the parsing for OS of Desktop/Web sessions ([#7321](https://github.com/vector-im/element-android/issues/7321))
  - [Device management] Hide the IP address and last activity date on current session ([#7324](https://github.com/vector-im/element-android/issues/7324))
  - [Device management] Update the unknown verification status icon ([#7327](https://github.com/vector-im/element-android/issues/7327))
- - Implement QR Code Login UI ([#7338](https://github.com/vector-im/element-android/issues/7338))
+ - [Voice Broadcast] Add the "io.element.voice_broadcast_info" state event with a minimalist timeline widget ([#7273](https://github.com/vector-im/element-android/issues/7273))
+ - [Voice Broadcast] Aggregate state events in the timeline ([#7283](https://github.com/vector-im/element-android/issues/7283))
  - [Voice Broadcast] Record and send non aggregated voice messages to the room ([#7363](https://github.com/vector-im/element-android/issues/7363))
  - [Voice Broadcast] Start listening to a voice broadcast ([#7387](https://github.com/vector-im/element-android/issues/7387))
  - [Voice Broadcast] Enable the feature (behind a lab flag and only for Android 10 and up) ([#7393](https://github.com/vector-im/element-android/issues/7393))
  - [Voice Broadcast] Add additional data in events ([#7397](https://github.com/vector-im/element-android/issues/7397))
+ - Implements MSC3881: Parses `enabled` and `device_id` fields from updated Pusher API ([#7217](https://github.com/vector-im/element-android/issues/7217))
+ - Adds pusher toggle setting to device manager v2 ([#7261](https://github.com/vector-im/element-android/issues/7261))
+ - Implement QR Code Login UI ([#7338](https://github.com/vector-im/element-android/issues/7338))
+ - Implements client-side of local notification settings event ([#7300](https://github.com/vector-im/element-android/issues/7300))
+ - Links "Enable Notifications for this session" setting to enabled value in pusher ([#7281](https://github.com/vector-im/element-android/issues/7281))
 
 SDK API changes ⚠️
 ------------------
  - Stop using `original_event` field from `/relations` endpoint ([#7282](https://github.com/vector-im/element-android/issues/7282))
  - Add `formattedText` or similar optional parameters in several methods:
-
   * RelationService:
   	* editTextMessage
   	* editReply
   	* replyToMessage
   * SendService:
   	* sendQuotedTextMessage
-
   This allows us to send any HTML formatted text message without needing to rely on automatic Markdown > HTML translation. All these new parameters have a `null` value by default, so previous calls to these API methods remain compatible. ([#7288](https://github.com/vector-im/element-android/issues/7288))
  - Add support for `m.login.token` auth during QR code based sign in ([#7358](https://github.com/vector-im/element-android/issues/7358))
  - Allow getting the formatted or plain text body of a message for the fun `TimelineEvent.getTextEditableContent()`. ([#7359](https://github.com/vector-im/element-android/issues/7359))

From 6b2ae76b48d3f65ebe07b1d3337f740d756ec46f Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 19 Oct 2022 17:14:07 +0200
Subject: [PATCH 361/400] Update recipe.

---
 .github/ISSUE_TEMPLATE/release.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE/release.yml b/.github/ISSUE_TEMPLATE/release.yml
index b28dbbde69..b41188a920 100644
--- a/.github/ISSUE_TEMPLATE/release.yml
+++ b/.github/ISSUE_TEMPLATE/release.yml
@@ -20,7 +20,6 @@ body:
         - [ ] Check the update of the store descriptions (using Google Translate if necessary) to ensure that the changes are acceptable to be published to the stores.
         - [ ] While Weblate is locked, and after the PR from Weblate has been merged, handle all the TODOs in the main `strings.xml` file
         - [ ] Run the script `./tools/release/pushPlayStoreMetaData.sh`. You can check in the GooglePlay console the Activity log to check the effect.
-
         - [ ] Ensure all [the required PRs](https://github.com/vector-im/element-android/pulls?q=is%3Aopen+is%3Apr+label%3AZ-NextRelease) have been merged
 
         ### Do the release
@@ -32,7 +31,6 @@ body:
         - [ ] Run the integration test, and especially `UiAllScreensSanityTest.allScreensTest()`
         - [ ] Create an account on matrix.org and do some smoke tests that the sanity test does not cover like: 1-1 call, 1-1 video call, Jitsi call for instance
         - [ ] Run towncrier: `towncrier build --version v1.2.3 --draft` (remove `--draft` do write the file CHANGES.md)
-        - [ ] Check that the folder `changelog.d` is empty. It can happen that some remaining files stay here
         - [ ] Check the file CHANGES.md consistency. It's possible to reorder items (most important changes first) or change their section if relevant. Also an opportunity to fix some typo, or rewrite things
         - [ ] Add file for fastlane under ./fastlane/metadata/android/en-US/changelogs
         - [ ] (optional) Push the branch and start a draft PR (will not be merged), to check that the CI is happy with all the changes.

From 86f70991276cff971d0553432085f3ed79d70c8a Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 19 Oct 2022 17:15:35 +0200
Subject: [PATCH 362/400] fastlane

---
 fastlane/metadata/android/en-US/changelogs/40105040.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 fastlane/metadata/android/en-US/changelogs/40105040.txt

diff --git a/fastlane/metadata/android/en-US/changelogs/40105040.txt b/fastlane/metadata/android/en-US/changelogs/40105040.txt
new file mode 100644
index 0000000000..1073dc57e0
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/40105040.txt
@@ -0,0 +1,2 @@
+Main changes in this version: New features under the labs settings: Rich text composer, new device management, voice broadcast. Still under active development!
+Full changelog: https://github.com/vector-im/element-android/releases

From 36eb538a9348c5e6028954eb4cb7164f2639595d Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 19 Oct 2022 17:17:21 +0200
Subject: [PATCH 363/400] Version++

---
 matrix-sdk-android/build.gradle | 2 +-
 vector-app/build.gradle         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 4a6c0edf10..968d8515ac 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -62,7 +62,7 @@ android {
         // that the app's state is completely cleared between tests.
         testInstrumentationRunnerArguments clearPackageData: 'true'
 
-        buildConfigField "String", "SDK_VERSION", "\"1.5.4\""
+        buildConfigField "String", "SDK_VERSION", "\"1.5.6\""
 
         buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
         buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
diff --git a/vector-app/build.gradle b/vector-app/build.gradle
index ca77e4b86f..1e8a8b3d3f 100644
--- a/vector-app/build.gradle
+++ b/vector-app/build.gradle
@@ -37,7 +37,7 @@ ext.versionMinor = 5
 // Note: even values are reserved for regular release, odd values for hotfix release.
 // When creating a hotfix, you should decrease the value, since the current value
 // is the value for the next regular release.
-ext.versionPatch = 4
+ext.versionPatch = 6
 
 static def getGitTimestamp() {
     def cmd = 'git show -s --format=%ct'

From ad7a6bd76b2a6f6b9e7bf4e0228867b76aaa7462 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 Oct 2022 23:07:12 +0000
Subject: [PATCH 364/400] Bump material from 1.6.1 to 1.7.0

Bumps [material](https://github.com/material-components/material-components-android) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.6.1...1.7.0)

---
updated-dependencies:
- dependency-name: com.google.android.material:material
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index f081e0a874..0d7ada0587 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -82,7 +82,7 @@ ext.libs = [
                 'transition'              : "androidx.transition:transition:1.2.0",
         ],
         google      : [
-                'material'                : "com.google.android.material:material:1.6.1",
+                'material'                : "com.google.android.material:material:1.7.0",
                 'appdistributionApi'      : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution",
                 'appdistribution'         : "com.google.firebase:firebase-appdistribution:$appDistribution",
                 // Phone number https://github.com/google/libphonenumber

From 26c550921af72d553575a98f7fbe095b2b4d6f88 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 20 Oct 2022 08:27:20 +0200
Subject: [PATCH 365/400] Bump dependency-check-gradle from 7.2.1 to 7.3.0
 (#7415)

Bumps dependency-check-gradle from 7.2.1 to 7.3.0.

---
updated-dependencies:
- dependency-name: org.owasp:dependency-check-gradle
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index d0f093a451..f162685d7d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ buildscript {
         classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513'
         classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
         classpath "com.likethesalad.android:stem-plugin:2.2.3"
-        classpath 'org.owasp:dependency-check-gradle:7.2.1'
+        classpath 'org.owasp:dependency-check-gradle:7.3.0'
         classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20"
         classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
         classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'

From 6554f571f22a45212ee926c55b2da93d63716826 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Wed, 19 Oct 2022 11:18:35 +0200
Subject: [PATCH 366/400] VoiceBroadcastPlayer - Inject ActiveSessionHolder
 instead of Session

---
 .../app/features/voicebroadcast/VoiceBroadcastPlayer.kt     | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index dfd50ea5cb..e93e128686 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -18,6 +18,7 @@ package im.vector.app.features.voicebroadcast
 
 import android.media.AudioAttributes
 import android.media.MediaPlayer
+import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
 import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State
 import im.vector.app.features.voice.VoiceFailure
@@ -25,7 +26,6 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.events.model.RelationType
 import org.matrix.android.sdk.api.session.events.model.getRelationContent
 import org.matrix.android.sdk.api.session.getRoom
@@ -39,10 +39,12 @@ import javax.inject.Singleton
 
 @Singleton
 class VoiceBroadcastPlayer @Inject constructor(
-        private val session: Session,
+        private val sessionHolder: ActiveSessionHolder,
         private val playbackTracker: AudioMessagePlaybackTracker,
 ) {
 
+    private val session get() = sessionHolder.getActiveSession()
+
     private val mediaPlayerScope = CoroutineScope(Dispatchers.IO)
 
     private var currentMediaPlayer: MediaPlayer? = null

From b89ab6c2fd54cedfb76228d3189deafbecee705a Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Wed, 19 Oct 2022 13:04:03 +0200
Subject: [PATCH 367/400] VoiceBroadcastPlayer - release previous MediaPlayer

---
 .../voicebroadcast/VoiceBroadcastPlayer.kt         | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index e93e128686..c7259b12f8 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -76,10 +76,8 @@ class VoiceBroadcastPlayer @Inject constructor(
 
     fun stop() {
         currentMediaPlayer?.stop()
-        currentMediaPlayer?.release()
-        currentMediaPlayer?.setOnInfoListener(null)
-        currentMediaPlayer = null
         currentVoiceBroadcastEventId?.let { playbackTracker.stopPlayback(it) }
+        release(currentMediaPlayer)
         playlist = emptyList()
         currentPlayingIndex = -1
     }
@@ -147,11 +145,21 @@ class VoiceBroadcastPlayer @Inject constructor(
         }
     }
 
+    private fun release(mp: MediaPlayer?) {
+        mp?.apply {
+            release()
+            setOnInfoListener(null)
+            setOnCompletionListener(null)
+            setOnErrorListener(null)
+        }
+    }
+
     inner class MediaPlayerListener : MediaPlayer.OnInfoListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
 
         override fun onInfo(mp: MediaPlayer, what: Int, extra: Int): Boolean {
             when (what) {
                 MediaPlayer.MEDIA_INFO_STARTED_AS_NEXT -> {
+                    release(currentMediaPlayer)
                     currentMediaPlayer = mp
                     currentPlayingIndex++
                     mediaPlayerScope.launch { prepareNextFile() }

From 0c847cffc131f446cc94c3757883aee2ea870f21 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Wed, 19 Oct 2022 13:16:54 +0200
Subject: [PATCH 368/400] VoiceBroadcastPlayer - Use more accurate coroutine
 scope

---
 .../features/voicebroadcast/VoiceBroadcastPlayer.kt | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index c7259b12f8..db07503927 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -24,6 +24,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlayb
 import im.vector.app.features.voice.VoiceFailure
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.events.model.RelationType
@@ -42,13 +43,13 @@ class VoiceBroadcastPlayer @Inject constructor(
         private val sessionHolder: ActiveSessionHolder,
         private val playbackTracker: AudioMessagePlaybackTracker,
 ) {
-
-    private val session get() = sessionHolder.getActiveSession()
-
-    private val mediaPlayerScope = CoroutineScope(Dispatchers.IO)
+    private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+    private val session
+        get() = sessionHolder.getActiveSession()
 
     private var currentMediaPlayer: MediaPlayer? = null
     private var currentPlayingIndex: Int = -1
+
     private var playlist = emptyList<MessageAudioEvent>()
     private val currentVoiceBroadcastEventId
         get() = playlist.firstOrNull()?.root?.getRelationContent()?.eventId
@@ -90,7 +91,7 @@ class VoiceBroadcastPlayer @Inject constructor(
 
     private fun startPlayback() {
         val content = playlist.firstOrNull()?.content ?: run { Timber.w("## VoiceBroadcastPlayer: No content to play"); return }
-        mediaPlayerScope.launch {
+        coroutineScope.launch {
             try {
                 currentMediaPlayer = prepareMediaPlayer(content)
                 currentMediaPlayer?.start()
@@ -162,7 +163,7 @@ class VoiceBroadcastPlayer @Inject constructor(
                     release(currentMediaPlayer)
                     currentMediaPlayer = mp
                     currentPlayingIndex++
-                    mediaPlayerScope.launch { prepareNextFile() }
+                    coroutineScope.launch { prepareNextFile() }
                 }
             }
             return false

From fe44a829afdc23d35e0fc13a399affe77d19c7b6 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Wed, 19 Oct 2022 13:17:59 +0200
Subject: [PATCH 369/400] VoiceBroadcastPlayer - Improve
 currentVoiceBroadcastId

---
 .../voicebroadcast/VoiceBroadcastPlayer.kt         | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index db07503927..72ec181966 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -51,8 +51,8 @@ class VoiceBroadcastPlayer @Inject constructor(
     private var currentPlayingIndex: Int = -1
 
     private var playlist = emptyList<MessageAudioEvent>()
-    private val currentVoiceBroadcastEventId
-        get() = playlist.firstOrNull()?.root?.getRelationContent()?.eventId
+    private val currentVoiceBroadcastId
+        get() = playlist.getOrNull(currentPlayingIndex)?.root?.getRelationContent()?.eventId
 
     private val mediaPlayerListener = MediaPlayerListener()
 
@@ -60,7 +60,7 @@ class VoiceBroadcastPlayer @Inject constructor(
         val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
 
         when {
-            currentVoiceBroadcastEventId != eventId -> {
+            currentVoiceBroadcastId != eventId -> {
                 stop()
                 updatePlaylist(room, eventId)
                 startPlayback()
@@ -72,12 +72,12 @@ class VoiceBroadcastPlayer @Inject constructor(
 
     fun pause() {
         currentMediaPlayer?.pause()
-        currentVoiceBroadcastEventId?.let { playbackTracker.pausePlayback(it) }
+        currentVoiceBroadcastId?.let { playbackTracker.pausePlayback(it) }
     }
 
     fun stop() {
         currentMediaPlayer?.stop()
-        currentVoiceBroadcastEventId?.let { playbackTracker.stopPlayback(it) }
+        currentVoiceBroadcastId?.let { playbackTracker.stopPlayback(it) }
         release(currentMediaPlayer)
         playlist = emptyList()
         currentPlayingIndex = -1
@@ -96,7 +96,7 @@ class VoiceBroadcastPlayer @Inject constructor(
                 currentMediaPlayer = prepareMediaPlayer(content)
                 currentMediaPlayer?.start()
                 currentPlayingIndex = 0
-                currentVoiceBroadcastEventId?.let { playbackTracker.startPlayback(it) }
+                currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) }
                 prepareNextFile()
             } catch (failure: Throwable) {
                 Timber.e(failure, "Unable to start playback")
@@ -107,7 +107,7 @@ class VoiceBroadcastPlayer @Inject constructor(
 
     private fun resumePlayback() {
         currentMediaPlayer?.start()
-        currentVoiceBroadcastEventId?.let { playbackTracker.startPlayback(it) }
+        currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) }
     }
 
     private suspend fun prepareNextFile() {

From e9c81ca98fea45ced23afa32185d3a29762ded4a Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Wed, 19 Oct 2022 18:07:44 +0200
Subject: [PATCH 370/400] VoiceBroadcastPlayer - Live listening

---
 .../sdk/api/session/events/model/Event.kt     |   2 +-
 .../room/timeline/TimelineEventDataSource.kt  |  10 +-
 .../voicebroadcast/VoiceBroadcastHelper.kt    |   2 +-
 .../voicebroadcast/VoiceBroadcastPlayer.kt    | 236 +++++++++++++++---
 .../usecase/GetVoiceBroadcastStateUseCase.kt  |  41 +++
 5 files changed, 249 insertions(+), 42 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt

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 71daf4cc4f..1f16041b54 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
@@ -401,7 +401,7 @@ fun Event.getRelationContent(): RelationDefaultContent? {
             when (getClearType()) {
                 EventType.STICKER -> getClearContent().toModel<MessageStickerContent>()?.relatesTo
                 in EventType.BEACON_LOCATION_DATA -> getClearContent().toModel<MessageBeaconLocationDataContent>()?.relatesTo
-                else -> null
+                else -> getClearContent()?.get("m.relates_to")?.toContent().toModel()
             }
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt
index 20094e4be8..2d6082f9b5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt
@@ -22,6 +22,8 @@ import io.realm.Sort
 import org.matrix.android.sdk.api.session.events.model.getRelationContent
 import org.matrix.android.sdk.api.session.events.model.isImageMessage
 import org.matrix.android.sdk.api.session.events.model.isVideoMessage
+import org.matrix.android.sdk.api.session.events.model.toModel
+import org.matrix.android.sdk.api.session.room.model.message.MessageContent
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.util.Optional
 import org.matrix.android.sdk.internal.database.RealmSessionProvider
@@ -74,7 +76,13 @@ internal class TimelineEventDataSource @Inject constructor(
                     .distinct(TimelineEventEntityFields.EVENT_ID)
                     .findAll()
                     .mapNotNull {
-                        timelineEventMapper.map(it).takeIf { it.root.getRelationContent()?.takeIf { it.type == eventType && it.eventId == eventId } != null }
+                        timelineEventMapper.map(it)
+                                .takeIf {
+                                    val isEventRelatedTo = it.root.getRelationContent()?.takeIf { it.type == eventType && it.eventId == eventId } != null
+                                    val isContentRelatedTo = it.root.getClearContent()?.toModel<MessageContent>()
+                                            ?.relatesTo?.takeIf { it.type == eventType && it.eventId == eventId } != null
+                                    isEventRelatedTo || isContentRelatedTo
+                                }
                     }
         }
     }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt
index b967afa9cb..58e7de7f32 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt
@@ -40,7 +40,7 @@ class VoiceBroadcastHelper @Inject constructor(
 
     suspend fun stopVoiceBroadcast(roomId: String) = stopVoiceBroadcastUseCase.execute(roomId)
 
-    fun playOrResumePlayback(roomId: String, eventId: String) = voiceBroadcastPlayer.play(roomId, eventId)
+    fun playOrResumePlayback(roomId: String, eventId: String) = voiceBroadcastPlayer.playOrResume(roomId, eventId)
 
     fun pausePlayback() = voiceBroadcastPlayer.pause()
 
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 72ec181966..7f5e13504e 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -20,13 +20,19 @@ import android.media.AudioAttributes
 import android.media.MediaPlayer
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
-import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State
 import im.vector.app.features.voice.VoiceFailure
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
+import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
+import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastStateUseCase
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.events.model.RelationType
 import org.matrix.android.sdk.api.session.events.model.getRelationContent
 import org.matrix.android.sdk.api.session.getRoom
@@ -34,6 +40,11 @@ import org.matrix.android.sdk.api.session.room.Room
 import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
 import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
+import org.matrix.android.sdk.api.session.room.timeline.Timeline
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
+import org.matrix.android.sdk.flow.flow
+import org.matrix.android.sdk.flow.unwrap
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -42,62 +53,117 @@ import javax.inject.Singleton
 class VoiceBroadcastPlayer @Inject constructor(
         private val sessionHolder: ActiveSessionHolder,
         private val playbackTracker: AudioMessagePlaybackTracker,
+        private val getVoiceBroadcastStateUseCase: GetVoiceBroadcastStateUseCase,
 ) {
-    private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
     private val session
         get() = sessionHolder.getActiveSession()
 
+    private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
+    private var voiceBroadcastStateJob: Job? = null
+    private var currentTimeline: Timeline? = null
+        set(value) {
+            field?.removeAllListeners()
+            field?.dispose()
+            field = value
+        }
+
+    private val mediaPlayerListener = MediaPlayerListener()
+    private var timelineListener: TimelineListener? = null
+
     private var currentMediaPlayer: MediaPlayer? = null
-    private var currentPlayingIndex: Int = -1
+    private var nextMediaPlayer: MediaPlayer? = null
+        set(value) {
+            field = value
+            currentMediaPlayer?.setNextMediaPlayer(value)
+        }
+    private var currentSequence: Int? = null
 
     private var playlist = emptyList<MessageAudioEvent>()
     private val currentVoiceBroadcastId
-        get() = playlist.getOrNull(currentPlayingIndex)?.root?.getRelationContent()?.eventId
+        get() = playlist.firstOrNull()?.root?.getRelationContent()?.eventId
 
-    private val mediaPlayerListener = MediaPlayerListener()
-
-    fun play(roomId: String, eventId: String) {
-        val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
+    private var state: State = State.IDLE
+        set(value) {
+            Timber.w("## VoiceBroadcastPlayer state: $field -> $value")
+            field = value
+        }
 
+    fun playOrResume(roomId: String, eventId: String) {
+        val hasChanged = currentVoiceBroadcastId != eventId
         when {
-            currentVoiceBroadcastId != eventId -> {
-                stop()
-                updatePlaylist(room, eventId)
-                startPlayback()
-            }
-            playbackTracker.getPlaybackState(eventId) is State.Playing -> pause()
-            else -> resumePlayback()
+            hasChanged -> startPlayback(roomId, eventId)
+            state == State.PAUSED -> resumePlayback()
+            else -> Unit
         }
     }
 
     fun pause() {
         currentMediaPlayer?.pause()
         currentVoiceBroadcastId?.let { playbackTracker.pausePlayback(it) }
+        state = State.PAUSED
     }
 
     fun stop() {
+        // Stop playback
         currentMediaPlayer?.stop()
         currentVoiceBroadcastId?.let { playbackTracker.stopPlayback(it) }
+
+        // Release current player
         release(currentMediaPlayer)
+        currentMediaPlayer = null
+
+        // Release next player
+        release(nextMediaPlayer)
+        nextMediaPlayer = null
+
+        // Do not observe anymore voice broadcast state changes
+        voiceBroadcastStateJob?.cancel()
+        voiceBroadcastStateJob = null
+
+        // In case of live broadcast, stop observing new chunks
+        currentTimeline?.dispose()
+        currentTimeline?.removeAllListeners()
+        currentTimeline = null
+        timelineListener = null
+
+        // Update state
+        state = State.IDLE
+
+        // Clear playlist
         playlist = emptyList()
-        currentPlayingIndex = -1
+        currentSequence = null
     }
 
-    private fun updatePlaylist(room: Room, eventId: String) {
-        val timelineEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId)
-        val audioEvents = timelineEvents.mapNotNull { it.root.asMessageAudioEvent() }
-        playlist = audioEvents.sortedBy { it.getVoiceBroadcastChunk()?.sequence?.toLong() ?: it.root.originServerTs }
+    private fun startPlayback(roomId: String, eventId: String) {
+        val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
+
+        // Stop listening previous voice broadcast if any
+        if (state != State.IDLE) stop()
+
+        state = State.BUFFERING
+
+        val voiceBroadcastState = getVoiceBroadcastStateUseCase.execute(roomId, eventId)
+        if (voiceBroadcastState == VoiceBroadcastState.STOPPED) {
+            // Get static playlist
+            updatePlaylist(getExistingChunks(room, eventId))
+            startPlayback(false)
+        } else {
+            playLiveVoiceBroadcast(room, eventId)
+        }
     }
 
-    private fun startPlayback() {
-        val content = playlist.firstOrNull()?.content ?: run { Timber.w("## VoiceBroadcastPlayer: No content to play"); return }
+    private fun startPlayback(isLive: Boolean) {
+        val event = if (isLive) playlist.lastOrNull() else playlist.firstOrNull()
+        val content = event?.content ?: run { Timber.w("## VoiceBroadcastPlayer: No content to play"); return }
+        val sequence = event.getVoiceBroadcastChunk()?.sequence
         coroutineScope.launch {
             try {
                 currentMediaPlayer = prepareMediaPlayer(content)
                 currentMediaPlayer?.start()
-                currentPlayingIndex = 0
                 currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) }
-                prepareNextFile()
+                currentSequence = sequence
+                state = State.PLAYING
+                nextMediaPlayer = prepareNextMediaPlayer()
             } catch (failure: Throwable) {
                 Timber.e(failure, "Unable to start playback")
                 throw VoiceFailure.UnableToPlay(failure)
@@ -105,19 +171,68 @@ class VoiceBroadcastPlayer @Inject constructor(
         }
     }
 
+    private fun playLiveVoiceBroadcast(room: Room, eventId: String) {
+        val voiceBroadcastEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent()
+                ?: error("Cannot retrieve voice broadcast $eventId")
+        updatePlaylist(getExistingChunks(room, eventId))
+        startPlayback(true)
+        room.flow()
+                .liveStateEvent(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO, QueryStringValue.Equals(voiceBroadcastEvent.root.stateKey!!))
+                .unwrap()
+                .mapNotNull { it.asVoiceBroadcastEvent()?.content?.voiceBroadcastState }
+                .onEach { state ->
+                    when (state) {
+                        VoiceBroadcastState.STARTED,
+                        VoiceBroadcastState.PAUSED,
+                        VoiceBroadcastState.RESUMED -> {
+                            observeIncomingChunks(room, eventId)
+                        }
+                        VoiceBroadcastState.STOPPED -> {
+                            currentTimeline?.dispose()
+                            currentTimeline?.removeAllListeners()
+                            currentTimeline = null
+                        }
+                    }
+                }
+                .launchIn(coroutineScope)
+    }
+
+    private fun getExistingChunks(room: Room, eventId: String): List<MessageAudioEvent> {
+        return room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId)
+                .mapNotNull { it.root.asMessageAudioEvent() }
+                .filter { it.isVoiceBroadcast() }
+    }
+
+    private fun observeIncomingChunks(room: Room, eventId: String) {
+        // Fixme this is probably not necessary here
+        currentTimeline?.dispose()
+        currentTimeline?.removeAllListeners()
+        currentTimeline = room.timelineService().createTimeline(null, TimelineSettings(5)).also { timeline ->
+            timelineListener = TimelineListener(eventId).also { timeline.addListener(it) }
+            timeline.start()
+        }
+    }
+
     private fun resumePlayback() {
         currentMediaPlayer?.start()
         currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) }
+        state = State.PLAYING
     }
 
-    private suspend fun prepareNextFile() {
-        val nextContent = playlist.getOrNull(currentPlayingIndex + 1)?.content
-        if (nextContent == null) {
-            currentMediaPlayer?.setOnCompletionListener(mediaPlayerListener)
-        } else {
-            val nextMediaPlayer = prepareMediaPlayer(nextContent)
-            currentMediaPlayer?.setNextMediaPlayer(nextMediaPlayer)
-        }
+    private fun updatePlaylist(playlist: List<MessageAudioEvent>) {
+        this.playlist = playlist.sortedBy { it.getVoiceBroadcastChunk()?.sequence?.toLong() ?: it.root.originServerTs }
+    }
+
+    private fun getNextAudioContent(): MessageAudioContent? {
+        val nextSequence = currentSequence?.plus(1)
+                ?: timelineListener?.let { playlist.lastOrNull()?.sequence }
+                ?: 1
+        return playlist.find { it.getVoiceBroadcastChunk()?.sequence == nextSequence }?.content
+    }
+
+    private suspend fun prepareNextMediaPlayer(): MediaPlayer? {
+        val nextContent = getNextAudioContent() ?: return null
+        return prepareMediaPlayer(nextContent)
     }
 
     private suspend fun prepareMediaPlayer(messageAudioContent: MessageAudioContent): MediaPlayer {
@@ -141,6 +256,7 @@ class VoiceBroadcastPlayer @Inject constructor(
                 setDataSource(fis.fd)
                 setOnInfoListener(mediaPlayerListener)
                 setOnErrorListener(mediaPlayerListener)
+                setOnCompletionListener(mediaPlayerListener)
                 prepare()
             }
         }
@@ -155,24 +271,59 @@ class VoiceBroadcastPlayer @Inject constructor(
         }
     }
 
-    inner class MediaPlayerListener : MediaPlayer.OnInfoListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
+    private inner class TimelineListener(private val voiceBroadcastId: String) : Timeline.Listener {
+        override fun onTimelineUpdated(snapshot: List<TimelineEvent>) {
+            val currentSequences = playlist.map { it.sequence }
+            val newChunks = snapshot
+                    .mapNotNull { timelineEvent ->
+                        timelineEvent.root.asMessageAudioEvent()
+                                ?.takeIf { it.isVoiceBroadcast() && it.getVoiceBroadcastEventId() == voiceBroadcastId && it.sequence !in currentSequences }
+                    }
+            if (newChunks.isEmpty()) return
+            updatePlaylist(playlist + newChunks)
+
+            when (state) {
+                State.PLAYING -> {
+                    if (nextMediaPlayer == null) {
+                        coroutineScope.launch { nextMediaPlayer = prepareNextMediaPlayer() }
+                    }
+                }
+                State.PAUSED -> {
+                    if (nextMediaPlayer == null) {
+                        coroutineScope.launch { nextMediaPlayer = prepareNextMediaPlayer() }
+                    }
+                }
+                State.BUFFERING -> {
+                    val newMediaContent = getNextAudioContent()
+                    if (newMediaContent != null) startPlayback(true)
+                }
+                State.IDLE -> startPlayback(true)
+            }
+        }
+    }
+
+    private inner class MediaPlayerListener : MediaPlayer.OnInfoListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
 
         override fun onInfo(mp: MediaPlayer, what: Int, extra: Int): Boolean {
             when (what) {
                 MediaPlayer.MEDIA_INFO_STARTED_AS_NEXT -> {
                     release(currentMediaPlayer)
                     currentMediaPlayer = mp
-                    currentPlayingIndex++
-                    coroutineScope.launch { prepareNextFile() }
+                    currentSequence = currentSequence?.plus(1)
+                    coroutineScope.launch { nextMediaPlayer = prepareNextMediaPlayer() }
                 }
             }
             return false
         }
 
         override fun onCompletion(mp: MediaPlayer) {
-            // Verify that a new media has not been set in the mean time
-            if (!currentMediaPlayer?.isPlaying.orFalse()) {
-                stop()
+            when {
+                timelineListener == null && nextMediaPlayer == null -> {
+                    stop()
+                }
+                nextMediaPlayer == null -> {
+                    state = State.BUFFERING
+                }
             }
         }
 
@@ -181,4 +332,11 @@ class VoiceBroadcastPlayer @Inject constructor(
             return true
         }
     }
+
+    enum class State {
+        PLAYING,
+        PAUSED,
+        BUFFERING,
+        IDLE
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt
new file mode 100644
index 0000000000..5b3153ea40
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.voicebroadcast.usecase
+
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
+import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.events.model.RelationType
+import org.matrix.android.sdk.api.session.getRoom
+import timber.log.Timber
+import javax.inject.Inject
+
+class GetVoiceBroadcastStateUseCase @Inject constructor(
+        private val session: Session,
+) {
+
+    fun execute(roomId: String, eventId: String): VoiceBroadcastState? {
+        val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
+
+        Timber.d("## GetVoiceBroadcastStateUseCase: get voice broadcast state requested for $eventId")
+
+        val initialEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() // Fallback to initial event
+        val relatedEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId).sortedBy { it.root.originServerTs }
+        val lastVoiceBroadcastEvent = relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent
+        return lastVoiceBroadcastEvent?.content?.voiceBroadcastState
+    }
+}

From f05f0a85b01ade9525b9f8abbc2e4616983e17fa Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 02:22:59 +0200
Subject: [PATCH 371/400] VoiceBroadcastRecorder - Improve recorder by sending
 chunk when pausing

---
 .../voicebroadcast/VoiceBroadcastRecorderQ.kt      | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
index 404b112574..21d12ee986 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -21,6 +21,7 @@ import android.media.MediaRecorder
 import android.os.Build
 import androidx.annotation.RequiresApi
 import im.vector.app.features.voice.AbstractVoiceRecorderQ
+import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
 
 @RequiresApi(Build.VERSION_CODES.Q)
@@ -30,6 +31,7 @@ class VoiceBroadcastRecorderQ(
 
     private var maxFileSize = 0L // zero or negative for no limit
     private var currentSequence = 0
+    private var currentRoomId: String? = null
 
     override var listener: VoiceBroadcastRecorder.Listener? = null
 
@@ -51,11 +53,23 @@ class VoiceBroadcastRecorderQ(
     }
 
     override fun startRecord(roomId: String, chunkLength: Int) {
+        currentRoomId = roomId
         maxFileSize = (chunkLength * audioEncodingBitRate / 8).toLong()
         currentSequence = 1
         startRecord(roomId)
     }
 
+    override fun pauseRecord() {
+        tryOrNull { mediaRecorder?.stop() }
+        mediaRecorder?.reset()
+        notifyOutputFileCreated()
+    }
+
+    override fun resumeRecord() {
+        currentSequence++
+        currentRoomId?.let { startRecord(it) }
+    }
+
     override fun stopRecord() {
         super.stopRecord()
         notifyOutputFileCreated()

From 6d6b4e52087c4c28c4beeedba00be437d6e82da6 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 11:24:45 +0200
Subject: [PATCH 372/400] VoiceBroadcast - Ignore voice broadcast info with
 empty content (eg. redacted)

---
 .../voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index 7cb66cd9e5..d5d58f822e 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -55,7 +55,7 @@ class StartVoiceBroadcastUseCase @Inject constructor(
                 QueryStringValue.IsNotEmpty
         )
                 .mapNotNull { it.asVoiceBroadcastEvent() }
-                .filter { it.content?.voiceBroadcastState != VoiceBroadcastState.STOPPED }
+                .filter { it.content?.voiceBroadcastState != null && it.content?.voiceBroadcastState != VoiceBroadcastState.STOPPED }
 
         if (onGoingVoiceBroadcastEvents.isEmpty()) {
             startVoiceBroadcast(room)

From ed0d255495c7c6a04bb4e8d2b7f26bc28f415268 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Thu, 20 Oct 2022 14:03:42 +0200
Subject: [PATCH 373/400] Quick improvement on the doc.

---
 .../matrix/android/sdk/api/session/room/timeline/Timeline.kt    | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt
index 1824d5dc6c..9ac33c0545 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt
@@ -106,6 +106,8 @@ interface Timeline {
 
         /**
          * Called when new events come through the sync.
+         * Note that the corresponding events may not be available yet in the database.
+         * [onTimelineUpdated] will be called with the event content.
          */
         fun onNewTimelineEvents(eventIds: List<String>) = Unit
 

From 94390697ae045e620452f6e99331c9dabf8c3e81 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 13:18:46 +0200
Subject: [PATCH 374/400] VoiceBroadcastPlayer - Filter live broadcast state
 listening on the referenced eventId

---
 .../app/features/voicebroadcast/VoiceBroadcastPlayer.kt     | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 7f5e13504e..c55cb8a1d0 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -179,7 +179,11 @@ class VoiceBroadcastPlayer @Inject constructor(
         room.flow()
                 .liveStateEvent(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO, QueryStringValue.Equals(voiceBroadcastEvent.root.stateKey!!))
                 .unwrap()
-                .mapNotNull { it.asVoiceBroadcastEvent()?.content?.voiceBroadcastState }
+                .mapNotNull { event ->
+                    event.asVoiceBroadcastEvent()
+                            ?.takeIf { it.reference?.eventId == eventId }
+                            ?.content?.voiceBroadcastState
+                }
                 .onEach { state ->
                     when (state) {
                         VoiceBroadcastState.STARTED,

From 99a2afa5ee1378050f9b2d334d72166a71a33e54 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 13:32:16 +0200
Subject: [PATCH 375/400] Add changelog

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

diff --git a/changelog.d/7419.wip b/changelog.d/7419.wip
new file mode 100644
index 0000000000..06f69dfa7f
--- /dev/null
+++ b/changelog.d/7419.wip
@@ -0,0 +1 @@
+[Voice Broadcast] Live listening support

From bafa2f8bde785d85a82df37c4715e7a06f2b261f Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 13:43:50 +0200
Subject: [PATCH 376/400] VoiceBroadcastRecorder - Send last sequence number on
 pause and stop

---
 .../app/features/voicebroadcast/VoiceBroadcastRecorder.kt       | 1 +
 .../app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt      | 2 +-
 .../voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt    | 2 ++
 .../voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt        | 1 +
 .../voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt         | 1 +
 5 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
index 37ff920c57..c9bb0c5f54 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
@@ -23,6 +23,7 @@ import java.io.File
 interface VoiceBroadcastRecorder : VoiceRecorder {
 
     var listener: Listener?
+    var currentSequence: Int
 
     fun startRecord(roomId: String, chunkLength: Int)
 
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
index 21d12ee986..a65aae6f8a 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -30,8 +30,8 @@ class VoiceBroadcastRecorderQ(
 ) : AbstractVoiceRecorderQ(context), VoiceBroadcastRecorder {
 
     private var maxFileSize = 0L // zero or negative for no limit
-    private var currentSequence = 0
     private var currentRoomId: String? = null
+    override var currentSequence = 0
 
     override var listener: VoiceBroadcastRecorder.Listener? = null
 
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
index a9db63c538..d882d4049e 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt
@@ -44,6 +44,8 @@ data class MessageVoiceBroadcastInfoContent(
         @Json(name = "state") val voiceBroadcastStateStr: String = "",
         /** The length of the voice chunks in seconds. **/
         @Json(name = "chunk_length") val chunkLength: Int? = null,
+        /** The sequence of the last sent chunk. **/
+        @Json(name = "last_chunk_sequence") val lastChunkSequence: Int? = null,
 ) : MessageContent {
 
     val voiceBroadcastState: VoiceBroadcastState? = VoiceBroadcastState.values()
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
index 835a57c102..1430dd8c86 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt
@@ -59,6 +59,7 @@ class PauseVoiceBroadcastUseCase @Inject constructor(
                 body = MessageVoiceBroadcastInfoContent(
                         relatesTo = reference,
                         voiceBroadcastStateStr = VoiceBroadcastState.PAUSED.value,
+                        lastChunkSequence = voiceBroadcastRecorder?.currentSequence,
                 ).toContent(),
         )
 
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
index 6eefa06979..bc6a3e7be6 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt
@@ -60,6 +60,7 @@ class StopVoiceBroadcastUseCase @Inject constructor(
                 body = MessageVoiceBroadcastInfoContent(
                         relatesTo = reference,
                         voiceBroadcastStateStr = VoiceBroadcastState.STOPPED.value,
+                        lastChunkSequence = voiceBroadcastRecorder?.currentSequence,
                 ).toContent(),
         )
 

From 05eeef9dfec7b89218f4abb430435561b092b607 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 14:36:47 +0200
Subject: [PATCH 377/400] VoiceBroadcastListener - Handle end of live listening

---
 .../voicebroadcast/VoiceBroadcastPlayer.kt    | 65 ++++++-------------
 ...UseCase.kt => GetVoiceBroadcastUseCase.kt} | 11 ++--
 2 files changed, 25 insertions(+), 51 deletions(-)
 rename vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/{GetVoiceBroadcastStateUseCase.kt => GetVoiceBroadcastUseCase.kt} (80%)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index c55cb8a1d0..403675a9ee 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -23,16 +23,12 @@ import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlayb
 import im.vector.app.features.voice.VoiceFailure
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
-import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastStateUseCase
+import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastUseCase
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.mapNotNull
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
-import org.matrix.android.sdk.api.query.QueryStringValue
 import org.matrix.android.sdk.api.session.events.model.RelationType
 import org.matrix.android.sdk.api.session.events.model.getRelationContent
 import org.matrix.android.sdk.api.session.getRoom
@@ -43,8 +39,6 @@ import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent
 import org.matrix.android.sdk.api.session.room.timeline.Timeline
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
-import org.matrix.android.sdk.flow.flow
-import org.matrix.android.sdk.flow.unwrap
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -53,7 +47,7 @@ import javax.inject.Singleton
 class VoiceBroadcastPlayer @Inject constructor(
         private val sessionHolder: ActiveSessionHolder,
         private val playbackTracker: AudioMessagePlaybackTracker,
-        private val getVoiceBroadcastStateUseCase: GetVoiceBroadcastStateUseCase,
+        private val getVoiceBroadcastUseCase: GetVoiceBroadcastUseCase,
 ) {
     private val session
         get() = sessionHolder.getActiveSession()
@@ -87,6 +81,7 @@ class VoiceBroadcastPlayer @Inject constructor(
             Timber.w("## VoiceBroadcastPlayer state: $field -> $value")
             field = value
         }
+    private var currentRoomId: String? = null
 
     fun playOrResume(roomId: String, eventId: String) {
         val hasChanged = currentVoiceBroadcastId != eventId
@@ -132,17 +127,19 @@ class VoiceBroadcastPlayer @Inject constructor(
         // Clear playlist
         playlist = emptyList()
         currentSequence = null
+        currentRoomId = null
     }
 
     private fun startPlayback(roomId: String, eventId: String) {
         val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
+        currentRoomId = roomId
 
         // Stop listening previous voice broadcast if any
         if (state != State.IDLE) stop()
 
         state = State.BUFFERING
 
-        val voiceBroadcastState = getVoiceBroadcastStateUseCase.execute(roomId, eventId)
+        val voiceBroadcastState = getVoiceBroadcastUseCase.execute(roomId, eventId)?.content?.voiceBroadcastState
         if (voiceBroadcastState == VoiceBroadcastState.STOPPED) {
             // Get static playlist
             updatePlaylist(getExistingChunks(room, eventId))
@@ -172,33 +169,10 @@ class VoiceBroadcastPlayer @Inject constructor(
     }
 
     private fun playLiveVoiceBroadcast(room: Room, eventId: String) {
-        val voiceBroadcastEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent()
-                ?: error("Cannot retrieve voice broadcast $eventId")
+        room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() ?: error("Cannot retrieve voice broadcast $eventId")
         updatePlaylist(getExistingChunks(room, eventId))
         startPlayback(true)
-        room.flow()
-                .liveStateEvent(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO, QueryStringValue.Equals(voiceBroadcastEvent.root.stateKey!!))
-                .unwrap()
-                .mapNotNull { event ->
-                    event.asVoiceBroadcastEvent()
-                            ?.takeIf { it.reference?.eventId == eventId }
-                            ?.content?.voiceBroadcastState
-                }
-                .onEach { state ->
-                    when (state) {
-                        VoiceBroadcastState.STARTED,
-                        VoiceBroadcastState.PAUSED,
-                        VoiceBroadcastState.RESUMED -> {
-                            observeIncomingChunks(room, eventId)
-                        }
-                        VoiceBroadcastState.STOPPED -> {
-                            currentTimeline?.dispose()
-                            currentTimeline?.removeAllListeners()
-                            currentTimeline = null
-                        }
-                    }
-                }
-                .launchIn(coroutineScope)
+        observeIncomingEvents(room, eventId)
     }
 
     private fun getExistingChunks(room: Room, eventId: String): List<MessageAudioEvent> {
@@ -207,10 +181,7 @@ class VoiceBroadcastPlayer @Inject constructor(
                 .filter { it.isVoiceBroadcast() }
     }
 
-    private fun observeIncomingChunks(room: Room, eventId: String) {
-        // Fixme this is probably not necessary here
-        currentTimeline?.dispose()
-        currentTimeline?.removeAllListeners()
+    private fun observeIncomingEvents(room: Room, eventId: String) {
         currentTimeline = room.timelineService().createTimeline(null, TimelineSettings(5)).also { timeline ->
             timelineListener = TimelineListener(eventId).also { timeline.addListener(it) }
             timeline.start()
@@ -321,13 +292,17 @@ class VoiceBroadcastPlayer @Inject constructor(
         }
 
         override fun onCompletion(mp: MediaPlayer) {
-            when {
-                timelineListener == null && nextMediaPlayer == null -> {
-                    stop()
-                }
-                nextMediaPlayer == null -> {
-                    state = State.BUFFERING
-                }
+            if (nextMediaPlayer != null) return
+            val roomId = currentRoomId ?: return
+            val voiceBroadcastId = currentVoiceBroadcastId ?: return
+            val voiceBroadcastEventContent = getVoiceBroadcastUseCase.execute(roomId, voiceBroadcastId)?.content ?: return
+            val isLive = voiceBroadcastEventContent.voiceBroadcastState != null && voiceBroadcastEventContent.voiceBroadcastState != VoiceBroadcastState.STOPPED
+
+            if (!isLive && voiceBroadcastEventContent.lastChunkSequence == currentSequence) {
+                // We'll not receive new chunks anymore so we can stop the live listening
+                stop()
+            } else {
+                state = State.BUFFERING
             }
         }
 
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastUseCase.kt
similarity index 80%
rename from vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt
rename to vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastUseCase.kt
index 5b3153ea40..d08fa14a95 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastUseCase.kt
@@ -16,7 +16,7 @@
 
 package im.vector.app.features.voicebroadcast.usecase
 
-import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.events.model.RelationType
@@ -24,18 +24,17 @@ import org.matrix.android.sdk.api.session.getRoom
 import timber.log.Timber
 import javax.inject.Inject
 
-class GetVoiceBroadcastStateUseCase @Inject constructor(
+class GetVoiceBroadcastUseCase @Inject constructor(
         private val session: Session,
 ) {
 
-    fun execute(roomId: String, eventId: String): VoiceBroadcastState? {
+    fun execute(roomId: String, eventId: String): VoiceBroadcastEvent? {
         val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
 
-        Timber.d("## GetVoiceBroadcastStateUseCase: get voice broadcast state requested for $eventId")
+        Timber.d("## GetVoiceBroadcastUseCase: get voice broadcast $eventId")
 
         val initialEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() // Fallback to initial event
         val relatedEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId).sortedBy { it.root.originServerTs }
-        val lastVoiceBroadcastEvent = relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent
-        return lastVoiceBroadcastEvent?.content?.voiceBroadcastState
+        return relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent
     }
 }

From 0a9f2bfa0ad050ff99ea546059d868134068dfac Mon Sep 17 00:00:00 2001
From: yostyle <y.pintas@gmail.com>
Date: Thu, 20 Oct 2022 19:30:17 +0200
Subject: [PATCH 378/400] Fix some PR comments

---
 .../vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt  | 2 --
 1 file changed, 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 403675a9ee..62252570c6 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -116,8 +116,6 @@ class VoiceBroadcastPlayer @Inject constructor(
         voiceBroadcastStateJob = null
 
         // In case of live broadcast, stop observing new chunks
-        currentTimeline?.dispose()
-        currentTimeline?.removeAllListeners()
         currentTimeline = null
         timelineListener = null
 

From 4c712095735fc5844ad93b5916fbe110bbf13ae2 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 17:58:03 +0200
Subject: [PATCH 379/400] VoiceBroadcast - Add recording view

---
 .../src/main/res/values/strings.xml           |   5 +
 .../ui-styles/src/main/res/values/dimens.xml  |   3 +
 .../timeline/factory/MessageItemFactory.kt    |   2 +-
 .../factory/VoiceBroadcastItemFactory.kt      |  44 ++++--
 .../item/MessageVoiceBroadcastItem.kt         | 104 -------------
 .../MessageVoiceBroadcastRecordingItem.kt     | 137 ++++++++++++++++++
 .../voicebroadcast/VoiceBroadcastRecorder.kt  |  17 ++-
 .../voicebroadcast/VoiceBroadcastRecorderQ.kt |  26 +++-
 .../usecase/StartVoiceBroadcastUseCase.kt     |   8 +-
 .../res/drawable/ic_live_broadcast_16.xml     |  21 +++
 .../main/res/drawable/ic_recording_dot.xml    |   9 ++
 vector/src/main/res/drawable/ic_stop.xml      |   9 ++
 .../res/drawable/rounded_rect_shape_2.xml     |  11 ++
 ...em_timeline_event_voice_broadcast_stub.xml | 107 +++++++++-----
 14 files changed, 337 insertions(+), 166 deletions(-)
 delete mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
 create mode 100644 vector/src/main/res/drawable/ic_live_broadcast_16.xml
 create mode 100644 vector/src/main/res/drawable/ic_recording_dot.xml
 create mode 100644 vector/src/main/res/drawable/ic_stop.xml
 create mode 100644 vector/src/main/res/drawable/rounded_rect_shape_2.xml

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index e6714005a1..69b4d57e28 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3078,6 +3078,11 @@
     <string name="audio_message_reply_content">%1$s (%2$s)</string>
     <string name="audio_message_file_size">(%1$s)</string>
 
+    <string name="voice_broadcast_live">Live</string>
+    <string name="a11y_resume_voice_broadcast_record">Resume voice broadcast record</string>
+    <string name="a11y_pause_voice_broadcast_record">Pause voice broadcast record</string>
+    <string name="a11y_stop_voice_broadcast_record">Stop voice broadcast record</string>
+
     <string name="upgrade_room_for_restricted">Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime.</string>
     <string name="upgrade_room_for_restricted_no_param">Anyone in a parent space will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime.</string>
 
diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml
index 52d16eae7d..50d5aaf014 100644
--- a/library/ui-styles/src/main/res/values/dimens.xml
+++ b/library/ui-styles/src/main/res/values/dimens.xml
@@ -73,6 +73,9 @@
     <dimen name="location_sharing_live_duration_choice_margin_horizontal">12dp</dimen>
     <dimen name="location_sharing_live_duration_choice_margin_vertical">22dp</dimen>
 
+    <!-- Voice Broadcast -->
+    <dimen name="voice_broadcast_controller_button_size">48dp</dimen>
+
     <!-- Material 3 -->
     <dimen name="collapsing_toolbar_layout_medium_size">112dp</dimen>
 
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 cb947a67ce..245d92f95b 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
@@ -201,7 +201,7 @@ class MessageItemFactory @Inject constructor(
             is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes)
             is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, attributes)
             is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes)
-            is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(messageContent, params.eventsGroup, highlight, callback, attributes)
+            is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(params, messageContent, highlight, callback, 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/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index f2dfb020a1..1064d2bbc5 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -15,46 +15,66 @@
  */
 package im.vector.app.features.home.room.detail.timeline.factory
 
+import im.vector.app.core.resources.ColorProvider
+import im.vector.app.core.resources.DrawableProvider
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
-import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
 import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
-import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventsGroup
 import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
-import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem
-import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem_
+import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.getRoom
+import org.matrix.android.sdk.api.util.toMatrixItem
 import javax.inject.Inject
 
 class VoiceBroadcastItemFactory @Inject constructor(
         private val session: Session,
         private val avatarSizeProvider: AvatarSizeProvider,
-        private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker,
+        private val colorProvider: ColorProvider,
+        private val drawableProvider: DrawableProvider,
+        private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
 ) {
 
     fun create(
+            params: TimelineItemFactoryParams,
             messageContent: MessageVoiceBroadcastInfoContent,
-            eventsGroup: TimelineEventsGroup?,
             highlight: Boolean,
             callback: TimelineEventController.Callback?,
             attributes: AbsMessageItem.Attributes,
-    ): MessageVoiceBroadcastItem? {
+    ): MessageVoiceBroadcastRecordingItem? {
         // Only display item of the initial event with updated data
         if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
-        val voiceBroadcastEventsGroup = eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
+        val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
         val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
         val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()
         val mostRecentMessageContent = mostRecentEvent?.content ?: return null
         val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId
-        return MessageVoiceBroadcastItem_()
+        return if (isRecording) {
+            createRecordingItem(params.event.roomId, highlight, callback, attributes)
+        } else {
+            createRecordingItem(params.event.roomId, highlight, callback, attributes)
+        }
+    }
+
+    private fun createRecordingItem(
+            roomId: String,
+            highlight: Boolean,
+            callback: TimelineEventController.Callback?,
+            attributes: AbsMessageItem.Attributes,
+    ): MessageVoiceBroadcastRecordingItem? {
+        val roomSummary = session.getRoom(roomId)?.roomSummary()
+        return MessageVoiceBroadcastRecordingItem_()
                 .attributes(attributes)
                 .highlighted(highlight)
-                .voiceBroadcastState(mostRecentMessageContent.voiceBroadcastState)
-                .recording(isRecording)
-                .audioMessagePlaybackTracker(audioMessagePlaybackTracker)
+                .roomItem(roomSummary?.toMatrixItem())
+                .colorProvider(colorProvider)
+                .drawableProvider(drawableProvider)
+                .voiceBroadcastRecorder(voiceBroadcastRecorder)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .callback(callback)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt
deleted file mode 100644
index 1927024a36..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.annotation.SuppressLint
-import android.widget.ImageButton
-import android.widget.TextView
-import com.airbnb.epoxy.EpoxyAttribute
-import com.airbnb.epoxy.EpoxyModelClass
-import im.vector.app.R
-import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
-import im.vector.app.features.home.room.detail.timeline.TimelineEventController
-import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
-import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State
-import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
-
-@EpoxyModelClass
-abstract class MessageVoiceBroadcastItem : AbsMessageItem<MessageVoiceBroadcastItem.Holder>() {
-
-    @EpoxyAttribute
-    var callback: TimelineEventController.Callback? = null
-
-    @EpoxyAttribute
-    var voiceBroadcastState: VoiceBroadcastState? = null
-
-    @EpoxyAttribute
-    var recording: Boolean = false
-
-    @EpoxyAttribute
-    lateinit var audioMessagePlaybackTracker: AudioMessagePlaybackTracker
-
-    private val voiceBroadcastEventId
-        get() = attributes.informationData.eventId
-
-    override fun isCacheable(): Boolean = false
-
-    override fun bind(holder: Holder) {
-        super.bind(holder)
-        bindVoiceBroadcastItem(holder)
-    }
-
-    @SuppressLint("SetTextI18n") // Temporary text
-    private fun bindVoiceBroadcastItem(holder: Holder) {
-        holder.currentStateText.text = "Voice Broadcast state: ${voiceBroadcastState?.value ?: "None"}"
-        if (recording) {
-            renderRecording(holder)
-        } else {
-            renderListening(holder)
-        }
-    }
-
-    private fun renderListening(holder: Holder) {
-        audioMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener {
-            override fun onUpdate(state: State) {
-                holder.playButton.isEnabled = state !is State.Playing
-                holder.pauseButton.isEnabled = state is State.Playing
-                holder.stopButton.isEnabled = state !is State.Idle
-            }
-        })
-        holder.playButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastEventId)) }
-        holder.pauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.Pause) }
-        holder.stopButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.Stop) }
-    }
-
-    private fun renderRecording(holder: Holder) {
-        with(holder) {
-            playButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.PAUSED
-            pauseButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.STARTED || voiceBroadcastState == VoiceBroadcastState.RESUMED
-            stopButton.isEnabled = voiceBroadcastState == VoiceBroadcastState.STARTED ||
-                    voiceBroadcastState == VoiceBroadcastState.RESUMED ||
-                    voiceBroadcastState == VoiceBroadcastState.PAUSED
-            playButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
-            pauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
-            stopButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
-        }
-    }
-
-    override fun getViewStubId() = STUB_ID
-
-    class Holder : AbsMessageLocationItem.Holder(STUB_ID) {
-        val currentStateText by bind<TextView>(R.id.currentStateText)
-        val playButton by bind<ImageButton>(R.id.playButton)
-        val pauseButton by bind<ImageButton>(R.id.pauseButton)
-        val stopButton by bind<ImageButton>(R.id.stopButton)
-    }
-
-    companion object {
-        private val STUB_ID = R.id.messageVoiceBroadcastStub
-    }
-}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
new file mode 100644
index 0000000000..d271c55ebb
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+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.extensions.tintBackground
+import im.vector.app.core.resources.ColorProvider
+import im.vector.app.core.resources.DrawableProvider
+import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
+import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
+import org.matrix.android.sdk.api.util.MatrixItem
+
+@EpoxyModelClass
+abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem<MessageVoiceBroadcastRecordingItem.Holder>() {
+
+    @EpoxyAttribute
+    var callback: TimelineEventController.Callback? = null
+
+    @EpoxyAttribute
+    var voiceBroadcastRecorder: VoiceBroadcastRecorder? = null
+
+    @EpoxyAttribute
+    lateinit var colorProvider: ColorProvider
+
+    @EpoxyAttribute
+    lateinit var drawableProvider: DrawableProvider
+
+    @EpoxyAttribute
+    var roomItem: MatrixItem? = null
+
+    @EpoxyAttribute
+    var title: String? = null
+
+    private lateinit var recorderListener: VoiceBroadcastRecorder.Listener
+
+    override fun isCacheable(): Boolean = false
+
+    override fun bind(holder: Holder) {
+        super.bind(holder)
+        bindVoiceBroadcastItem(holder)
+    }
+
+    private fun bindVoiceBroadcastItem(holder: Holder) {
+        recorderListener = object : VoiceBroadcastRecorder.Listener {
+            override fun onStateUpdated(state: VoiceBroadcastRecorder.State) {
+                renderState(holder, state)
+            }
+        }
+        voiceBroadcastRecorder?.addListener(recorderListener)
+        renderHeader(holder)
+    }
+
+    private fun renderHeader(holder: Holder) {
+        with(holder) {
+            roomItem?.let {
+                attributes.avatarRenderer.render(it, roomAvatarImageView)
+                titleText.text = it.displayName
+            }
+        }
+    }
+
+    private fun renderState(holder: Holder, state: VoiceBroadcastRecorder.State) {
+        with(holder) {
+            when (state) {
+                VoiceBroadcastRecorder.State.Recording -> {
+                    stopRecordButton.isEnabled = true
+
+                    liveIndicator.isVisible = true
+                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
+
+                    val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
+                    val drawable = drawableProvider.getDrawable(R.drawable.ic_play_pause_pause, drawableColor)
+                    recordButton.setImageDrawable(drawable)
+                    recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_pause_voice_broadcast_record)
+                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
+                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+                }
+                VoiceBroadcastRecorder.State.Paused -> {
+                    stopRecordButton.isEnabled = true
+
+                    liveIndicator.isVisible = true
+                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
+
+                    recordButton.setImageResource(R.drawable.ic_recording_dot)
+                    recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_resume_voice_broadcast_record)
+                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
+                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+                }
+                VoiceBroadcastRecorder.State.Idle -> {
+                    recordButton.isEnabled = false
+                    stopRecordButton.isEnabled = false
+                    liveIndicator.isVisible = false
+                }
+            }
+        }
+    }
+
+    override fun unbind(holder: Holder) {
+        super.unbind(holder)
+        voiceBroadcastRecorder?.removeListener(recorderListener)
+    }
+
+    override fun getViewStubId() = STUB_ID
+
+    class Holder : AbsMessageItem.Holder(STUB_ID) {
+        val liveIndicator by bind<TextView>(R.id.liveIndicator)
+        val roomAvatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
+        val titleText by bind<TextView>(R.id.titleText)
+        val recordButton by bind<ImageButton>(R.id.recordButton)
+        val stopRecordButton by bind<ImageButton>(R.id.stopRecordButton)
+    }
+
+    companion object {
+        private val STUB_ID = R.id.messageVoiceBroadcastStub
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
index c9bb0c5f54..8b69051823 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt
@@ -22,12 +22,21 @@ import java.io.File
 
 interface VoiceBroadcastRecorder : VoiceRecorder {
 
-    var listener: Listener?
-    var currentSequence: Int
+    val currentSequence: Int
+    val state: State
 
     fun startRecord(roomId: String, chunkLength: Int)
+    fun addListener(listener: Listener)
+    fun removeListener(listener: Listener)
 
-    fun interface Listener {
-        fun onVoiceMessageCreated(file: File, @IntRange(from = 1) sequence: Int)
+    interface Listener {
+        fun onVoiceMessageCreated(file: File, @IntRange(from = 1) sequence: Int) = Unit
+        fun onStateUpdated(state: State) = Unit
+    }
+
+    enum class State {
+        Recording,
+        Paused,
+        Idle,
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
index a65aae6f8a..cd1a61b986 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -32,8 +32,13 @@ class VoiceBroadcastRecorderQ(
     private var maxFileSize = 0L // zero or negative for no limit
     private var currentRoomId: String? = null
     override var currentSequence = 0
+    override var state = VoiceBroadcastRecorder.State.Idle
+        set(value) {
+            field = value
+            listeners.forEach { it.onStateUpdated(value) }
+        }
 
-    override var listener: VoiceBroadcastRecorder.Listener? = null
+    private val listeners = mutableListOf<VoiceBroadcastRecorder.Listener>()
 
     override val outputFormat = MediaRecorder.OutputFormat.MPEG_4
     override val audioEncoder = MediaRecorder.AudioEncoder.HE_AAC
@@ -57,24 +62,28 @@ class VoiceBroadcastRecorderQ(
         maxFileSize = (chunkLength * audioEncodingBitRate / 8).toLong()
         currentSequence = 1
         startRecord(roomId)
+        state = VoiceBroadcastRecorder.State.Recording
     }
 
     override fun pauseRecord() {
         tryOrNull { mediaRecorder?.stop() }
         mediaRecorder?.reset()
         notifyOutputFileCreated()
+        state = VoiceBroadcastRecorder.State.Paused
     }
 
     override fun resumeRecord() {
         currentSequence++
         currentRoomId?.let { startRecord(it) }
+        state = VoiceBroadcastRecorder.State.Recording
     }
 
     override fun stopRecord() {
         super.stopRecord()
         notifyOutputFileCreated()
-        listener = null
+        listeners.clear()
         currentSequence = 0
+        state = VoiceBroadcastRecorder.State.Idle
     }
 
     override fun release() {
@@ -82,6 +91,15 @@ class VoiceBroadcastRecorderQ(
         super.release()
     }
 
+    override fun addListener(listener: VoiceBroadcastRecorder.Listener) {
+        listeners.add(listener)
+        listener.onStateUpdated(state)
+    }
+
+    override fun removeListener(listener: VoiceBroadcastRecorder.Listener) {
+        listeners.remove(listener)
+    }
+
     private fun onMaxFileSizeApproaching(roomId: String) {
         setNextOutputFile(roomId)
     }
@@ -92,8 +110,8 @@ class VoiceBroadcastRecorderQ(
     }
 
     private fun notifyOutputFileCreated() {
-        outputFile?.let {
-            listener?.onVoiceMessageCreated(it, currentSequence)
+        outputFile?.let { file ->
+            listeners.forEach { it.onVoiceMessageCreated(file, currentSequence) }
             outputFile = nextOutputFile
             nextOutputFile = null
         }
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
index d5d58f822e..7934d18e36 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt
@@ -81,9 +81,11 @@ class StartVoiceBroadcastUseCase @Inject constructor(
     }
 
     private fun startRecording(room: Room, eventId: String, chunkLength: Int) {
-        voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file, sequence ->
-            sendVoiceFile(room, file, eventId, sequence)
-        }
+        voiceBroadcastRecorder?.addListener(object : VoiceBroadcastRecorder.Listener {
+            override fun onVoiceMessageCreated(file: File, sequence: Int) {
+                sendVoiceFile(room, file, eventId, sequence)
+            }
+        })
         voiceBroadcastRecorder?.startRecord(room.roomId, chunkLength)
     }
 
diff --git a/vector/src/main/res/drawable/ic_live_broadcast_16.xml b/vector/src/main/res/drawable/ic_live_broadcast_16.xml
new file mode 100644
index 0000000000..7d427a56d0
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_live_broadcast_16.xml
@@ -0,0 +1,21 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="16"
+    android:viewportHeight="16">
+  <path
+      android:pathData="M13.459,2.791C13.233,2.5 12.814,2.448 12.523,2.674C12.233,2.9 12.181,3.318 12.406,3.609L12.406,3.609L12.407,3.61L12.416,3.622C12.425,3.634 12.439,3.654 12.458,3.68C12.496,3.733 12.552,3.815 12.62,3.923C12.757,4.138 12.943,4.456 13.128,4.854C13.502,5.654 13.866,6.756 13.866,8C13.866,9.245 13.502,10.347 13.128,11.147C12.943,11.545 12.757,11.863 12.62,12.078C12.552,12.186 12.496,12.267 12.458,12.321C12.439,12.347 12.425,12.367 12.416,12.378L12.407,12.391L12.406,12.391L12.406,12.392C12.181,12.683 12.233,13.101 12.523,13.327C12.814,13.553 13.233,13.5 13.459,13.21L12.962,12.823C13.459,13.21 13.459,13.21 13.459,13.21L13.46,13.208L13.462,13.205L13.468,13.198L13.485,13.175C13.5,13.155 13.52,13.128 13.545,13.093C13.595,13.023 13.664,12.922 13.745,12.794C13.908,12.538 14.123,12.17 14.337,11.711C14.763,10.797 15.199,9.499 15.199,8C15.199,6.502 14.763,5.204 14.337,4.29C14.123,3.831 13.908,3.463 13.745,3.207C13.664,3.079 13.595,2.978 13.545,2.908C13.52,2.873 13.5,2.846 13.485,2.826L13.468,2.803L13.462,2.795L13.46,2.793L13.46,2.792C13.46,2.792 13.459,2.791 12.933,3.2L13.459,2.791Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M11.726,5.191C11.5,4.901 11.081,4.848 10.791,5.074C10.501,5.3 10.448,5.717 10.672,6.008L10.674,6.011C10.677,6.015 10.683,6.022 10.691,6.033C10.707,6.056 10.731,6.092 10.762,6.141C10.825,6.238 10.91,6.384 10.996,6.568C11.169,6.94 11.333,7.442 11.333,8.001C11.333,8.559 11.169,9.061 10.996,9.433C10.91,9.617 10.825,9.763 10.762,9.86C10.731,9.909 10.707,9.945 10.691,9.968C10.683,9.979 10.677,9.986 10.674,9.99L10.672,9.994C10.448,10.284 10.501,10.701 10.791,10.927C11.081,11.153 11.5,11.101 11.726,10.81L11.2,10.401C11.726,10.81 11.726,10.81 11.726,10.81L11.727,10.808L11.729,10.806L11.733,10.801L11.744,10.787C11.752,10.775 11.764,10.759 11.778,10.74C11.806,10.7 11.843,10.646 11.887,10.576C11.975,10.438 12.09,10.241 12.204,9.997C12.431,9.511 12.667,8.813 12.667,8.001C12.667,7.188 12.431,6.49 12.204,6.004C12.09,5.76 11.975,5.563 11.887,5.425C11.843,5.356 11.806,5.301 11.778,5.261C11.764,5.242 11.752,5.226 11.744,5.214L11.733,5.2L11.729,5.195L11.727,5.193L11.727,5.192C11.727,5.192 11.726,5.191 11.2,5.601L11.726,5.191Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M2.407,13.21C2.633,13.5 3.052,13.553 3.343,13.327C3.633,13.101 3.686,12.683 3.461,12.392L3.46,12.391L3.459,12.391L3.45,12.378C3.441,12.366 3.427,12.347 3.408,12.321C3.371,12.267 3.314,12.186 3.246,12.078C3.109,11.863 2.924,11.545 2.738,11.147C2.364,10.347 2,9.245 2,8C2,6.756 2.364,5.654 2.738,4.854C2.924,4.456 3.109,4.138 3.246,3.923C3.314,3.815 3.371,3.733 3.408,3.68C3.427,3.654 3.441,3.634 3.45,3.622L3.459,3.61L3.46,3.609L3.461,3.609C3.686,3.318 3.633,2.9 3.343,2.674C3.052,2.448 2.633,2.5 2.407,2.791L2.904,3.177C2.407,2.791 2.407,2.791 2.407,2.791L2.406,2.793L2.404,2.795L2.399,2.802L2.381,2.826C2.366,2.846 2.346,2.873 2.321,2.908C2.272,2.978 2.203,3.079 2.121,3.207C1.958,3.463 1.744,3.831 1.529,4.29C1.103,5.204 0.667,6.502 0.667,8C0.667,9.499 1.103,10.797 1.529,11.711C1.744,12.17 1.958,12.538 2.121,12.794C2.203,12.922 2.272,13.023 2.321,13.093C2.346,13.128 2.366,13.155 2.381,13.175L2.399,13.198L2.404,13.205L2.406,13.208L2.407,13.209C2.407,13.209 2.407,13.21 2.934,12.8L2.407,13.21Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M4.14,10.809C4.366,11.1 4.785,11.153 5.076,10.926C5.365,10.701 5.418,10.284 5.194,9.993L5.192,9.99C5.189,9.986 5.183,9.978 5.175,9.967C5.16,9.945 5.135,9.909 5.104,9.86C5.042,9.762 4.956,9.616 4.87,9.433C4.697,9.061 4.533,8.559 4.533,8C4.533,7.442 4.697,6.94 4.87,6.568C4.956,6.384 5.042,6.238 5.104,6.14C5.135,6.092 5.16,6.055 5.175,6.033C5.183,6.022 5.189,6.014 5.192,6.01L5.194,6.007C5.418,5.717 5.365,5.299 5.076,5.074C4.785,4.848 4.366,4.9 4.14,5.191L4.666,5.6C4.14,5.191 4.14,5.191 4.14,5.191L4.139,5.192L4.137,5.194L4.134,5.199L4.123,5.214C4.114,5.226 4.102,5.241 4.088,5.261C4.061,5.3 4.023,5.355 3.979,5.424C3.891,5.562 3.776,5.759 3.662,6.004C3.436,6.489 3.2,7.187 3.2,8C3.2,8.813 3.436,9.511 3.662,9.996C3.776,10.241 3.891,10.438 3.979,10.576C4.023,10.645 4.061,10.7 4.088,10.739C4.102,10.759 4.114,10.775 4.123,10.786L4.134,10.801L4.137,10.806L4.139,10.808L4.14,10.809C4.14,10.809 4.14,10.809 4.666,10.4L4.14,10.809Z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M8,8m-1.333,0a1.333,1.333 0,1 1,2.667 0a1.333,1.333 0,1 1,-2.667 0"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/vector/src/main/res/drawable/ic_recording_dot.xml b/vector/src/main/res/drawable/ic_recording_dot.xml
new file mode 100644
index 0000000000..f5d92f9718
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_recording_dot.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="16"
+    android:viewportHeight="16">
+  <path
+      android:pathData="M8,8m-7.5,0a7.5,7.5 0,1 1,15 0a7.5,7.5 0,1 1,-15 0"
+      android:fillColor="#FF5B55"/>
+</vector>
diff --git a/vector/src/main/res/drawable/ic_stop.xml b/vector/src/main/res/drawable/ic_stop.xml
new file mode 100644
index 0000000000..459a7cfce2
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_stop.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="18dp"
+    android:height="24dp"
+    android:viewportWidth="18"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M1.5,3L16.5,3A1.5,1.5 0,0 1,18 4.5L18,19.5A1.5,1.5 0,0 1,16.5 21L1.5,21A1.5,1.5 0,0 1,0 19.5L0,4.5A1.5,1.5 0,0 1,1.5 3z"
+      android:fillColor="#737D8C"/>
+</vector>
diff --git a/vector/src/main/res/drawable/rounded_rect_shape_2.xml b/vector/src/main/res/drawable/rounded_rect_shape_2.xml
new file mode 100644
index 0000000000..977de2fd09
--- /dev/null
+++ b/vector/src/main/res/drawable/rounded_rect_shape_2.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <size android:width="40dp" android:height="40dp"/>
+
+    <solid android:color="?vctr_system" />
+
+    <corners android:radius="2dp" />
+
+</shape>
diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml
index e35060f72a..6773280ba5 100644
--- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml
@@ -5,58 +5,89 @@
     android:id="@+id/messageRootLayout"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:background="@drawable/rounded_rect_shape_8"
+    android:backgroundTint="?vctr_content_quinary"
     android:padding="@dimen/layout_vertical_margin"
     tools:viewBindingIgnore="true">
 
     <TextView
-        android:id="@+id/currentStateText"
-        style="@style/Widget.Vector.TextView.HeadlineMedium"
+        android:id="@+id/liveIndicator"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toTopOf="@id/playButton"
+        android:layout_height="20dp"
+        android:background="@drawable/rounded_rect_shape_2"
+        android:backgroundTint="?colorError"
+        android:drawablePadding="4dp"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:maxWidth="100dp"
+        android:paddingHorizontal="4dp"
+        android:singleLine="true"
+        android:text="@string/voice_broadcast_live"
+        android:textColor="?colorOnError"
+        app:drawableStartCompat="@drawable/ic_live_broadcast_16"
+        app:drawableTint="?colorOnError"
         app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <ImageView
+        android:id="@+id/roomAvatarImageView"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:contentDescription="@string/avatar"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:text="Voice Broadcast state: STARTED" />
+        tools:src="@sample/user_round_avatars" />
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/avatarRightBarrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierDirection="right"
+        app:barrierMargin="6dp"
+        app:constraint_referenced_ids="roomAvatarImageView" />
+
+    <TextView
+        android:id="@+id/titleText"
+        style="@style/Widget.Vector.TextView.Body.Medium"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/avatar"
+        app:layout_constraintStart_toEndOf="@id/avatarRightBarrier"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:src="@sample/users.json/data/displayName" />
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/headerBottomBarrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierDirection="bottom"
+        app:barrierMargin="12dp"
+        app:constraint_referenced_ids="roomAvatarImageView,titleText" />
 
     <ImageButton
-        android:id="@+id/playButton"
-        android:layout_width="@dimen/item_event_message_media_button_size"
-        android:layout_height="@dimen/item_event_message_media_button_size"
-        android:layout_marginTop="@dimen/layout_vertical_margin"
-        android:background="?android:attr/selectableItemBackground"
-        android:contentDescription="@string/a11y_play_voice_message"
-        android:src="@drawable/ic_play_pause_play"
+        android:id="@+id/recordButton"
+        android:layout_width="@dimen/voice_broadcast_controller_button_size"
+        android:layout_height="@dimen/voice_broadcast_controller_button_size"
+        android:background="@drawable/bg_rounded_button"
+        android:backgroundTint="?vctr_system"
+        android:contentDescription="@string/a11y_resume_voice_broadcast_record"
+        android:src="@drawable/ic_recording_dot"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/pauseButton"
+        app:layout_constraintEnd_toStartOf="@id/stopRecordButton"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/currentStateText"
-        app:tint="@color/vector_content_primary_tint_selector" />
+        app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" />
 
     <ImageButton
-        android:id="@+id/pauseButton"
-        android:layout_width="@dimen/item_event_message_media_button_size"
-        android:layout_height="@dimen/item_event_message_media_button_size"
-        android:background="?android:attr/selectableItemBackground"
-        android:contentDescription="@string/a11y_pause_voice_message"
-        android:src="@drawable/ic_play_pause_pause"
-        app:layout_constraintBottom_toBottomOf="@id/playButton"
-        app:layout_constraintEnd_toStartOf="@id/stopButton"
-        app:layout_constraintStart_toEndOf="@id/playButton"
-        app:layout_constraintTop_toTopOf="@id/playButton"
-        app:tint="@color/vector_content_primary_tint_selector" />
-
-    <ImageButton
-        android:id="@+id/stopButton"
-        android:layout_width="@dimen/item_event_message_media_button_size"
-        android:layout_height="@dimen/item_event_message_media_button_size"
-        android:background="?android:attr/selectableItemBackground"
-        android:contentDescription="@string/a11y_stop_voice_message"
-        android:src="@drawable/ic_close_24dp"
-        app:layout_constraintBottom_toBottomOf="@id/pauseButton"
+        android:id="@+id/stopRecordButton"
+        android:layout_width="@dimen/voice_broadcast_controller_button_size"
+        android:layout_height="@dimen/voice_broadcast_controller_button_size"
+        android:background="@drawable/bg_rounded_button"
+        android:backgroundTint="?vctr_system"
+        android:contentDescription="@string/a11y_stop_voice_broadcast_record"
+        android:src="@drawable/ic_stop"
+        app:layout_constraintBottom_toBottomOf="@id/recordButton"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toEndOf="@id/pauseButton"
-        app:layout_constraintTop_toTopOf="@id/playButton"
-        app:tint="@color/vector_content_primary_tint_selector" />
+        app:layout_constraintStart_toEndOf="@id/recordButton"
+        app:layout_constraintTop_toTopOf="@id/recordButton" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>

From f1b4ebbc37f266702327fc9db517c8d142178387 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 18:10:32 +0200
Subject: [PATCH 380/400] VoiceBroadcast - Introduce listening view

---
 .../factory/VoiceBroadcastItemFactory.kt      |  28 +++-
 .../MessageVoiceBroadcastListeningItem.kt     | 137 ++++++++++++++++++
 .../MessageVoiceBroadcastRecordingItem.kt     |   2 +-
 ...em_timeline_event_view_stubs_container.xml |  11 +-
 ..._event_voice_broadcast_listening_stub.xml} |   0
 ...e_event_voice_broadcast_recording_stub.xml |  93 ++++++++++++
 6 files changed, 265 insertions(+), 6 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
 rename vector/src/main/res/layout/{item_timeline_event_voice_broadcast_stub.xml => item_timeline_event_voice_broadcast_listening_stub.xml} (100%)
 create mode 100644 vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index 1064d2bbc5..13a38ac4be 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -15,12 +15,16 @@
  */
 package im.vector.app.features.home.room.detail.timeline.factory
 
+import im.vector.app.core.epoxy.VectorEpoxyHolder
+import im.vector.app.core.epoxy.VectorEpoxyModel
 import im.vector.app.core.resources.ColorProvider
 import im.vector.app.core.resources.DrawableProvider
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
 import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
 import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
@@ -46,7 +50,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
             highlight: Boolean,
             callback: TimelineEventController.Callback?,
             attributes: AbsMessageItem.Attributes,
-    ): MessageVoiceBroadcastRecordingItem? {
+    ): VectorEpoxyModel<out VectorEpoxyHolder>? {
         // Only display item of the initial event with updated data
         if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
         val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
@@ -57,7 +61,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
         return if (isRecording) {
             createRecordingItem(params.event.roomId, highlight, callback, attributes)
         } else {
-            createRecordingItem(params.event.roomId, highlight, callback, attributes)
+            createListeningItem(params.event.roomId, highlight, callback, attributes)
         }
     }
 
@@ -66,7 +70,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
             highlight: Boolean,
             callback: TimelineEventController.Callback?,
             attributes: AbsMessageItem.Attributes,
-    ): MessageVoiceBroadcastRecordingItem? {
+    ): MessageVoiceBroadcastRecordingItem {
         val roomSummary = session.getRoom(roomId)?.roomSummary()
         return MessageVoiceBroadcastRecordingItem_()
                 .attributes(attributes)
@@ -78,4 +82,22 @@ class VoiceBroadcastItemFactory @Inject constructor(
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .callback(callback)
     }
+
+    private fun createListeningItem(
+            roomId: String,
+            highlight: Boolean,
+            callback: TimelineEventController.Callback?,
+            attributes: AbsMessageItem.Attributes,
+    ): MessageVoiceBroadcastListeningItem {
+        val roomSummary = session.getRoom(roomId)?.roomSummary()
+        return MessageVoiceBroadcastListeningItem_()
+                .attributes(attributes)
+                .highlighted(highlight)
+                .roomItem(roomSummary?.toMatrixItem())
+                .colorProvider(colorProvider)
+                .drawableProvider(drawableProvider)
+                .voiceBroadcastRecorder(voiceBroadcastRecorder)
+                .leftGuideline(avatarSizeProvider.leftGuideline)
+                .callback(callback)
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
new file mode 100644
index 0000000000..e5d0fd6c30
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.ImageButton
+import android.widget.ImageView
+import android.widget.TextView
+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.extensions.tintBackground
+import im.vector.app.core.resources.ColorProvider
+import im.vector.app.core.resources.DrawableProvider
+import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
+import im.vector.app.features.home.room.detail.timeline.TimelineEventController
+import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
+import org.matrix.android.sdk.api.util.MatrixItem
+
+@EpoxyModelClass
+abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceBroadcastListeningItem.Holder>() {
+
+    @EpoxyAttribute
+    var callback: TimelineEventController.Callback? = null
+
+    @EpoxyAttribute
+    var voiceBroadcastRecorder: VoiceBroadcastRecorder? = null
+
+    @EpoxyAttribute
+    lateinit var colorProvider: ColorProvider
+
+    @EpoxyAttribute
+    lateinit var drawableProvider: DrawableProvider
+
+    @EpoxyAttribute
+    var roomItem: MatrixItem? = null
+
+    @EpoxyAttribute
+    var title: String? = null
+
+    private lateinit var recorderListener: VoiceBroadcastRecorder.Listener
+
+    override fun isCacheable(): Boolean = false
+
+    override fun bind(holder: Holder) {
+        super.bind(holder)
+        bindVoiceBroadcastItem(holder)
+    }
+
+    private fun bindVoiceBroadcastItem(holder: Holder) {
+        recorderListener = object : VoiceBroadcastRecorder.Listener {
+            override fun onStateUpdated(state: VoiceBroadcastRecorder.State) {
+                renderState(holder, state)
+            }
+        }
+        voiceBroadcastRecorder?.addListener(recorderListener)
+        renderHeader(holder)
+    }
+
+    private fun renderHeader(holder: Holder) {
+        with(holder) {
+            roomItem?.let {
+                attributes.avatarRenderer.render(it, roomAvatarImageView)
+                titleText.text = it.displayName
+            }
+        }
+    }
+
+    private fun renderState(holder: Holder, state: VoiceBroadcastRecorder.State) {
+        with(holder) {
+            when (state) {
+                VoiceBroadcastRecorder.State.Recording -> {
+                    stopRecordButton.isEnabled = true
+
+                    liveIndicator.isVisible = true
+                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
+
+                    val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
+                    val drawable = drawableProvider.getDrawable(R.drawable.ic_play_pause_pause, drawableColor)
+                    recordButton.setImageDrawable(drawable)
+                    recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_pause_voice_broadcast_record)
+                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
+                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+                }
+                VoiceBroadcastRecorder.State.Paused -> {
+                    stopRecordButton.isEnabled = true
+
+                    liveIndicator.isVisible = true
+                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
+
+                    recordButton.setImageResource(R.drawable.ic_recording_dot)
+                    recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_resume_voice_broadcast_record)
+                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
+                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+                }
+                VoiceBroadcastRecorder.State.Idle -> {
+                    recordButton.isEnabled = false
+                    stopRecordButton.isEnabled = false
+                    liveIndicator.isVisible = false
+                }
+            }
+        }
+    }
+
+    override fun unbind(holder: Holder) {
+        super.unbind(holder)
+        voiceBroadcastRecorder?.removeListener(recorderListener)
+    }
+
+    override fun getViewStubId() = STUB_ID
+
+    class Holder : AbsMessageItem.Holder(STUB_ID) {
+        val liveIndicator by bind<TextView>(R.id.liveIndicator)
+        val roomAvatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
+        val titleText by bind<TextView>(R.id.titleText)
+        val recordButton by bind<ImageButton>(R.id.recordButton)
+        val stopRecordButton by bind<ImageButton>(R.id.stopRecordButton)
+    }
+
+    companion object {
+        private val STUB_ID = R.id.messageVoiceBroadcastListeningStub
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
index d271c55ebb..30475c1659 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
@@ -132,6 +132,6 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem<MessageVoiceB
     }
 
     companion object {
-        private val STUB_ID = R.id.messageVoiceBroadcastStub
+        private val STUB_ID = R.id.messageVoiceBroadcastRecordingStub
     }
 }
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 6fcf5711f7..643f4a89c8 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
@@ -48,10 +48,17 @@
         tools:visibility="gone" />
 
     <ViewStub
-        android:id="@+id/messageVoiceBroadcastStub"
+        android:id="@+id/messageVoiceBroadcastRecordingStub"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout="@layout/item_timeline_event_voice_broadcast_stub"
+        android:layout="@layout/item_timeline_event_voice_broadcast_recording_stub"
+        tools:visibility="gone" />
+
+    <ViewStub
+        android:id="@+id/messageVoiceBroadcastListeningStub"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout="@layout/item_timeline_event_voice_broadcast_listening_stub"
         tools:visibility="gone" />
 
     <ViewStub
diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
similarity index 100%
rename from vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml
rename to vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml
new file mode 100644
index 0000000000..6773280ba5
--- /dev/null
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml
@@ -0,0 +1,93 @@
+<?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:id="@+id/messageRootLayout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/rounded_rect_shape_8"
+    android:backgroundTint="?vctr_content_quinary"
+    android:padding="@dimen/layout_vertical_margin"
+    tools:viewBindingIgnore="true">
+
+    <TextView
+        android:id="@+id/liveIndicator"
+        android:layout_width="wrap_content"
+        android:layout_height="20dp"
+        android:background="@drawable/rounded_rect_shape_2"
+        android:backgroundTint="?colorError"
+        android:drawablePadding="4dp"
+        android:ellipsize="end"
+        android:gravity="center_vertical"
+        android:maxWidth="100dp"
+        android:paddingHorizontal="4dp"
+        android:singleLine="true"
+        android:text="@string/voice_broadcast_live"
+        android:textColor="?colorOnError"
+        app:drawableStartCompat="@drawable/ic_live_broadcast_16"
+        app:drawableTint="?colorOnError"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <ImageView
+        android:id="@+id/roomAvatarImageView"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:contentDescription="@string/avatar"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:src="@sample/user_round_avatars" />
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/avatarRightBarrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierDirection="right"
+        app:barrierMargin="6dp"
+        app:constraint_referenced_ids="roomAvatarImageView" />
+
+    <TextView
+        android:id="@+id/titleText"
+        style="@style/Widget.Vector.TextView.Body.Medium"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/avatar"
+        app:layout_constraintStart_toEndOf="@id/avatarRightBarrier"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:src="@sample/users.json/data/displayName" />
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/headerBottomBarrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierDirection="bottom"
+        app:barrierMargin="12dp"
+        app:constraint_referenced_ids="roomAvatarImageView,titleText" />
+
+    <ImageButton
+        android:id="@+id/recordButton"
+        android:layout_width="@dimen/voice_broadcast_controller_button_size"
+        android:layout_height="@dimen/voice_broadcast_controller_button_size"
+        android:background="@drawable/bg_rounded_button"
+        android:backgroundTint="?vctr_system"
+        android:contentDescription="@string/a11y_resume_voice_broadcast_record"
+        android:src="@drawable/ic_recording_dot"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/stopRecordButton"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" />
+
+    <ImageButton
+        android:id="@+id/stopRecordButton"
+        android:layout_width="@dimen/voice_broadcast_controller_button_size"
+        android:layout_height="@dimen/voice_broadcast_controller_button_size"
+        android:background="@drawable/bg_rounded_button"
+        android:backgroundTint="?vctr_system"
+        android:contentDescription="@string/a11y_stop_voice_broadcast_record"
+        android:src="@drawable/ic_stop"
+        app:layout_constraintBottom_toBottomOf="@id/recordButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@id/recordButton"
+        app:layout_constraintTop_toTopOf="@id/recordButton" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

From f711a0ea740f14f4d63eff35d67355e5224bdf57 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 19:07:34 +0200
Subject: [PATCH 381/400] VoiceBroadcast - Listening view

---
 .../src/main/res/values/strings.xml           |  3 +
 .../factory/VoiceBroadcastItemFactory.kt      | 11 ++-
 .../MessageVoiceBroadcastListeningItem.kt     | 97 +++++++++++--------
 .../voicebroadcast/VoiceBroadcastPlayer.kt    | 17 +++-
 ...e_event_voice_broadcast_listening_stub.xml | 32 +++---
 5 files changed, 98 insertions(+), 62 deletions(-)

diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index 69b4d57e28..ea9b4b5999 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -3082,6 +3082,9 @@
     <string name="a11y_resume_voice_broadcast_record">Resume voice broadcast record</string>
     <string name="a11y_pause_voice_broadcast_record">Pause voice broadcast record</string>
     <string name="a11y_stop_voice_broadcast_record">Stop voice broadcast record</string>
+    <string name="a11y_play_voice_broadcast">Play or resume voice broadcast</string>
+    <string name="a11y_pause_voice_broadcast">Pause voice broadcast</string>
+    <string name="a11y_voice_broadcast_buffering">Buffering</string>
 
     <string name="upgrade_room_for_restricted">Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime.</string>
     <string name="upgrade_room_for_restricted_no_param">Anyone in a parent space will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime.</string>
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index 13a38ac4be..af14d532c8 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -27,6 +27,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadca
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_
+import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer
 import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
 import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent
 import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
@@ -42,6 +43,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
         private val colorProvider: ColorProvider,
         private val drawableProvider: DrawableProvider,
         private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
+        private val voiceBroadcastPlayer: VoiceBroadcastPlayer,
 ) {
 
     fun create(
@@ -53,7 +55,8 @@ class VoiceBroadcastItemFactory @Inject constructor(
     ): VectorEpoxyModel<out VectorEpoxyHolder>? {
         // Only display item of the initial event with updated data
         if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null
-        val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null
+        val eventsGroup = params.eventsGroup ?: return null
+        val voiceBroadcastEventsGroup = VoiceBroadcastEventsGroup(eventsGroup)
         val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent()
         val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()
         val mostRecentMessageContent = mostRecentEvent?.content ?: return null
@@ -61,7 +64,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
         return if (isRecording) {
             createRecordingItem(params.event.roomId, highlight, callback, attributes)
         } else {
-            createListeningItem(params.event.roomId, highlight, callback, attributes)
+            createListeningItem(params.event.roomId, eventsGroup.groupId, highlight, callback, attributes)
         }
     }
 
@@ -85,6 +88,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
 
     private fun createListeningItem(
             roomId: String,
+            voiceBroadcastId: String,
             highlight: Boolean,
             callback: TimelineEventController.Callback?,
             attributes: AbsMessageItem.Attributes,
@@ -96,7 +100,8 @@ class VoiceBroadcastItemFactory @Inject constructor(
                 .roomItem(roomSummary?.toMatrixItem())
                 .colorProvider(colorProvider)
                 .drawableProvider(drawableProvider)
-                .voiceBroadcastRecorder(voiceBroadcastRecorder)
+                .voiceBroadcastPlayer(voiceBroadcastPlayer)
+                .voiceBroadcastId(voiceBroadcastId)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .callback(callback)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
index e5d0fd6c30..3a090b0eb6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
@@ -16,6 +16,7 @@
 
 package im.vector.app.features.home.room.detail.timeline.item
 
+import android.view.View
 import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
@@ -23,12 +24,11 @@ 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.extensions.tintBackground
 import im.vector.app.core.resources.ColorProvider
 import im.vector.app.core.resources.DrawableProvider
-import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
+import im.vector.app.features.home.room.detail.RoomDetailAction
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
-import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder
+import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer
 import org.matrix.android.sdk.api.util.MatrixItem
 
 @EpoxyModelClass
@@ -38,7 +38,10 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
     var callback: TimelineEventController.Callback? = null
 
     @EpoxyAttribute
-    var voiceBroadcastRecorder: VoiceBroadcastRecorder? = null
+    var voiceBroadcastPlayer: VoiceBroadcastPlayer? = null
+
+    @EpoxyAttribute
+    lateinit var voiceBroadcastId: String
 
     @EpoxyAttribute
     lateinit var colorProvider: ColorProvider
@@ -52,7 +55,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
     @EpoxyAttribute
     var title: String? = null
 
-    private lateinit var recorderListener: VoiceBroadcastRecorder.Listener
+    private lateinit var playerListener: VoiceBroadcastPlayer.Listener
 
     override fun isCacheable(): Boolean = false
 
@@ -62,12 +65,10 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
     }
 
     private fun bindVoiceBroadcastItem(holder: Holder) {
-        recorderListener = object : VoiceBroadcastRecorder.Listener {
-            override fun onStateUpdated(state: VoiceBroadcastRecorder.State) {
-                renderState(holder, state)
-            }
+        playerListener = VoiceBroadcastPlayer.Listener { state ->
+            renderState(holder, state)
         }
-        voiceBroadcastRecorder?.addListener(recorderListener)
+        voiceBroadcastPlayer?.addListener(playerListener)
         renderHeader(holder)
     }
 
@@ -80,45 +81,59 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
         }
     }
 
-    private fun renderState(holder: Holder, state: VoiceBroadcastRecorder.State) {
+    private fun renderState(holder: Holder, state: VoiceBroadcastPlayer.State) {
+        if (isCurrentMediaActive()) {
+            renderActiveMedia(holder, state)
+        } else {
+            renderInactiveMedia(holder)
+        }
+    }
+
+    @Suppress("UNUSED_PARAMETER")
+    private fun renderActiveMedia(holder: Holder, state: VoiceBroadcastPlayer.State) {
         with(holder) {
+            bufferingView.isVisible = state == VoiceBroadcastPlayer.State.BUFFERING
+            playPauseButton.isVisible = state != VoiceBroadcastPlayer.State.BUFFERING
+            liveIndicator.isVisible = false
+//            liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
+
             when (state) {
-                VoiceBroadcastRecorder.State.Recording -> {
-                    stopRecordButton.isEnabled = true
-
-                    liveIndicator.isVisible = true
-                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
-
-                    val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
-                    val drawable = drawableProvider.getDrawable(R.drawable.ic_play_pause_pause, drawableColor)
-                    recordButton.setImageDrawable(drawable)
-                    recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_pause_voice_broadcast_record)
-                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
-                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+                VoiceBroadcastPlayer.State.PLAYING -> {
+                    playPauseButton.setImageResource(R.drawable.ic_play_pause_pause)
+                    playPauseButton.contentDescription = view.resources.getString(R.string.a11y_play_voice_broadcast)
+                    playPauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.Pause) }
                 }
-                VoiceBroadcastRecorder.State.Paused -> {
-                    stopRecordButton.isEnabled = true
-
-                    liveIndicator.isVisible = true
-                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
-
-                    recordButton.setImageResource(R.drawable.ic_recording_dot)
-                    recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_resume_voice_broadcast_record)
-                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
-                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
-                }
-                VoiceBroadcastRecorder.State.Idle -> {
-                    recordButton.isEnabled = false
-                    stopRecordButton.isEnabled = false
-                    liveIndicator.isVisible = false
+                VoiceBroadcastPlayer.State.IDLE,
+                VoiceBroadcastPlayer.State.PAUSED -> {
+                    playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
+                    playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast)
+                    playPauseButton.setOnClickListener {
+                        attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId))
+                    }
                 }
+                VoiceBroadcastPlayer.State.BUFFERING -> Unit
             }
         }
     }
 
+    private fun renderInactiveMedia(holder: Holder) {
+        with(holder) {
+            liveIndicator.isVisible = false
+            bufferingView.isVisible = false
+            playPauseButton.isVisible = true
+            playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
+            playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast)
+            playPauseButton.setOnClickListener {
+                attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId))
+            }
+        }
+    }
+
+    private fun isCurrentMediaActive() = voiceBroadcastPlayer?.currentVoiceBroadcastId == voiceBroadcastId
+
     override fun unbind(holder: Holder) {
         super.unbind(holder)
-        voiceBroadcastRecorder?.removeListener(recorderListener)
+        voiceBroadcastPlayer?.removeListener(playerListener)
     }
 
     override fun getViewStubId() = STUB_ID
@@ -127,8 +142,8 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
         val liveIndicator by bind<TextView>(R.id.liveIndicator)
         val roomAvatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
         val titleText by bind<TextView>(R.id.titleText)
-        val recordButton by bind<ImageButton>(R.id.recordButton)
-        val stopRecordButton by bind<ImageButton>(R.id.stopRecordButton)
+        val playPauseButton by bind<ImageButton>(R.id.playPauseButton)
+        val bufferingView by bind<View>(R.id.bufferingView)
     }
 
     companion object {
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 62252570c6..9c118f957d 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -73,15 +73,17 @@ class VoiceBroadcastPlayer @Inject constructor(
     private var currentSequence: Int? = null
 
     private var playlist = emptyList<MessageAudioEvent>()
-    private val currentVoiceBroadcastId
+    val currentVoiceBroadcastId
         get() = playlist.firstOrNull()?.root?.getRelationContent()?.eventId
 
     private var state: State = State.IDLE
         set(value) {
             Timber.w("## VoiceBroadcastPlayer state: $field -> $value")
             field = value
+            listeners.forEach { it.onStateChanged(value) }
         }
     private var currentRoomId: String? = null
+    private var listeners = mutableListOf<Listener>()
 
     fun playOrResume(roomId: String, eventId: String) {
         val hasChanged = currentVoiceBroadcastId != eventId
@@ -128,6 +130,15 @@ class VoiceBroadcastPlayer @Inject constructor(
         currentRoomId = null
     }
 
+    fun addListener(listener: Listener) {
+        listeners.add(listener)
+        listener.onStateChanged(state)
+    }
+
+    fun removeListener(listener: Listener) {
+        listeners.remove(listener)
+    }
+
     private fun startPlayback(roomId: String, eventId: String) {
         val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
         currentRoomId = roomId
@@ -316,4 +327,8 @@ class VoiceBroadcastPlayer @Inject constructor(
         BUFFERING,
         IDLE
     }
+
+    fun interface Listener {
+        fun onStateChanged(state: State)
+    }
 }
diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
index 6773280ba5..506ca4ff47 100644
--- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
@@ -65,29 +65,27 @@
         app:constraint_referenced_ids="roomAvatarImageView,titleText" />
 
     <ImageButton
-        android:id="@+id/recordButton"
+        android:id="@+id/playPauseButton"
         android:layout_width="@dimen/voice_broadcast_controller_button_size"
         android:layout_height="@dimen/voice_broadcast_controller_button_size"
         android:background="@drawable/bg_rounded_button"
         android:backgroundTint="?vctr_system"
-        android:contentDescription="@string/a11y_resume_voice_broadcast_record"
-        android:src="@drawable/ic_recording_dot"
+        android:contentDescription="@string/a11y_play_voice_broadcast"
+        android:src="@drawable/ic_play_pause_play"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@id/stopRecordButton"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier"
+        app:tint="?vctr_content_secondary" />
+
+    <com.google.android.material.progressindicator.CircularProgressIndicator
+        android:id="@+id/bufferingView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:contentDescription="@string/a11y_voice_broadcast_buffering"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@id/playPauseButton"
         app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" />
 
-    <ImageButton
-        android:id="@+id/stopRecordButton"
-        android:layout_width="@dimen/voice_broadcast_controller_button_size"
-        android:layout_height="@dimen/voice_broadcast_controller_button_size"
-        android:background="@drawable/bg_rounded_button"
-        android:backgroundTint="?vctr_system"
-        android:contentDescription="@string/a11y_stop_voice_broadcast_record"
-        android:src="@drawable/ic_stop"
-        app:layout_constraintBottom_toBottomOf="@id/recordButton"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toEndOf="@id/recordButton"
-        app:layout_constraintTop_toTopOf="@id/recordButton" />
-
 </androidx.constraintlayout.widget.ConstraintLayout>

From cebc096ac70dad17ebef02f5cc3882ae2b5e93d2 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 21:06:27 +0200
Subject: [PATCH 382/400] VoiceBroadcast - Update live indicator icon

---
 .../factory/VoiceBroadcastItemFactory.kt      |  4 ++-
 .../MessageVoiceBroadcastListeningItem.kt     | 28 +++++++++++++++++--
 .../MessageVoiceBroadcastRecordingItem.kt     |  2 +-
 3 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index af14d532c8..5a70f66d75 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -64,7 +64,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
         return if (isRecording) {
             createRecordingItem(params.event.roomId, highlight, callback, attributes)
         } else {
-            createListeningItem(params.event.roomId, eventsGroup.groupId, highlight, callback, attributes)
+            createListeningItem(params.event.roomId, eventsGroup.groupId, mostRecentMessageContent.voiceBroadcastState, highlight, callback, attributes)
         }
     }
 
@@ -89,6 +89,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
     private fun createListeningItem(
             roomId: String,
             voiceBroadcastId: String,
+            voiceBroadcastState: VoiceBroadcastState?,
             highlight: Boolean,
             callback: TimelineEventController.Callback?,
             attributes: AbsMessageItem.Attributes,
@@ -102,6 +103,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
                 .drawableProvider(drawableProvider)
                 .voiceBroadcastPlayer(voiceBroadcastPlayer)
                 .voiceBroadcastId(voiceBroadcastId)
+                .voiceBroadcastState(voiceBroadcastState)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .callback(callback)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
index 3a090b0eb6..bf0f202f0c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
@@ -24,11 +24,13 @@ 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.extensions.tintBackground
 import im.vector.app.core.resources.ColorProvider
 import im.vector.app.core.resources.DrawableProvider
 import im.vector.app.features.home.room.detail.RoomDetailAction
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
 import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer
+import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import org.matrix.android.sdk.api.util.MatrixItem
 
 @EpoxyModelClass
@@ -43,6 +45,9 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
     @EpoxyAttribute
     lateinit var voiceBroadcastId: String
 
+    @EpoxyAttribute
+    var voiceBroadcastState: VoiceBroadcastState? = null
+
     @EpoxyAttribute
     lateinit var colorProvider: ColorProvider
 
@@ -70,6 +75,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
         }
         voiceBroadcastPlayer?.addListener(playerListener)
         renderHeader(holder)
+        renderLiveIcon(holder)
     }
 
     private fun renderHeader(holder: Holder) {
@@ -81,6 +87,25 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
         }
     }
 
+    private fun renderLiveIcon(holder: Holder) {
+        with(holder) {
+            when (voiceBroadcastState) {
+                VoiceBroadcastState.STARTED,
+                VoiceBroadcastState.RESUMED -> {
+                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorError))
+                    liveIndicator.isVisible = true
+                }
+                VoiceBroadcastState.PAUSED -> {
+                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))
+                    liveIndicator.isVisible = true
+                }
+                VoiceBroadcastState.STOPPED, null -> {
+                    liveIndicator.isVisible = false
+                }
+            }
+        }
+    }
+
     private fun renderState(holder: Holder, state: VoiceBroadcastPlayer.State) {
         if (isCurrentMediaActive()) {
             renderActiveMedia(holder, state)
@@ -94,8 +119,6 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
         with(holder) {
             bufferingView.isVisible = state == VoiceBroadcastPlayer.State.BUFFERING
             playPauseButton.isVisible = state != VoiceBroadcastPlayer.State.BUFFERING
-            liveIndicator.isVisible = false
-//            liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
 
             when (state) {
                 VoiceBroadcastPlayer.State.PLAYING -> {
@@ -118,7 +141,6 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
 
     private fun renderInactiveMedia(holder: Holder) {
         with(holder) {
-            liveIndicator.isVisible = false
             bufferingView.isVisible = false
             playPauseButton.isVisible = true
             playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
index 30475c1659..ff459e00d2 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
@@ -87,7 +87,7 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem<MessageVoiceB
                     stopRecordButton.isEnabled = true
 
                     liveIndicator.isVisible = true
-                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorOnError))
+                    liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorError))
 
                     val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)
                     val drawable = drawableProvider.getDrawable(R.drawable.ic_play_pause_pause, drawableColor)

From 9c5b6d8207ff3b5f94aa5bb44f0ab848aee5e7b6 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 21:11:00 +0200
Subject: [PATCH 383/400] Fix null voiceBroadcastId when the playlist is empty

---
 .../features/voicebroadcast/VoiceBroadcastPlayer.kt    | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 9c118f957d..28ed7a58b8 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -30,7 +30,6 @@ import kotlinx.coroutines.Job
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.launch
 import org.matrix.android.sdk.api.session.events.model.RelationType
-import org.matrix.android.sdk.api.session.events.model.getRelationContent
 import org.matrix.android.sdk.api.session.getRoom
 import org.matrix.android.sdk.api.session.room.Room
 import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
@@ -73,8 +72,7 @@ class VoiceBroadcastPlayer @Inject constructor(
     private var currentSequence: Int? = null
 
     private var playlist = emptyList<MessageAudioEvent>()
-    val currentVoiceBroadcastId
-        get() = playlist.firstOrNull()?.root?.getRelationContent()?.eventId
+    var currentVoiceBroadcastId: String? = null
 
     private var state: State = State.IDLE
         set(value) {
@@ -128,6 +126,7 @@ class VoiceBroadcastPlayer @Inject constructor(
         playlist = emptyList()
         currentSequence = null
         currentRoomId = null
+        currentVoiceBroadcastId = null
     }
 
     fun addListener(listener: Listener) {
@@ -141,11 +140,12 @@ class VoiceBroadcastPlayer @Inject constructor(
 
     private fun startPlayback(roomId: String, eventId: String) {
         val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId")
-        currentRoomId = roomId
-
         // Stop listening previous voice broadcast if any
         if (state != State.IDLE) stop()
 
+        currentRoomId = roomId
+        currentVoiceBroadcastId = eventId
+
         state = State.BUFFERING
 
         val voiceBroadcastState = getVoiceBroadcastUseCase.execute(roomId, eventId)?.content?.voiceBroadcastState

From 6ff7a7f3aeb43bd8819d3d53d3a20125eada8125 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 21:20:17 +0200
Subject: [PATCH 384/400] Update buffering view

---
 ...item_timeline_event_voice_broadcast_listening_stub.xml | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
index 506ca4ff47..6a4fcef77a 100644
--- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
@@ -78,14 +78,18 @@
         app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier"
         app:tint="?vctr_content_secondary" />
 
-    <com.google.android.material.progressindicator.CircularProgressIndicator
+
+    <ProgressBar
         android:id="@+id/bufferingView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:contentDescription="@string/a11y_voice_broadcast_buffering"
+        android:indeterminate="true"
+        android:indeterminateTint="?vctr_content_secondary"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toEndOf="@id/playPauseButton"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" />
 
+
 </androidx.constraintlayout.widget.ConstraintLayout>

From 72a1acec8959a0db909e529f0e7ac585d537cf2f Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 21:26:39 +0200
Subject: [PATCH 385/400] Fix voice broadcast state update on wrong thread

---
 .../app/features/voicebroadcast/VoiceBroadcastPlayer.kt      | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index 28ed7a58b8..bb5fe5c37d 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -18,6 +18,7 @@ package im.vector.app.features.voicebroadcast
 
 import android.media.AudioAttributes
 import android.media.MediaPlayer
+import androidx.annotation.MainThread
 import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
 import im.vector.app.features.voice.VoiceFailure
@@ -29,6 +30,7 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import org.matrix.android.sdk.api.session.events.model.RelationType
 import org.matrix.android.sdk.api.session.getRoom
 import org.matrix.android.sdk.api.session.room.Room
@@ -75,6 +77,7 @@ class VoiceBroadcastPlayer @Inject constructor(
     var currentVoiceBroadcastId: String? = null
 
     private var state: State = State.IDLE
+        @MainThread
         set(value) {
             Timber.w("## VoiceBroadcastPlayer state: $field -> $value")
             field = value
@@ -168,7 +171,7 @@ class VoiceBroadcastPlayer @Inject constructor(
                 currentMediaPlayer?.start()
                 currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) }
                 currentSequence = sequence
-                state = State.PLAYING
+                withContext(Dispatchers.Main) { state = State.PLAYING }
                 nextMediaPlayer = prepareNextMediaPlayer()
             } catch (failure: Throwable) {
                 Timber.e(failure, "Unable to start playback")

From 930c85672866c5d83c47795656d40c8b740639e6 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 22:38:02 +0200
Subject: [PATCH 386/400] Add additional information in listening tile

---
 .../factory/VoiceBroadcastItemFactory.kt      | 14 ++++-
 .../MessageVoiceBroadcastListeningItem.kt     |  6 +-
 ...dcast_16.xml => ic_voice_broadcast_16.xml} |  0
 ...e_event_voice_broadcast_listening_stub.xml | 60 +++++++++++++++++--
 ...e_event_voice_broadcast_recording_stub.xml |  2 +-
 5 files changed, 75 insertions(+), 7 deletions(-)
 rename vector/src/main/res/drawable/{ic_live_broadcast_16.xml => ic_voice_broadcast_16.xml} (100%)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index 5a70f66d75..98f45cd5db 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -34,6 +34,7 @@ import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
 import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
 import org.matrix.android.sdk.api.session.Session
 import org.matrix.android.sdk.api.session.getRoom
+import org.matrix.android.sdk.api.session.getUser
 import org.matrix.android.sdk.api.util.toMatrixItem
 import javax.inject.Inject
 
@@ -61,10 +62,19 @@ class VoiceBroadcastItemFactory @Inject constructor(
         val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()
         val mostRecentMessageContent = mostRecentEvent?.content ?: return null
         val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId
+        val recorderName = mostRecentTimelineEvent.root.stateKey?.let {  session.getUser(it) }?.displayName ?: mostRecentTimelineEvent.root.stateKey
         return if (isRecording) {
             createRecordingItem(params.event.roomId, highlight, callback, attributes)
         } else {
-            createListeningItem(params.event.roomId, eventsGroup.groupId, mostRecentMessageContent.voiceBroadcastState, highlight, callback, attributes)
+            createListeningItem(
+                    params.event.roomId,
+                    eventsGroup.groupId,
+                    mostRecentMessageContent.voiceBroadcastState,
+                    recorderName,
+                    highlight,
+                    callback,
+                    attributes
+            )
         }
     }
 
@@ -90,6 +100,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
             roomId: String,
             voiceBroadcastId: String,
             voiceBroadcastState: VoiceBroadcastState?,
+            broadcasterName: String?,
             highlight: Boolean,
             callback: TimelineEventController.Callback?,
             attributes: AbsMessageItem.Attributes,
@@ -104,6 +115,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
                 .voiceBroadcastPlayer(voiceBroadcastPlayer)
                 .voiceBroadcastId(voiceBroadcastId)
                 .voiceBroadcastState(voiceBroadcastState)
+                .broadcasterName(broadcasterName)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .callback(callback)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
index bf0f202f0c..130d44202f 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
@@ -48,6 +48,9 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
     @EpoxyAttribute
     var voiceBroadcastState: VoiceBroadcastState? = null
 
+    @EpoxyAttribute
+    var broadcasterName: String? = null
+
     @EpoxyAttribute
     lateinit var colorProvider: ColorProvider
 
@@ -84,6 +87,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
                 attributes.avatarRenderer.render(it, roomAvatarImageView)
                 titleText.text = it.displayName
             }
+            broadcasterNameText.text = broadcasterName
         }
     }
 
@@ -114,7 +118,6 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
         }
     }
 
-    @Suppress("UNUSED_PARAMETER")
     private fun renderActiveMedia(holder: Holder, state: VoiceBroadcastPlayer.State) {
         with(holder) {
             bufferingView.isVisible = state == VoiceBroadcastPlayer.State.BUFFERING
@@ -166,6 +169,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
         val titleText by bind<TextView>(R.id.titleText)
         val playPauseButton by bind<ImageButton>(R.id.playPauseButton)
         val bufferingView by bind<View>(R.id.bufferingView)
+        val broadcasterNameText by bind<TextView>(R.id.broadcasterNameText)
     }
 
     companion object {
diff --git a/vector/src/main/res/drawable/ic_live_broadcast_16.xml b/vector/src/main/res/drawable/ic_voice_broadcast_16.xml
similarity index 100%
rename from vector/src/main/res/drawable/ic_live_broadcast_16.xml
rename to vector/src/main/res/drawable/ic_voice_broadcast_16.xml
diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
index 6a4fcef77a..73acde2942 100644
--- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
@@ -24,7 +24,7 @@
         android:singleLine="true"
         android:text="@string/voice_broadcast_live"
         android:textColor="?colorOnError"
-        app:drawableStartCompat="@drawable/ic_live_broadcast_16"
+        app:drawableStartCompat="@drawable/ic_voice_broadcast_16"
         app:drawableTint="?colorOnError"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
@@ -36,7 +36,7 @@
         android:contentDescription="@string/avatar"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@sample/user_round_avatars" />
+        tools:src="@sample/room_round_avatars" />
 
     <androidx.constraintlayout.widget.Barrier
         android:id="@+id/avatarRightBarrier"
@@ -54,7 +54,59 @@
         android:contentDescription="@string/avatar"
         app:layout_constraintStart_toEndOf="@id/avatarRightBarrier"
         app:layout_constraintTop_toTopOf="parent"
-        tools:src="@sample/users.json/data/displayName" />
+        tools:src="@sample/rooms.json/data/name" />
+
+    <LinearLayout
+        android:id="@+id/broadcasterViewGroup"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        app:layout_constraintStart_toEndOf="@id/avatarRightBarrier"
+        app:layout_constraintTop_toBottomOf="@id/titleText">
+
+        <ImageView
+            android:id="@+id/broadcasterIcon"
+            android:layout_width="16dp"
+            android:layout_height="16dp"
+            android:layout_marginEnd="5dp"
+            android:src="@drawable/ic_microphone"
+            app:tint="?vctr_content_secondary" />
+
+        <TextView
+            android:id="@+id/broadcasterNameText"
+            style="@style/Widget.Vector.TextView.Caption"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            tools:text="@sample/users.json/data/displayName" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/voiceBroadcastViewGroup"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        app:layout_constraintStart_toEndOf="@id/avatarRightBarrier"
+        app:layout_constraintTop_toBottomOf="@id/broadcasterViewGroup">
+
+        <ImageView
+            android:id="@+id/voiceBroadcastIcon"
+            android:layout_width="16dp"
+            android:layout_height="16dp"
+            android:layout_marginEnd="5dp"
+            android:src="@drawable/ic_voice_broadcast_16"
+            app:tint="?vctr_content_secondary" />
+
+        <TextView
+            android:id="@+id/voiceBroadcastText"
+            style="@style/Widget.Vector.TextView.Caption"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/attachment_type_voice_broadcast" />
+    </LinearLayout>
 
     <androidx.constraintlayout.widget.Barrier
         android:id="@+id/headerBottomBarrier"
@@ -62,7 +114,7 @@
         android:layout_height="wrap_content"
         app:barrierDirection="bottom"
         app:barrierMargin="12dp"
-        app:constraint_referenced_ids="roomAvatarImageView,titleText" />
+        app:constraint_referenced_ids="roomAvatarImageView,titleText,broadcasterViewGroup,voiceBroadcastViewGroup" />
 
     <ImageButton
         android:id="@+id/playPauseButton"
diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml
index 6773280ba5..e3bb85138d 100644
--- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_recording_stub.xml
@@ -24,7 +24,7 @@
         android:singleLine="true"
         android:text="@string/voice_broadcast_live"
         android:textColor="?colorOnError"
-        app:drawableStartCompat="@drawable/ic_live_broadcast_16"
+        app:drawableStartCompat="@drawable/ic_voice_broadcast_16"
         app:drawableTint="?colorOnError"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent" />

From 8869d82dd0f7a3e2aa232456f7cf324daaf04d16 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 22:39:27 +0200
Subject: [PATCH 387/400] Add changelog

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

diff --git a/changelog.d/7421.wip b/changelog.d/7421.wip
new file mode 100644
index 0000000000..4a399eee04
--- /dev/null
+++ b/changelog.d/7421.wip
@@ -0,0 +1 @@
+[Voice Broadcast] Improve rendering in the timeline

From 9a96de4f06f60ed47261f9cb34b743db4e169918 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 22:45:22 +0200
Subject: [PATCH 388/400] Set id to VoiceBroadcast items

---
 .../timeline/factory/VoiceBroadcastItemFactory.kt   | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
index 98f45cd5db..5dc601a91a 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt
@@ -62,9 +62,15 @@ class VoiceBroadcastItemFactory @Inject constructor(
         val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent()
         val mostRecentMessageContent = mostRecentEvent?.content ?: return null
         val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId
-        val recorderName = mostRecentTimelineEvent.root.stateKey?.let {  session.getUser(it) }?.displayName ?: mostRecentTimelineEvent.root.stateKey
+        val recorderName = mostRecentTimelineEvent.root.stateKey?.let { session.getUser(it) }?.displayName ?: mostRecentTimelineEvent.root.stateKey
         return if (isRecording) {
-            createRecordingItem(params.event.roomId, highlight, callback, attributes)
+            createRecordingItem(
+                    params.event.roomId,
+                    eventsGroup.groupId,
+                    highlight,
+                    callback,
+                    attributes
+            )
         } else {
             createListeningItem(
                     params.event.roomId,
@@ -80,12 +86,14 @@ class VoiceBroadcastItemFactory @Inject constructor(
 
     private fun createRecordingItem(
             roomId: String,
+            voiceBroadcastId: String,
             highlight: Boolean,
             callback: TimelineEventController.Callback?,
             attributes: AbsMessageItem.Attributes,
     ): MessageVoiceBroadcastRecordingItem {
         val roomSummary = session.getRoom(roomId)?.roomSummary()
         return MessageVoiceBroadcastRecordingItem_()
+                .id("voice_broadcast_$voiceBroadcastId")
                 .attributes(attributes)
                 .highlighted(highlight)
                 .roomItem(roomSummary?.toMatrixItem())
@@ -107,6 +115,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
     ): MessageVoiceBroadcastListeningItem {
         val roomSummary = session.getRoom(roomId)?.roomSummary()
         return MessageVoiceBroadcastListeningItem_()
+                .id("voice_broadcast_$voiceBroadcastId")
                 .attributes(attributes)
                 .highlighted(highlight)
                 .roomItem(roomSummary?.toMatrixItem())

From f2cc08263fa59f3ca58b3ac41a7508b04aa65117 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 22:48:09 +0200
Subject: [PATCH 389/400] Call onClick instead of setOnClickListener

---
 .../timeline/item/MessageVoiceBroadcastListeningItem.kt  | 7 ++++---
 .../timeline/item/MessageVoiceBroadcastRecordingItem.kt  | 9 +++++----
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
index 130d44202f..5b58dda4e6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt
@@ -24,6 +24,7 @@ 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.epoxy.onClick
 import im.vector.app.core.extensions.tintBackground
 import im.vector.app.core.resources.ColorProvider
 import im.vector.app.core.resources.DrawableProvider
@@ -127,13 +128,13 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
                 VoiceBroadcastPlayer.State.PLAYING -> {
                     playPauseButton.setImageResource(R.drawable.ic_play_pause_pause)
                     playPauseButton.contentDescription = view.resources.getString(R.string.a11y_play_voice_broadcast)
-                    playPauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.Pause) }
+                    playPauseButton.onClick { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.Pause) }
                 }
                 VoiceBroadcastPlayer.State.IDLE,
                 VoiceBroadcastPlayer.State.PAUSED -> {
                     playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
                     playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast)
-                    playPauseButton.setOnClickListener {
+                    playPauseButton.onClick {
                         attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId))
                     }
                 }
@@ -148,7 +149,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem<MessageVoiceB
             playPauseButton.isVisible = true
             playPauseButton.setImageResource(R.drawable.ic_play_pause_play)
             playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast)
-            playPauseButton.setOnClickListener {
+            playPauseButton.onClick {
                 attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId))
             }
         }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
index ff459e00d2..41ef1fac9c 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
@@ -23,6 +23,7 @@ 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.epoxy.onClick
 import im.vector.app.core.extensions.tintBackground
 import im.vector.app.core.resources.ColorProvider
 import im.vector.app.core.resources.DrawableProvider
@@ -93,8 +94,8 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem<MessageVoiceB
                     val drawable = drawableProvider.getDrawable(R.drawable.ic_play_pause_pause, drawableColor)
                     recordButton.setImageDrawable(drawable)
                     recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_pause_voice_broadcast_record)
-                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
-                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+                    recordButton.onClick { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Pause) }
+                    stopRecordButton.onClick { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
                 }
                 VoiceBroadcastRecorder.State.Paused -> {
                     stopRecordButton.isEnabled = true
@@ -104,8 +105,8 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem<MessageVoiceB
 
                     recordButton.setImageResource(R.drawable.ic_recording_dot)
                     recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_resume_voice_broadcast_record)
-                    recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
-                    stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
+                    recordButton.onClick { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) }
+                    stopRecordButton.onClick { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) }
                 }
                 VoiceBroadcastRecorder.State.Idle -> {
                     recordButton.isEnabled = false

From 4a76998c98bec82af2cbe8e436f7edbd07f7bfaa Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 22:50:09 +0200
Subject: [PATCH 390/400] Use CopyOnWriteArrayList

---
 .../vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt | 3 ++-
 .../app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt     | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
index bb5fe5c37d..2c892c8306 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt
@@ -41,6 +41,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
 import timber.log.Timber
+import java.util.concurrent.CopyOnWriteArrayList
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -84,7 +85,7 @@ class VoiceBroadcastPlayer @Inject constructor(
             listeners.forEach { it.onStateChanged(value) }
         }
     private var currentRoomId: String? = null
-    private var listeners = mutableListOf<Listener>()
+    private var listeners = CopyOnWriteArrayList<Listener>()
 
     fun playOrResume(roomId: String, eventId: String) {
         val hasChanged = currentVoiceBroadcastId != eventId
diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
index cd1a61b986..5285dc5e3b 100644
--- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
+++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt
@@ -23,6 +23,7 @@ import androidx.annotation.RequiresApi
 import im.vector.app.features.voice.AbstractVoiceRecorderQ
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.content.ContentAttachmentData
+import java.util.concurrent.CopyOnWriteArrayList
 
 @RequiresApi(Build.VERSION_CODES.Q)
 class VoiceBroadcastRecorderQ(
@@ -38,7 +39,7 @@ class VoiceBroadcastRecorderQ(
             listeners.forEach { it.onStateUpdated(value) }
         }
 
-    private val listeners = mutableListOf<VoiceBroadcastRecorder.Listener>()
+    private val listeners = CopyOnWriteArrayList<VoiceBroadcastRecorder.Listener>()
 
     override val outputFormat = MediaRecorder.OutputFormat.MPEG_4
     override val audioEncoder = MediaRecorder.AudioEncoder.HE_AAC

From 34cafa373f5f29a65d3d4e3318bfe0273396c149 Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 23:43:33 +0200
Subject: [PATCH 391/400] Add missing content description

---
 .../item_timeline_event_voice_broadcast_listening_stub.xml      | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
index 73acde2942..248c04a2f6 100644
--- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml
@@ -71,6 +71,7 @@
             android:layout_width="16dp"
             android:layout_height="16dp"
             android:layout_marginEnd="5dp"
+            android:contentDescription="@null"
             android:src="@drawable/ic_microphone"
             app:tint="?vctr_content_secondary" />
 
@@ -97,6 +98,7 @@
             android:layout_width="16dp"
             android:layout_height="16dp"
             android:layout_marginEnd="5dp"
+            android:contentDescription="@null"
             android:src="@drawable/ic_voice_broadcast_16"
             app:tint="?vctr_content_secondary" />
 

From 926f4d920180e40905f253d00df93aef714793ae Mon Sep 17 00:00:00 2001
From: Florian Renaud <florianr@element.io>
Date: Thu, 20 Oct 2022 23:58:17 +0200
Subject: [PATCH 392/400] Fix play/pause button disabled

---
 .../detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
index 41ef1fac9c..c417053b2a 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt
@@ -86,6 +86,7 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem<MessageVoiceB
             when (state) {
                 VoiceBroadcastRecorder.State.Recording -> {
                     stopRecordButton.isEnabled = true
+                    recordButton.isEnabled = true
 
                     liveIndicator.isVisible = true
                     liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorError))
@@ -99,6 +100,7 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem<MessageVoiceB
                 }
                 VoiceBroadcastRecorder.State.Paused -> {
                     stopRecordButton.isEnabled = true
+                    recordButton.isEnabled = true
 
                     liveIndicator.isVisible = true
                     liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary))

From 8a4d918b250325949c2bb6c0a06bc99c586a7bf9 Mon Sep 17 00:00:00 2001
From: Kat Gerasimova <ekaterinag@element.io>
Date: Fri, 21 Oct 2022 09:59:40 +0100
Subject: [PATCH 393/400] Update triage-labelled.yml

---
 .github/workflows/triage-labelled.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml
index 18ce26171c..34e36a55da 100644
--- a/.github/workflows/triage-labelled.yml
+++ b/.github/workflows/triage-labelled.yml
@@ -52,8 +52,8 @@ jobs:
       (contains(github.event.issue.labels.*.name, 'S-Critical') &&
        (contains(github.event.issue.labels.*.name, 'O-Frequent') ||
         contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
-       contains(github.event.issue.labels.*.name, 'S-Major') &&
-       contains(github.event.issue.labels.*.name, 'O-Frequent') ||
+       (contains(github.event.issue.labels.*.name, 'S-Major') &&
+        contains(github.event.issue.labels.*.name, 'O-Frequent')) ||
        contains(github.event.issue.labels.*.name, 'A11y'))
     steps:
       - uses: octokit/graphql-action@v2.x

From 113d0ff5400860337deb5786c105cfeba655363e Mon Sep 17 00:00:00 2001
From: Nikita Fedrunov <66663241+fedrunov@users.noreply.github.com>
Date: Fri, 21 Oct 2022 15:06:20 +0200
Subject: [PATCH 394/400] =?UTF-8?q?thread=20read=20receipts=20and=20unread?=
 =?UTF-8?q?=20notifications=20support=20is=20added=20to=20hom=E2=80=A6=20(?=
 =?UTF-8?q?#7386)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../homeserver/HomeServerCapabilities.kt      |  5 +++
 .../auth/version/HomeServerVersion.kt         |  1 +
 .../sdk/internal/auth/version/Versions.kt     | 11 ++++++
 .../database/RealmSessionStoreMigration.kt    |  4 ++-
 .../mapper/HomeServerCapabilitiesMapper.kt    |  1 +
 .../database/migration/MigrateSessionTo040.kt | 34 +++++++++++++++++++
 .../model/HomeServerCapabilitiesEntity.kt     |  1 +
 .../GetHomeServerCapabilitiesTask.kt          |  3 ++
 8 files changed, 59 insertions(+), 1 deletion(-)
 create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
index 8c14ca892a..773e870ffd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt
@@ -65,6 +65,11 @@ data class HomeServerCapabilities(
          * True if the home server supports login via qr code, false otherwise.
          */
         val canLoginWithQrCode: Boolean = false,
+
+        /**
+         * True if the home server supports threaded read receipts and unread notifications.
+         */
+        val canUseThreadReadReceiptsAndNotifications: Boolean = false,
 ) {
 
     enum class RoomCapabilitySupport {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt
index 75639c6a21..d443d6e3c8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt
@@ -60,5 +60,6 @@ internal data class HomeServerVersion(
         val r0_6_0 = HomeServerVersion(major = 0, minor = 6, patch = 0)
         val r0_6_1 = HomeServerVersion(major = 0, minor = 6, patch = 1)
         val v1_3_0 = HomeServerVersion(major = 1, minor = 3, patch = 0)
+        val v1_4_0 = HomeServerVersion(major = 1, minor = 4, patch = 0)
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt
index 5e133fab9c..1245d8df4b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt
@@ -54,6 +54,8 @@ private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
 private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
 private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
 private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
+private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771"
+private const val FEATURE_THREADS_MSC3773 = "org.matrix.msc3773"
 
 /**
  * Return true if the SDK supports this homeserver version.
@@ -79,6 +81,15 @@ internal fun Versions.doesServerSupportThreads(): Boolean {
     return unstableFeatures?.get(FEATURE_THREADS_MSC3440_STABLE) ?: false
 }
 
+/**
+ * Indicate if the homeserver support MSC3771 and MSC3773 for threaded read receipts and unread notifications.
+ */
+internal fun Versions.doesServerSupportThreadUnreadNotifications(): Boolean {
+    val msc3771 = unstableFeatures?.get(FEATURE_THREADS_MSC3771) ?: false
+    val msc3773 = unstableFeatures?.get(FEATURE_THREADS_MSC3773) ?: false
+    return getMaxVersion() >= HomeServerVersion.v1_4_0 || (msc3771 && msc3773)
+}
+
 internal fun Versions.doesServerSupportQrCodeLogin(): Boolean {
     return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
index 9a2c32f97c..def0f6de7a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
@@ -56,6 +56,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038
 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo039
+import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo040
 import org.matrix.android.sdk.internal.util.Normalizer
 import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
 import javax.inject.Inject
@@ -64,7 +65,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
         private val normalizer: Normalizer
 ) : MatrixRealmMigration(
         dbName = "Session",
-        schemaVersion = 39L,
+        schemaVersion = 40L,
 ) {
     /**
      * Forces all RealmSessionStoreMigration instances to be equal.
@@ -113,5 +114,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
         if (oldVersion < 37) MigrateSessionTo037(realm).perform()
         if (oldVersion < 38) MigrateSessionTo038(realm).perform()
         if (oldVersion < 39) MigrateSessionTo039(realm).perform()
+        if (oldVersion < 40) MigrateSessionTo040(realm).perform()
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
index 63fa101c45..3528ca0051 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt
@@ -45,6 +45,7 @@ internal object HomeServerCapabilitiesMapper {
                 canUseThreading = entity.canUseThreading,
                 canControlLogoutDevices = entity.canControlLogoutDevices,
                 canLoginWithQrCode = entity.canLoginWithQrCode,
+                canUseThreadReadReceiptsAndNotifications = entity.canUseThreadReadReceiptsAndNotifications
         )
     }
 
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt
new file mode 100644
index 0000000000..b3e02342dd
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 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.database.migration
+
+import io.realm.DynamicRealm
+import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
+import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities
+import org.matrix.android.sdk.internal.util.database.RealmMigrator
+
+internal class MigrateSessionTo040(realm: DynamicRealm) : RealmMigrator(realm, 40) {
+
+    override fun doMigrate(realm: DynamicRealm) {
+        realm.schema.get("HomeServerCapabilitiesEntity")
+                ?.addField(HomeServerCapabilitiesEntityFields.CAN_USE_THREAD_READ_RECEIPTS_AND_NOTIFICATIONS, Boolean::class.java)
+                ?.transform { obj ->
+                    obj.set(HomeServerCapabilitiesEntityFields.CAN_USE_THREAD_READ_RECEIPTS_AND_NOTIFICATIONS, false)
+                }
+                ?.forceRefreshOfHomeServerCapabilities()
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
index cfa02b2c74..89f1e50b30 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt
@@ -32,6 +32,7 @@ internal open class HomeServerCapabilitiesEntity(
         var canUseThreading: Boolean = false,
         var canControlLogoutDevices: Boolean = false,
         var canLoginWithQrCode: Boolean = false,
+        var canUseThreadReadReceiptsAndNotifications: Boolean = false,
 ) : RealmObject() {
 
     companion object
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
index 2c3cb440b6..a5953d870c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt
@@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.internal.auth.version.Versions
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
+import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreadUnreadNotifications
 import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads
 import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
 import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
@@ -144,6 +145,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
                 homeServerCapabilitiesEntity.canControlLogoutDevices = getVersionResult.doesServerSupportLogoutDevices()
                 homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
                         getVersionResult.doesServerSupportThreads()
+                homeServerCapabilitiesEntity.canUseThreadReadReceiptsAndNotifications =
+                        getVersionResult.doesServerSupportThreadUnreadNotifications()
                 homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult.doesServerSupportQrCodeLogin()
             }
 

From 31811bb7e0eff83b1fcabc1cd27298ebb121bc5e Mon Sep 17 00:00:00 2001
From: jonnyandrew <jonny.andrew@protonmail.com>
Date: Fri, 21 Oct 2022 17:36:31 +0100
Subject: [PATCH 395/400] Fix crash by disabling Flipper on API 22 and below
 (#7428)

* Disable Flipper on API 21 and below - only affects debug builds.

Due to a bug: https://github.com/facebook/flipper/issues/3572

* Add jonnyandrew to PR sign-off allowlist

Co-authored-by: Benoit Marty <benoit.marty@gmail.com>
---
 changelog.d/7428.bugfix                       |  1 +
 tools/danger/dangerfile.js                    |  1 +
 .../vector/app/flipper/VectorFlipperProxy.kt  | 56 ++++++++++++-------
 3 files changed, 38 insertions(+), 20 deletions(-)
 create mode 100644 changelog.d/7428.bugfix

diff --git a/changelog.d/7428.bugfix b/changelog.d/7428.bugfix
new file mode 100644
index 0000000000..8f014af31b
--- /dev/null
+++ b/changelog.d/7428.bugfix
@@ -0,0 +1 @@
+Fix crash by disabling Flipper on Android API 22 and below - only affects debug version of the application.
diff --git a/tools/danger/dangerfile.js b/tools/danger/dangerfile.js
index 1a36474470..2fc55bad0c 100644
--- a/tools/danger/dangerfile.js
+++ b/tools/danger/dangerfile.js
@@ -81,6 +81,7 @@ const allowList = [
     "Florian14",
     "ganfra",
     "jmartinesp",
+    "jonnyandrew",
     "langleyd",
     "MadLittleMods",
     "manuroe",
diff --git a/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt b/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt
index 2e4336c942..cbf9e4f11f 100644
--- a/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt
+++ b/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt
@@ -17,6 +17,7 @@
 package im.vector.app.flipper
 
 import android.content.Context
+import android.os.Build
 import com.facebook.flipper.android.AndroidFlipperClient
 import com.facebook.flipper.android.utils.FlipperUtils
 import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin
@@ -31,6 +32,7 @@ import com.kgurgul.flipper.RealmDatabaseDriver
 import com.kgurgul.flipper.RealmDatabaseProvider
 import im.vector.app.core.debug.FlipperProxy
 import io.realm.RealmConfiguration
+import okhttp3.Interceptor
 import org.matrix.android.sdk.api.Matrix
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -41,29 +43,43 @@ class VectorFlipperProxy @Inject constructor(
 ) : FlipperProxy {
     private val networkFlipperPlugin = NetworkFlipperPlugin()
 
+    private val isEnabled: Boolean
+        get() {
+            // https://github.com/facebook/flipper/issues/3572
+            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+                return false
+            }
+
+            return FlipperUtils.shouldEnableFlipper(context)
+        }
+
     override fun init(matrix: Matrix) {
+        if (!isEnabled) return
+
         SoLoader.init(context, false)
 
-        if (FlipperUtils.shouldEnableFlipper(context)) {
-            val client = AndroidFlipperClient.getInstance(context)
-            client.addPlugin(CrashReporterPlugin.getInstance())
-            client.addPlugin(SharedPreferencesFlipperPlugin(context))
-            client.addPlugin(InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()))
-            client.addPlugin(networkFlipperPlugin)
-            client.addPlugin(
-                    DatabasesFlipperPlugin(
-                            RealmDatabaseDriver(
-                                    context = context,
-                                    realmDatabaseProvider = object : RealmDatabaseProvider {
-                                        override fun getRealmConfigurations(): List<RealmConfiguration> {
-                                            return matrix.debugService().getAllRealmConfigurations()
-                                        }
-                                    })
-                    )
-            )
-            client.start()
-        }
+        val client = AndroidFlipperClient.getInstance(context)
+        client.addPlugin(CrashReporterPlugin.getInstance())
+        client.addPlugin(SharedPreferencesFlipperPlugin(context))
+        client.addPlugin(InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()))
+        client.addPlugin(networkFlipperPlugin)
+        client.addPlugin(
+                DatabasesFlipperPlugin(
+                        RealmDatabaseDriver(
+                                context = context,
+                                realmDatabaseProvider = object : RealmDatabaseProvider {
+                                    override fun getRealmConfigurations(): List<RealmConfiguration> {
+                                        return matrix.debugService().getAllRealmConfigurations()
+                                    }
+                                })
+                )
+        )
+        client.start()
     }
 
-    override fun networkInterceptor() = FlipperOkhttpInterceptor(networkFlipperPlugin)
+    override fun networkInterceptor(): Interceptor? {
+        if (!isEnabled) return null
+
+        return FlipperOkhttpInterceptor(networkFlipperPlugin)
+    }
 }

From 59cf20be35000c695a9e5aa39717ae8072205fba Mon Sep 17 00:00:00 2001
From: Nikita Fedrunov <66663241+fedrunov@users.noreply.github.com>
Date: Mon, 24 Oct 2022 11:01:44 +0200
Subject: [PATCH 396/400] added UI tests for new App Layout's screens (#7305)

---
 .../app/espresso/tools/ViewActionsExt.kt      | 44 ++++++++++++++++++
 .../vector/app/ui/UiAllScreensSanityTest.kt   |  8 ++++
 .../im/vector/app/ui/robot/ElementRobot.kt    | 14 ++++++
 .../app/ui/robot/LayoutPreferencesRobot.kt    | 46 +++++++++++++++++++
 .../im/vector/app/ui/robot/RoomListRobot.kt   | 37 +++++++++++++--
 5 files changed, 144 insertions(+), 5 deletions(-)
 create mode 100644 vector-app/src/androidTest/java/im/vector/app/espresso/tools/ViewActionsExt.kt
 create mode 100644 vector-app/src/androidTest/java/im/vector/app/ui/robot/LayoutPreferencesRobot.kt

diff --git a/vector-app/src/androidTest/java/im/vector/app/espresso/tools/ViewActionsExt.kt b/vector-app/src/androidTest/java/im/vector/app/espresso/tools/ViewActionsExt.kt
new file mode 100644
index 0000000000..c1e5c0164b
--- /dev/null
+++ b/vector-app/src/androidTest/java/im/vector/app/espresso/tools/ViewActionsExt.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.espresso.tools
+
+import android.view.View
+import androidx.test.espresso.PerformException
+import androidx.test.espresso.UiController
+import androidx.test.espresso.ViewAction
+import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import com.google.android.material.tabs.TabLayout
+import org.hamcrest.Matchers.allOf
+
+fun selectTabAtPosition(tabIndex: Int): ViewAction {
+    return object : ViewAction {
+        override fun getDescription() = "with tab at index $tabIndex"
+
+        override fun getConstraints() = allOf(isDisplayed(), isAssignableFrom(TabLayout::class.java))
+
+        override fun perform(uiController: UiController, view: View) {
+            val tabLayout = view as TabLayout
+            val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
+                    ?: throw PerformException.Builder()
+                            .withCause(Throwable("No tab at index $tabIndex"))
+                            .build()
+
+            tabAtIndex.select()
+        }
+    }
+}
diff --git a/vector-app/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector-app/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
index d4878b8dcc..52607bd9a1 100644
--- a/vector-app/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
+++ b/vector-app/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt
@@ -135,6 +135,14 @@ class UiAllScreensSanityTest {
 
         elementRobot.space { selectSpace(spaceName) }
 
+        elementRobot.layoutPreferences {
+            crawl()
+        }
+
+        elementRobot.roomList {
+            crawlTabs()
+        }
+
         elementRobot.withDeveloperMode {
             settings {
                 advancedSettings { crawlDeveloperOptions() }
diff --git a/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt b/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
index d9dfb0facf..8f1df52863 100644
--- a/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
+++ b/vector-app/src/androidTest/java/im/vector/app/ui/robot/ElementRobot.kt
@@ -17,8 +17,10 @@
 package im.vector.app.ui.robot
 
 import android.view.View
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.espresso.Espresso.closeSoftKeyboard
 import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
 import androidx.test.espresso.Espresso.pressBack
 import androidx.test.espresso.action.ViewActions
 import androidx.test.espresso.action.ViewActions.click
@@ -94,6 +96,18 @@ class ElementRobot(
         waitUntilViewVisible(withId(R.id.roomListContainer))
     }
 
+    fun layoutPreferences(block: LayoutPreferencesRobot.() -> Unit) {
+        openActionBarOverflowOrOptionsMenu(
+                ApplicationProvider.getApplicationContext()
+        )
+        clickOn(R.string.home_layout_preferences)
+        waitUntilDialogVisible(withId(R.id.home_layout_settings_recents))
+
+        block(LayoutPreferencesRobot())
+
+        pressBack()
+    }
+
     fun newDirectMessage(block: NewDirectMessageRobot.() -> Unit) {
         if (labsPreferences.isNewAppLayoutEnabled) {
             clickOn(R.id.newLayoutCreateChatButton)
diff --git a/vector-app/src/androidTest/java/im/vector/app/ui/robot/LayoutPreferencesRobot.kt b/vector-app/src/androidTest/java/im/vector/app/ui/robot/LayoutPreferencesRobot.kt
new file mode 100644
index 0000000000..429511e10f
--- /dev/null
+++ b/vector-app/src/androidTest/java/im/vector/app/ui/robot/LayoutPreferencesRobot.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.ui.robot
+
+import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
+import im.vector.app.R
+
+class LayoutPreferencesRobot {
+
+    fun crawl() {
+        toggleRecents()
+        toggleFilters()
+        useAZOrderd()
+        useActivityOrder()
+    }
+
+    fun toggleRecents() {
+        clickOn(R.id.home_layout_settings_recents)
+    }
+
+    fun toggleFilters() {
+        clickOn(R.id.home_layout_settings_filters)
+    }
+
+    fun useAZOrderd() {
+        clickOn(R.id.home_layout_settings_sort_name)
+    }
+
+    fun useActivityOrder() {
+        clickOn(R.id.home_layout_settings_sort_activity)
+    }
+}
diff --git a/vector-app/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt b/vector-app/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt
index e4984aeed0..cbc46f15e7 100644
--- a/vector-app/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt
+++ b/vector-app/src/androidTest/java/im/vector/app/ui/robot/RoomListRobot.kt
@@ -21,29 +21,41 @@ import androidx.test.espresso.Espresso.onView
 import androidx.test.espresso.Espresso.pressBack
 import androidx.test.espresso.action.ViewActions
 import androidx.test.espresso.contrib.RecyclerViewActions
-import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
+import androidx.test.espresso.matcher.ViewMatchers.withId
 import androidx.test.espresso.matcher.ViewMatchers.withText
 import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions
 import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
 import im.vector.app.R
+import im.vector.app.espresso.tools.selectTabAtPosition
 import im.vector.app.espresso.tools.waitUntilActivityVisible
 import im.vector.app.espresso.tools.waitUntilDialogVisible
+import im.vector.app.espresso.tools.waitUntilViewVisible
+import im.vector.app.features.home.HomeActivity
+import im.vector.app.features.home.room.list.home.header.HomeRoomFilter
 import im.vector.app.features.roomdirectory.RoomDirectoryActivity
 import im.vector.app.ui.robot.settings.labs.LabFeaturesPreferences
+import im.vector.app.waitForView
 
 class RoomListRobot(private val labsPreferences: LabFeaturesPreferences) {
 
     fun openRoom(roomName: String, block: RoomDetailRobot.() -> Unit) {
-        clickOn(roomName)
+        onView(withId(R.id.roomListView))
+                .perform(
+                        RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
+                                hasDescendant(withText(roomName)),
+                                ViewActions.click()
+                        )
+                )
         block(RoomDetailRobot())
         pressBack()
     }
 
     fun verifyCreatedRoom() {
-        onView(ViewMatchers.withId(R.id.roomListView))
+        onView(withId(R.id.roomListView))
                 .perform(
                         RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
-                                ViewMatchers.hasDescendant(withText(R.string.room_displayname_empty_room)),
+                                hasDescendant(withText(R.string.room_displayname_empty_room)),
                                 ViewActions.longClick()
                         )
                 )
@@ -53,7 +65,7 @@ class RoomListRobot(private val labsPreferences: LabFeaturesPreferences) {
     fun newRoom(block: NewRoomRobot.() -> Unit) {
         if (labsPreferences.isNewAppLayoutEnabled) {
             clickOn(R.id.newLayoutCreateChatButton)
-            waitUntilDialogVisible(ViewMatchers.withId(R.id.create_room))
+            waitUntilDialogVisible(withId(R.id.create_room))
             clickOn(R.id.create_room)
         } else {
             clickOn(R.id.createGroupRoomButton)
@@ -67,4 +79,19 @@ class RoomListRobot(private val labsPreferences: LabFeaturesPreferences) {
             pressBack()
         }
     }
+
+    fun crawlTabs() {
+        waitUntilActivityVisible<HomeActivity> {
+            waitUntilViewVisible(withId(R.id.roomListContainer))
+        }
+
+        selectFilterTab(HomeRoomFilter.UNREADS)
+        waitForView(withId(R.id.emptyTitleView))
+        selectFilterTab(HomeRoomFilter.ALL)
+        waitForView(withId(R.id.roomNameView))
+    }
+
+    fun selectFilterTab(filter: HomeRoomFilter) {
+        onView(withId(R.id.home_filter_tabs_tabs)).perform(selectTabAtPosition(filter.ordinal))
+    }
 }

From c9fee49b98f0c8e4f9f02532810f1e1fd3e1f134 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 24 Oct 2022 23:03:12 +0000
Subject: [PATCH 397/400] Bump alex-page/github-project-automation-plus from
 0.8.1 to 0.8.2

Bumps [alex-page/github-project-automation-plus](https://github.com/alex-page/github-project-automation-plus) from 0.8.1 to 0.8.2.
- [Release notes](https://github.com/alex-page/github-project-automation-plus/releases)
- [Commits](https://github.com/alex-page/github-project-automation-plus/compare/bb266ff4dde9242060e2d5418e120a133586d488...1f8873e97e3c8f58161a323b7c568c1f623a1c4d)

---
updated-dependencies:
- dependency-name: alex-page/github-project-automation-plus
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/triage-incoming.yml      | 2 +-
 .github/workflows/triage-priority-bugs.yml | 4 ++--
 .github/workflows/triage-unlabelled.yml    | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/triage-incoming.yml b/.github/workflows/triage-incoming.yml
index 6a22bf5223..4dadc25ab4 100644
--- a/.github/workflows/triage-incoming.yml
+++ b/.github/workflows/triage-incoming.yml
@@ -10,7 +10,7 @@ jobs:
     # Skip in forks
     if: github.repository == 'vector-im/element-android'
     steps:
-      - uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
+      - uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d
         with:
           project: Issue triage
           column: Incoming
diff --git a/.github/workflows/triage-priority-bugs.yml b/.github/workflows/triage-priority-bugs.yml
index e762102226..07e73fe805 100644
--- a/.github/workflows/triage-priority-bugs.yml
+++ b/.github/workflows/triage-priority-bugs.yml
@@ -24,7 +24,7 @@ jobs:
         contains(github.event.issue.labels.*.name, 'A11y') &&
         contains(github.event.issue.labels.*.name, 'O-Frequent'))
     steps:
-      - uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
+      - uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d
         with:
           project: Android App Team
           column: Important Issues & Topics (P1)
@@ -50,7 +50,7 @@ jobs:
         contains(github.event.issue.labels.*.name, 'A11y') &&
         contains(github.event.issue.labels.*.name, 'O-Frequent')))
     steps:
-      - uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
+      - uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d
         with:
           project: Crypto Team
           column: Ready
diff --git a/.github/workflows/triage-unlabelled.yml b/.github/workflows/triage-unlabelled.yml
index 06df286d09..98d6579958 100644
--- a/.github/workflows/triage-unlabelled.yml
+++ b/.github/workflows/triage-unlabelled.yml
@@ -28,7 +28,7 @@ jobs:
             echo "ALREADY_IN_BOARD=false" >> $GITHUB_ENV
           fi
       - name: Move issue
-        uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
+        uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d
         if: ${{ env.ALREADY_IN_BOARD == 'true' }}
         with:
           project: Issue triage

From 085fbcf2fa5a28f6bf4b4ecaf455e7e0f60a60b3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 24 Oct 2022 23:08:46 +0000
Subject: [PATCH 398/400] Bump fragment from 1.5.3 to 1.5.4

Bumps `fragment` from 1.5.3 to 1.5.4.

Updates `fragment-ktx` from 1.5.3 to 1.5.4

Updates `fragment-testing` from 1.5.3 to 1.5.4

---
updated-dependencies:
- dependency-name: androidx.fragment:fragment-ktx
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: androidx.fragment:fragment-testing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index f081e0a874..23829b7317 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -30,7 +30,7 @@ def vanniktechEmoji = "0.16.0-SNAPSHOT"
 
 def sentry = "6.4.3"
 
-def fragment = "1.5.3"
+def fragment = "1.5.4"
 
 // Testing
 def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819

From 8163d0d47b76796969127b1946006accd82c022d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 24 Oct 2022 23:09:47 +0000
Subject: [PATCH 399/400] Bump kluent-android from 1.68 to 1.72

Bumps [kluent-android](https://github.com/MarkusAmshove/Kluent) from 1.68 to 1.72.
- [Release notes](https://github.com/MarkusAmshove/Kluent/releases)
- [Changelog](https://github.com/MarkusAmshove/Kluent/blob/master/CHANGELOG.md)
- [Commits](https://github.com/MarkusAmshove/Kluent/compare/v1.68...v1.72)

---
updated-dependencies:
- dependency-name: org.amshove.kluent:kluent-android
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index f081e0a874..c86b50d80a 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -167,7 +167,7 @@ ext.libs = [
                 'sentryAndroid'         : "io.sentry:sentry-android:$sentry"
         ],
         tests       : [
-                'kluent'                 : "org.amshove.kluent:kluent-android:1.68",
+                'kluent'                 : "org.amshove.kluent:kluent-android:1.72",
                 'timberJunitRule'        : "net.lachlanmckee:timber-junit-rule:1.0.1",
                 'junit'                  : "junit:junit:4.13.2",
         ]

From b3dcc605d98818670db2cffaacd4848a7083cbfe Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 24 Oct 2022 23:11:23 +0000
Subject: [PATCH 400/400] Bump exifinterface from 1.3.4 to 1.3.5

Bumps exifinterface from 1.3.4 to 1.3.5.

---
updated-dependencies:
- dependency-name: androidx.exifinterface:exifinterface
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 dependencies.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dependencies.gradle b/dependencies.gradle
index f081e0a874..b44808f630 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -55,7 +55,7 @@ ext.libs = [
                 'biometric'               : "androidx.biometric:biometric:1.1.0",
                 'core'                    : "androidx.core:core-ktx:1.9.0",
                 'recyclerview'            : "androidx.recyclerview:recyclerview:1.2.1",
-                'exifinterface'           : "androidx.exifinterface:exifinterface:1.3.4",
+                'exifinterface'           : "androidx.exifinterface:exifinterface:1.3.5",
                 'fragmentKtx'             : "androidx.fragment:fragment-ktx:$fragment",
                 'fragmentTesting'         : "androidx.fragment:fragment-testing:$fragment",
                 'constraintLayout'        : "androidx.constraintlayout:constraintlayout:2.1.4",