From 00bfbe9bc6a3f3c6576cc161d8144dc22d23ba5e Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 7 Oct 2022 11:16:41 -0400 Subject: [PATCH 001/300] Adds API parsing of unread threads notifications --- .../sdk/api/session/sync/model/RoomSync.kt | 6 ++++ .../RoomSyncUnreadThreadNotifications.kt | 33 +++++++++++++++++++ .../internal/session/filter/FilterFactory.kt | 12 ++++--- .../session/filter/RoomEventFilter.kt | 10 ++++-- .../room/summary/RoomSummaryUpdater.kt | 4 +++ .../sync/handler/room/RoomSyncHandler.kt | 4 ++- 6 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncUnreadThreadNotifications.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt index e5ac0a39b23..472121269f7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt @@ -47,6 +47,12 @@ data class RoomSync( */ @Json(name = "unread_notifications") val unreadNotifications: RoomSyncUnreadNotifications? = null, + + /** + * The count of threads with unread notifications (not the total # of notifications in all threads) + */ + @Json(name = "org.matrix.msc3773.unread_thread_notifications") val unreadThreadNotifications: Map? = null, + /** * The room summary. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncUnreadThreadNotifications.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncUnreadThreadNotifications.kt new file mode 100644 index 00000000000..70524d299aa --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncUnreadThreadNotifications.kt @@ -0,0 +1,33 @@ +/* + * 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.sync.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class RoomSyncUnreadThreadNotifications( + /** + * The number of threads with unread messages that match the push notification rules. + */ + @Json(name = "notification_count") val notificationCount: Int? = null, + + /** + * The number of threads with highlighted unread messages (subset of notifications). + */ + @Json(name = "highlight_count") val highlightCount: Int? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt index 676a4f6a38c..141144acd8a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt @@ -28,7 +28,8 @@ internal object FilterFactory { limit = numberOfEvents, // senders = listOf(userId), // relationSenders = userId?.let { listOf(it) }, - relationTypes = listOf(RelationType.THREAD) + relationTypes = listOf(RelationType.THREAD), + enableUnreadThreadNotifications = true, ) } @@ -37,7 +38,8 @@ internal object FilterFactory { limit = numberOfEvents, containsUrl = true, types = listOf(EventType.MESSAGE), - lazyLoadMembers = true + lazyLoadMembers = true, + enableUnreadThreadNotifications = true, ) } @@ -62,7 +64,8 @@ internal object FilterFactory { fun createElementRoomFilter(): RoomEventFilter { return RoomEventFilter( - lazyLoadMembers = true + lazyLoadMembers = true, + enableUnreadThreadNotifications = true, // TODO Enable this for optimization // types = (listOfSupportedEventTypes + listOfSupportedStateEventTypes).toMutableList() ) @@ -77,7 +80,8 @@ internal object FilterFactory { private fun createElementStateFilter(): RoomEventFilter { return RoomEventFilter( - lazyLoadMembers = true + lazyLoadMembers = true, + enableUnreadThreadNotifications = true, ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt index 220c4011375..81d1c042618 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.filter import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.sync.model.RoomSync import org.matrix.android.sdk.internal.di.MoshiProvider /** @@ -76,7 +77,11 @@ internal data class RoomEventFilter( /** * If true, enables lazy-loading of membership events. See Lazy-loading room members for more information. Defaults to false. */ - @Json(name = "lazy_load_members") val lazyLoadMembers: Boolean? = null + @Json(name = "lazy_load_members") val lazyLoadMembers: Boolean? = null, + /** + * If true, this will opt-in for the server to return unread threads notifications in [RoomSync] + */ + @Json(name = "org.matrix.msc3773.unread_thread_notifications") val enableUnreadThreadNotifications: Boolean? = null, ) { fun toJSONString(): String { @@ -92,6 +97,7 @@ internal data class RoomEventFilter( rooms != null || notRooms != null || containsUrl != null || - lazyLoadMembers != null) + lazyLoadMembers != null || + enableUnreadThreadNotifications != null) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 6979d428274..537483193a7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.sync.model.RoomSyncSummary import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications +import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadThreadNotifications import org.matrix.android.sdk.internal.crypto.EventDecryptor import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService import org.matrix.android.sdk.internal.database.mapper.ContentMapper @@ -91,6 +92,7 @@ internal class RoomSummaryUpdater @Inject constructor( membership: Membership? = null, roomSummary: RoomSyncSummary? = null, unreadNotifications: RoomSyncUnreadNotifications? = null, + unreadThreadNotifications: Map? = null, updateMembers: Boolean = false, inviterId: String? = null, aggregator: SyncResponsePostTreatmentAggregator? = null @@ -111,6 +113,8 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.highlightCount = unreadNotifications?.highlightCount ?: 0 roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?: 0 + // TODO: Handle unreadThreadNotifications + if (membership != null) { roomSummaryEntity.membership = membership } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index a2f2251b70e..2825be8291d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -287,6 +287,7 @@ internal class RoomSyncHandler @Inject constructor( Membership.JOIN, roomSync.summary, roomSync.unreadNotifications, + roomSync.unreadThreadNotifications, updateMembers = hasRoomMember, aggregator = aggregator ) @@ -372,7 +373,8 @@ internal class RoomSyncHandler @Inject constructor( roomEntity.chunks.clearWith { it.deleteOnCascade(deleteStateEvents = true, canDeleteRoot = true) } roomTypingUsersHandler.handle(realm, roomId, null) roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.LEAVE) - roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications, aggregator = aggregator) + roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, + roomSync.unreadNotifications, roomSync.unreadThreadNotifications, aggregator = aggregator) return roomEntity } From ebd8461724430f9a51b1927653ab37ea9d62ad95 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 7 Oct 2022 11:42:18 -0400 Subject: [PATCH 002/300] Adds thread notifications and highlights to RoomSummaryEntity --- .../sdk/internal/database/model/RoomSummaryEntity.kt | 10 ++++++++++ .../session/room/summary/RoomSummaryUpdater.kt | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 471bec59afe..650dd3c5cb9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -115,6 +115,16 @@ internal open class RoomSummaryEntity( if (value != field) field = value } + var threadNotificationCount: Int = 0 + set(value) { + if (value != field) field = value + } + + var threadHighlightCount: Int = 0 + set(value) { + if (value != field) field = value + } + var readMarkerId: String? = null set(value) { if (value != field) field = value diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 537483193a7..185b7d35d6f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -113,7 +113,15 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.highlightCount = unreadNotifications?.highlightCount ?: 0 roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?: 0 - // TODO: Handle unreadThreadNotifications + roomSummaryEntity.threadHighlightCount = unreadThreadNotifications + ?.mapNotNull { it.value.highlightCount } + ?.sum() + ?: 0 + roomSummaryEntity.threadNotificationCount = unreadThreadNotifications + ?.mapNotNull { it.value.notificationCount } + ?.sum() + ?: 0 + if (membership != null) { roomSummaryEntity.membership = membership From c2ae75d9bdbdbdf3ba9d180ea24f0c382a16c404 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 7 Oct 2022 11:45:58 -0400 Subject: [PATCH 003/300] Changes thread notifications saved to entity from sum to size --- .../internal/session/room/summary/RoomSummaryUpdater.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 185b7d35d6f..c740e072573 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -114,12 +114,12 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?: 0 roomSummaryEntity.threadHighlightCount = unreadThreadNotifications - ?.mapNotNull { it.value.highlightCount } - ?.sum() + ?.map { it.value.highlightCount.takeIf { count -> (count ?: 0) > 0 } } + ?.size ?: 0 roomSummaryEntity.threadNotificationCount = unreadThreadNotifications - ?.mapNotNull { it.value.notificationCount } - ?.sum() + ?.map { it.value.notificationCount.takeIf { count -> (count ?: 0) > 0 } } + ?.size ?: 0 From a2382c6a0157ada1b8129f906b340bdc7c8893bb Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 7 Oct 2022 11:47:18 -0400 Subject: [PATCH 004/300] Adds thread notification fields to RoomSummary --- .../android/sdk/api/session/room/model/RoomSummary.kt | 8 ++++++++ .../sdk/internal/database/mapper/RoomSummaryMapper.kt | 2 ++ 2 files changed, 10 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt index ff4977491fc..6058564b78a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomSummary.kt @@ -97,6 +97,14 @@ data class RoomSummary( * Number of unread and highlighted message in this room. */ val highlightCount: Int = 0, + /** + * Number of threads with unread messages in this room + */ + val threadNotificationCount: Int = 0, + /** + * Number of threads with highlighted messages in this room + */ + val threadHighlightCount: Int = 0, /** * True if this room has unread messages. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt index 72b0f7a0432..6e9fff78e1c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt @@ -61,6 +61,8 @@ internal class RoomSummaryMapper @Inject constructor( otherMemberIds = roomSummaryEntity.otherMemberIds.toList(), highlightCount = roomSummaryEntity.highlightCount, notificationCount = roomSummaryEntity.notificationCount, + threadHighlightCount = roomSummaryEntity.threadHighlightCount, + threadNotificationCount = roomSummaryEntity.threadNotificationCount, hasUnreadMessages = roomSummaryEntity.hasUnreadMessages, tags = tags, typingUsers = typingUsers, 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 005/300] 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] --- .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 5698a696b6a..30b6600c940 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 1692e2e281a..9d9e8e76e8e 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 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 006/300] 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] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index edff769070b..30bd576c21b 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 007/300] 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] --- vector/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/build.gradle b/vector/build.gradle index c59e1a30281..32579d0ccc6 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 e875d9d329e5131881166465fc31a51d1051541a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 13 Oct 2022 11:52:35 +0200 Subject: [PATCH 008/300] Ensure the latest paparazzi version is used, when updated by Dependabot. Dependabot can update the plugin version, but not the library we add manually. --- build.gradle | 4 ++-- dependencies.gradle | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index e474b685318..486da53e989 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ buildscript { classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10" 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' + classpath libs.squareup.paparazziPlugin // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -322,7 +322,7 @@ ext.initScreenshotTests = { project -> if (hasScreenshots) { project.apply plugin: 'app.cash.paparazzi' } - project.dependencies { testCompileOnly "app.cash.paparazzi:paparazzi:1.0.0" } + project.dependencies { testCompileOnly libs.squareup.paparazzi } project.android.testOptions.unitTests.all { def screenshotTestCapture = "**/*ScreenshotTest*" if (hasScreenshots) { diff --git a/dependencies.gradle b/dependencies.gradle index edff769070b..3b39f72965d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,5 +1,4 @@ ext.versions = [ - 'minSdk' : 21, 'compileSdk' : 32, 'targetSdk' : 32, @@ -27,22 +26,20 @@ def jjwt = "0.11.5" // Temporary version to unblock #6929. Once 0.16.0 is released we should use it, and revert // the whole commit which set version 0.16.0-SNAPSHOT def vanniktechEmoji = "0.16.0-SNAPSHOT" - def sentry = "6.4.3" - def fragment = "1.5.3" - // 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 def espresso = "3.4.0" def androidxTest = "1.4.0" def androidxOrchestrator = "1.4.1" +def paparazzi = "1.1.0" + ext.libs = [ gradle : [ 'gradlePlugin' : "com.android.tools.build:gradle:$gradle", 'kotlinPlugin' : "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin", 'hiltPlugin' : "com.google.dagger:hilt-android-gradle-plugin:$dagger" - ], jetbrains : [ 'coroutinesCore' : "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutines", @@ -108,6 +105,8 @@ ext.libs = [ 'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi", 'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi", 'moshiAdapters' : "com.squareup.moshi:moshi-adapters:$moshi", + 'paparazzi' : "app.cash.paparazzi:paparazzi:$paparazzi", + 'paparazziPlugin' : "app.cash.paparazzi:paparazzi-gradle-plugin:$paparazzi", 'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit", 'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit" ], From 21ae4c6ddbf29bd33f3888788717f0cbd06d74b5 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:11:15 +0100 Subject: [PATCH 009/300] 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 5ae70e1978c..8f2a784d49b 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 627a8256799..991b7b654d5 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 446f9318479..90dc57b4f0b 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 ea8578ed8c5..86467520837 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 5f0a2298cb6..062b2466e55 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, @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 0c6fb45e788..22cc185fa75 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 00000000000..477719f607a --- /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 { + 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, + 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 Date: Thu, 13 Oct 2022 15:18:31 +0100 Subject: [PATCH 010/300] 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 00000000000..3d17076a445 --- /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 Date: Thu, 13 Oct 2022 15:19:39 +0100 Subject: [PATCH 011/300] 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 90dc57b4f0b..7fd730bece4 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 Date: Tue, 11 Oct 2022 23:32:49 +0100 Subject: [PATCH 012/300] Retry scanning if not a QR code From 1c70d455fbfda7a301ed01f1f18eef1ddf2aa902 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 11 Oct 2022 23:33:30 +0100 Subject: [PATCH 013/300] Implementations of MSC3886 and MSC3903 From b192fdb0a89b1aa922440ae3dda3bf288a8b7611 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 11 Oct 2022 23:34:05 +0100 Subject: [PATCH 014/300] 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 f97a59b4328..d0c34b83afc 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 d7d74b08e97..00000000000 --- 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 Date: Wed, 12 Oct 2022 14:32:09 +0300 Subject: [PATCH 015/300] 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 00000000000..d7d74b08e97 --- /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 Date: Wed, 12 Oct 2022 13:08:01 +0100 Subject: [PATCH 016/300] 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 d0c34b83afc..f97a59b4328 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 ebb3d201c18efba47ec546d778c2f311f266ff9e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:35:45 +0100 Subject: [PATCH 017/300] 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 8f2a784d49b..c8065e4524f 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 7fd730bece4..6c3622ed5d0 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 5843c3832b6e654a813dde273c75cf9e68497e63 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:58:19 +0100 Subject: [PATCH 018/300] 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 6c3622ed5d0..5b12e3bdc3d 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 579df742579f19fda49913d031759f26ea61c841 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:02:57 +0100 Subject: [PATCH 019/300] 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 463692e574c..b1f65194f1e 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 b5e81d27d65bb8dc5819250c29035e004c24121f Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:08:50 +0100 Subject: [PATCH 020/300] 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 22cc185fa75..52045a1d7a8 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 22b344c43a224a52d45be8087a7ead5678d6b38f Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:11:41 +0100 Subject: [PATCH 021/300] 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 c8065e4524f..e490311b916 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 8dbb1b830ef8c508190b0367501a1ef441c717c6 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 16:28:47 +0100 Subject: [PATCH 022/300] 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 b1a240e9421..a1ed27df1d8 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 6a653c33ae053d73e2d55e640a4b4bfccc74e648 Mon Sep 17 00:00:00 2001 From: Kat Gerasimova Date: Thu, 13 Oct 2022 17:09:11 +0100 Subject: [PATCH 023/300] Add issue automation for PS feature teams --- .github/workflows/triage-labelled.yml | 78 +++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml index 174e3c54c09..4031e9d5713 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -246,3 +246,81 @@ jobs: env: PROJECT_ID: "PN_kwDOAM0swc4ABTXY" GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} + + ps_features1: + name: Add labelled issues to PS features team 1 + runs-on: ubuntu-latest + if: > + contains(github.event.issue.labels.*.name, 'A-Polls') || + contains(github.event.issue.labels.*.name, 'A-Location-Sharing') || + (contains(github.event.issue.labels.*.name, 'A-Voice-Messages') && + !contains(github.event.issue.labels.*.name, 'A-Broadcast')) || + (contains(github.event.issue.labels.*.name, 'A-Session-Mgmt') && + contains(github.event.issue.labels.*.name, 'A-User-Settings')) + steps: + - uses: octokit/graphql-action@v2.x + id: add_to_project + with: + headers: '{"GraphQL-Features": "projects_next_graphql"}' + query: | + mutation add_to_project($projectid:ID!,$contentid:ID!) { + addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) { + item { + id + } + } + } + projectid: ${{ env.PROJECT_ID }} + contentid: ${{ github.event.issue.node_id }} + env: + PROJECT_ID: "PVT_kwDOAM0swc4AHJKF" + GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} + + ps_features2: + name: Add labelled issues to PS features team 2 + runs-on: ubuntu-latest + if: > + contains(github.event.issue.labels.*.name, 'A-DM-Start') || + contains(github.event.issue.labels.*.name, 'A-Broadcast') + steps: + - uses: octokit/graphql-action@v2.x + id: add_to_project + with: + headers: '{"GraphQL-Features": "projects_next_graphql"}' + query: | + mutation add_to_project($projectid:ID!,$contentid:ID!) { + addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) { + item { + id + } + } + } + projectid: ${{ env.PROJECT_ID }} + contentid: ${{ github.event.issue.node_id }} + env: + PROJECT_ID: "PVT_kwDOAM0swc4AHJKd" + GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} + + ps_features3: + name: Add labelled issues to PS features team 3 + runs-on: ubuntu-latest + if: > + contains(github.event.issue.labels.*.name, 'A-Composer-WYSIWYG') + steps: + - uses: octokit/graphql-action@v2.x + id: add_to_project + with: + headers: '{"GraphQL-Features": "projects_next_graphql"}' + query: | + mutation add_to_project($projectid:ID!,$contentid:ID!) { + addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) { + item { + id + } + } + } + projectid: ${{ env.PROJECT_ID }} + contentid: ${{ github.event.issue.node_id }} + env: + PROJECT_ID: "PVT_kwDOAM0swc4AHJKW" + GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} From 0111b932dea08e98362182165a23d717bea68592 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 21:08:45 +0100 Subject: [PATCH 024/300] 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 e0323fdc2d8..3c7b0a47293 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 dc258408e7c..0f282fee380 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 Date: Thu, 13 Oct 2022 21:15:52 +0100 Subject: [PATCH 025/300] QR login + E2EE set up --- .../sdk/internal/rendezvous/Rendezvous.kt | 97 +++++++++---------- .../features/login/qr/QrCodeLoginViewModel.kt | 73 ++++++-------- 2 files changed, 75 insertions(+), 95 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 dd7b91582be..2f85a97c558 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? - - // 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) + 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; + } - // request secrets from the verifying device - Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") + // set other device as verified + Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified"); + crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, 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") - } - } + // 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) +// } - return newSession + // 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 f97a59b4328..c729152e44e 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(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 Date: Thu, 13 Oct 2022 21:26:22 +0100 Subject: [PATCH 026/300] 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 33837bc4254..cced29aab45 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 3e5e1121ea1..cc4346d55ef 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 88238c0f04648a7468c2ee33d86068fa79e4fe5f Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 15:11:15 +0100 Subject: [PATCH 027/300] 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 d040f9c67be..6bb47dda5bb 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 627a8256799..991b7b654d5 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 4f45f807df3..5b6f0686c4e 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 ea8578ed8c5..86467520837 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 5f0a2298cb6..062b2466e55 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, @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 0c6fb45e788..22cc185fa75 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 00000000000..477719f607a --- /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 { + 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, + 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 Date: Thu, 13 Oct 2022 15:18:31 +0100 Subject: [PATCH 028/300] 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 00000000000..3d17076a445 --- /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 Date: Thu, 13 Oct 2022 15:19:39 +0100 Subject: [PATCH 029/300] 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 5b6f0686c4e..773f2118b1e 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 Date: Tue, 11 Oct 2022 23:32:49 +0100 Subject: [PATCH 030/300] Retry scanning if not a QR code From de611ca81a1c9f16ded4b6b8d3d48b12b7edd68e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 11 Oct 2022 23:33:30 +0100 Subject: [PATCH 031/300] Implementations of MSC3886 and MSC3903 From bfab07d716bddf91b2e7cab707dd2fe8e5ffb982 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 11 Oct 2022 23:34:05 +0100 Subject: [PATCH 032/300] 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 da3348653c9..05344f0a677 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 d7d74b08e97..00000000000 --- 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 Date: Wed, 12 Oct 2022 14:32:09 +0300 Subject: [PATCH 033/300] 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 00000000000..d7d74b08e97 --- /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 Date: Wed, 12 Oct 2022 13:08:01 +0100 Subject: [PATCH 034/300] 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 05344f0a677..877d32a11dd 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 Date: Thu, 13 Oct 2022 15:35:45 +0100 Subject: [PATCH 035/300] 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 6bb47dda5bb..9fde6d93264 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 773f2118b1e..7aeeacd10b1 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 Date: Thu, 13 Oct 2022 15:58:19 +0100 Subject: [PATCH 036/300] 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 7aeeacd10b1..5449c0a735f 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 Date: Thu, 13 Oct 2022 16:02:57 +0100 Subject: [PATCH 037/300] 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 463692e574c..b1f65194f1e 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 Date: Thu, 13 Oct 2022 16:08:50 +0100 Subject: [PATCH 038/300] 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 22cc185fa75..52045a1d7a8 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 Date: Thu, 13 Oct 2022 16:11:41 +0100 Subject: [PATCH 039/300] 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 9fde6d93264..1cc53b311c4 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 Date: Thu, 13 Oct 2022 16:28:47 +0100 Subject: [PATCH 040/300] 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 b1a240e9421..a1ed27df1d8 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 Date: Thu, 13 Oct 2022 21:08:45 +0100 Subject: [PATCH 041/300] 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 042f8852319..fac31ce4e38 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 dc258408e7c..0f282fee380 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 Date: Thu, 13 Oct 2022 21:15:52 +0100 Subject: [PATCH 042/300] 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 00000000000..2f85a97c558 --- /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? = 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 877d32a11dd..d9c30690a79 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(initialState) { + val TAG: String = QrCodeLoginViewModel::class.java.simpleName @AssistedFactory interface Factory : MavericksAssistedViewModelFactory { @@ -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() + + 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) } } - - // 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) { + 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 Date: Thu, 13 Oct 2022 21:26:22 +0100 Subject: [PATCH 043/300] 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 00000000000..cced29aab45 --- /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() + + 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() + 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 00000000000..cc4346d55ef --- /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 Date: Thu, 13 Oct 2022 22:23:26 +0100 Subject: [PATCH 044/300] 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 1cc53b311c4..252c33a8c4c 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 Date: Thu, 13 Oct 2022 22:32:02 +0100 Subject: [PATCH 045/300] 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 ae3ba9574b7..17b4ff24095 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 Date: Thu, 13 Oct 2022 22:37:19 +0100 Subject: [PATCH 046/300] 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 ae65963f37b..22af8cebbda 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 00000000000..43552f46be6 --- /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 00000000000..0e2ea8c7587 --- /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 00000000000..753b0bc6fa3 --- /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 00000000000..e296dce09d2 --- /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 00000000000..d490de01338 --- /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 00000000000..ead273e8ce2 --- /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 00000000000..6285c1e57a4 --- /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 00000000000..1b1826194fb --- /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 00000000000..c3b6ba7ac87 --- /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 00000000000..ddc0ae20e7c --- /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 Date: Thu, 13 Oct 2022 22:42:58 +0100 Subject: [PATCH 047/300] 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 822e9d38651..108fe7db7cc 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 @@ Linking with this device is not supported. The linking wasn’t completed in the required time. The request was denied on the other device. + The request failed. Open ${app_name} on your other device Go to Settings -> Security & Privacy -> Show All Sessions Select \'Show QR code\' 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 330562b8740..4de191f863c 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 9a6cc13de02..00000000000 --- 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 1c0841aa115..fb372cbb2fd 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() { @@ -77,11 +78,12 @@ class QrCodeLoginStatusFragment : VectorBaseFragment 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 d9c30690a79..8461d8d88fd 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(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 048/300] 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] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e474b685318..32a7c2c26fe 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 5abb786b6b792ea70f38ca616bd5925a329af719 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Fri, 14 Oct 2022 00:59:31 +0100 Subject: [PATCH 049/300] 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 2f85a97c558..d8fd3230565 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 43552f46be6..66554688086 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 0e2ea8c7587..0920bf224f1 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 753b0bc6fa3..f054b7cb734 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 cced29aab45..9f347b85368 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 e296dce09d2..14976ec836e 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 d490de01338..5a609d497e9 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 ead273e8ce2..feb7d0cf35b 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 6285c1e57a4..d317531835a 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 1b1826194fb..ee9058daa3d 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 c3b6ba7ac87..51371ddb2fd 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 ddc0ae20e7c..122b6ede82c 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 cc4346d55ef..7918946d75a 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 Date: Fri, 14 Oct 2022 01:07:19 +0100 Subject: [PATCH 050/300] 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 d8fd3230565..534d5df1ca7 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 66554688086..2a73e8f112d 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 0920bf224f1..a607dc7f38f 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 f054b7cb734..11471288f6d 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 9f347b85368..0f69bd7eda0 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 14976ec836e..b203101b660 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 5a609d497e9..785ce1fed7f 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 feb7d0cf35b..f731c89649a 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 d317531835a..1c070599b0e 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 ee9058daa3d..55b3bbb5d9a 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 51371ddb2fd..9c3e44f25b6 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 122b6ede82c..9a9db58a41e 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 7918946d75a..ba604595441 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 4de191f863c..4bef41b6c1a 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 fb372cbb2fd..5451a16b44f 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() { 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 8461d8d88fd..276cedad430 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 Date: Fri, 14 Oct 2022 01:11:25 +0100 Subject: [PATCH 051/300] 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 534d5df1ca7..e33130e5297 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 2a73e8f112d..588d034f108 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 11471288f6d..de0aed7efc6 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 0f69bd7eda0..1c8bca5d1cb 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 f731c89649a..fec55ffb67c 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 ba604595441..475a4fbe6ce 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 276cedad430..7a510984399 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 efa70fa0ff92b56d15f638dbd240a050f9b3ff25 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 21:48:33 +0100 Subject: [PATCH 052/300] 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 17b4ff24095..ae3ba9574b7 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 Date: Fri, 14 Oct 2022 01:45:03 +0100 Subject: [PATCH 053/300] 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 3a302feba09..1d8171a9e59 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 b5ffa28db7c..701f2dcab81 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 62eb0523b08..061276ba212 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 03cf2f43e6a..8e64948c6ab 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 48f66cfc757..7e6da851dd4 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 Date: Fri, 14 Oct 2022 02:04:08 +0100 Subject: [PATCH 054/300] 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 5451a16b44f..05ed070915a 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 Date: Fri, 14 Oct 2022 11:49:24 +0100 Subject: [PATCH 055/300] 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 174e3c54c09..18ce26171c6 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 e439b72e48170c9bfcd0b57157c60816655d52e8 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Fri, 14 Oct 2022 13:46:57 +0100 Subject: [PATCH 056/300] 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 e33130e5297..17f3a731812 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 Date: Fri, 14 Oct 2022 14:01:13 +0100 Subject: [PATCH 057/300] 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 00000000000..240fac35160 --- /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 Date: Fri, 14 Oct 2022 14:17:19 +0100 Subject: [PATCH 058/300] 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 17f3a731812..67bdeb22353 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? = 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 Date: Fri, 14 Oct 2022 14:18:50 +0100 Subject: [PATCH 059/300] 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 67bdeb22353..6403d170316 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 fdc55965ca0e3328d6ed4f32b00bde97f00941b9 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Fri, 14 Oct 2022 17:23:26 +0100 Subject: [PATCH 060/300] 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 588d034f108..9d2843ce66c 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 556be3af4d3..f0030d67633 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 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 Date: Fri, 14 Oct 2022 17:25:06 +0100 Subject: [PATCH 061/300] 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 7a510984399..af0df22ca3d 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(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 a3fc78594502953d86944d3f1ec0509c13921e7d Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Fri, 14 Oct 2022 18:52:42 +0100 Subject: [PATCH 062/300] 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 af0df22ca3d..81a00cf5488 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 d979b50b2cd8b098b5bd3e353264e7e76be5dabe Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 10:52:24 +0100 Subject: [PATCH 063/300] 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 6403d170316..b43b122cba9 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 1c8bca5d1cb..4d5ed30ac5d 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 475a4fbe6ce..004cf38e241 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 Date: Mon, 17 Oct 2022 10:54:28 +0100 Subject: [PATCH 064/300] 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 004cf38e241..dde5edcb939 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 Date: Mon, 17 Oct 2022 11:24:48 +0100 Subject: [PATCH 065/300] 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 b43b122cba9..4270d4a09ca 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? = 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 4d5ed30ac5d..5dbac894aef 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 b203101b660..0840e1ca2ed 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 00000000000..410c5c10367 --- /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 00000000000..56274522322 --- /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? = 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 00000000000..9854e7c0703 --- /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 00000000000..70a441d7608 --- /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 dde5edcb939..03e8b0cda70 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 Date: Mon, 17 Oct 2022 11:25:09 +0100 Subject: [PATCH 066/300] 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 81a00cf5488..8b70f61e800 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 Date: Mon, 17 Oct 2022 11:48:35 +0100 Subject: [PATCH 067/300] 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 d65e629b71e..42bba5fe884 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 Date: Mon, 17 Oct 2022 11:50:56 +0100 Subject: [PATCH 068/300] 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 4270d4a09ca..e467ff06e3b 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 5dbac894aef..489d20e5885 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 00000000000..2dd6e7be28b --- /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 56274522322..593177e625d 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? = null, - @Json val protocol: String? = null, + @Json val outcome: Outcome? = null, + @Json val protocols: List? = 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 9854e7c0703..5ff4cd7cfa3 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 00000000000..18381984a54 --- /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 Date: Mon, 17 Oct 2022 12:01:12 +0100 Subject: [PATCH 069/300] Thread safe use of OlmSAS --- .../channels/ECDHRendezvousChannel.kt | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 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 489d20e5885..ca7083b2972 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 - - 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, + 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 (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 Date: Mon, 17 Oct 2022 12:05:32 +0100 Subject: [PATCH 070/300] 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 03e8b0cda70..e899a64e990 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 1976451c8164ee15b88186ae1fc6291513d158ec Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 12:23:14 +0100 Subject: [PATCH 071/300] 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 9d2843ce66c..31014e392d9 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 e899a64e990..3e9b11a68b3 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 8b70f61e800..36e88b284c3 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 Date: Mon, 17 Oct 2022 12:32:40 +0100 Subject: [PATCH 072/300] 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 3e9b11a68b3..ca2a3425cd6 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 d616251f260515548f8b699514247b6e0a8ee9bc Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 13:41:27 +0100 Subject: [PATCH 073/300] 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 9147c4e1cde..2c3cb440b62 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 e01ee619d3bb59d5bec0c1d0ee1bef9829584f40 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 16:02:25 +0100 Subject: [PATCH 074/300] 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 | 59 ++++---- .../login/qr/QrCodeLoginStatusFragment.kt | 6 + .../features/login/qr/QrCodeLoginViewModel.kt | 27 +++- 11 files changed, 172 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 7d0173f3412..e28ebb31c62 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 @@ The linking wasn’t completed in the required time. The request was denied on the other device. The request failed. + 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); + The other device is already signed in. + The other device must be signed in. + The QR code scanned is invalid. + The sign in was cancelled on the other device. + The homeserver doesn\'t support sign in with QR code. Open ${app_name} on your other device Go to Settings -> Security & Privacy -> Show All Sessions Select \'Show QR code in this device\' 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 e467ff06e3b..9ad889fca07 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") + } + + // 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) + 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 31014e392d9..be79569164f 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 a607dc7f38f..18e625d8259 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 de0aed7efc6..5daf9069306 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 ca7083b2972..9a5c5e865a3 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 2dd6e7be28b..2ef24e9cb79 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 fec55ffb67c..c52b11a3226 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 ca2a3425cd6..fa0e5d8e2a3 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 0691e2367e1..617d620c277 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 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 9d4e5e98701..7f95fad4851 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(initialState) { - val TAG: String = QrCodeLoginViewModel::class.java.simpleName @AssistedFactory interface Factory : MavericksAssistedViewModelFactory { override fun create(initialState: QrCodeLoginViewState): QrCodeLoginViewModel } - companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + companion object : MavericksViewModelFactory 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 Date: Mon, 17 Oct 2022 16:03:47 +0100 Subject: [PATCH 075/300] 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 785ce1fed7f..00000000000 --- 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 e877feed6eedc22792a24188e3fa37099d5f1f93 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 16:06:08 +0100 Subject: [PATCH 076/300] 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 2ef24e9cb79..a0b2cb31b4a 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 5ff4cd7cfa3..9e0dbd95624 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 18381984a54..3442d4abf4c 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 1c070599b0e..8ce64302120 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 9c3e44f25b6..2814cad77c4 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 9a9db58a41e..d9f73a0b993 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 623277e31ff94a4deac05b3bc297b95f52c85441 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 16:13:49 +0100 Subject: [PATCH 077/300] 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 9a5c5e865a3..2f368d65206 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 fa0e5d8e2a3..50cebae12dd 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 Date: Mon, 17 Oct 2022 16:19:03 +0100 Subject: [PATCH 078/300] 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 a0b2cb31b4a..0ebd1f88b34 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 9e0dbd95624..33beb1f5250 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 3442d4abf4c..7c020da641d 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 8ce64302120..65037e1252e 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 2814cad77c4..aa578e36605 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 Date: Mon, 17 Oct 2022 16:24:01 +0100 Subject: [PATCH 079/300] 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 7f95fad4851..69deb44c707 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 Date: Mon, 17 Oct 2022 18:32:35 +0300 Subject: [PATCH 080/300] Implement try again button action. --- .../GetHomeServerCapabilitiesTask.kt | 1 + .../features/login/qr/QrCodeLoginAction.kt | 1 + .../features/login/qr/QrCodeLoginActivity.kt | 42 +++++++++++-------- .../login/qr/QrCodeLoginStatusFragment.kt | 7 ++++ .../login/qr/QrCodeLoginViewEvents.kt | 1 + .../features/login/qr/QrCodeLoginViewModel.kt | 5 +++ 6 files changed, 40 insertions(+), 17 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 2c3cb440b62..4b56d8e756c 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 8854d0720fc..5ea46d3dcd2 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 2f302618906..c9b8ae00804 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,28 +38,31 @@ 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 -> { + 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) } - QrCodeLoginType.LINK_A_DEVICE -> { - if (qrCodeLoginArgs.showQrCodeImmediately) { - handleNavigateToShowQrCodeScreen() - } else { - showInstructionsFragment(qrCodeLoginArgs) - } - } - null -> { - Timber.i("QrCodeLoginArgs is null. This is not expected.") - finish() - return - } + } + null -> { + Timber.i("QrCodeLoginArgs is null. This is not expected.") + finish() } } - - observeViewEvents() } private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) { @@ -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 617d620c277..097c956f2c6 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 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 Date: Mon, 17 Oct 2022 16:34:51 +0100 Subject: [PATCH 081/300] 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 7c020da641d..6fce2fa11c4 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 aa578e36605..6fca7efa714 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 d9f73a0b993..ab58179f5a7 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 eec99e65bde304e5da4882799188c4f3d6776b72 Mon Sep 17 00:00:00 2001 From: NIkita Fedrunov Date: Mon, 17 Oct 2022 16:48:02 +0200 Subject: [PATCH 082/300] thread read receipts and unread notifications support is added to homeserver capatibilities --- .../homeserver/HomeServerCapabilities.kt | 5 +++ .../auth/version/HomeServerVersion.kt | 1 + .../sdk/internal/auth/version/Versions.kt | 12 +++++++ .../database/RealmSessionStoreMigration.kt | 4 ++- .../mapper/HomeServerCapabilitiesMapper.kt | 1 + .../database/migration/MigrateSessionTo040.kt | 34 +++++++++++++++++++ .../model/HomeServerCapabilitiesEntity.kt | 1 + .../GetHomeServerCapabilitiesTask.kt | 3 ++ 8 files changed, 60 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 8c14ca892aa..773e870ffd9 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 75639c6a218..d443d6e3c8c 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 5e133fab9cc..9ed89016836 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,16 @@ 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 9a2c32f97cd..def0f6de7a7 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 63fa101c45c..3528ca0051d 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 00000000000..b3e02342dd1 --- /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 cfa02b2c743..89f1e50b309 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 2c3cb440b62..a5953d870c8 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 d3e61a23a9a23ea75fe57f5b4c01cf25ea642558 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 17:29:45 +0100 Subject: [PATCH 083/300] 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 ab58179f5a7..75f0024fdac 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 Date: Mon, 17 Oct 2022 17:30:54 +0100 Subject: [PATCH 084/300] 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 ed2fb6e0f5f..2647c5974dc 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(initialState) { @AssistedFactory From 8c8190202f9792b708d623ef5944016220edc69c Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 17:31:14 +0100 Subject: [PATCH 085/300] 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 097c956f2c6..6ef261e6d90 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 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 Date: Mon, 17 Oct 2022 17:33:43 +0100 Subject: [PATCH 086/300] 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 4b56d8e756c..2c3cb440b62 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 Date: Mon, 17 Oct 2022 17:36:35 +0100 Subject: [PATCH 087/300] 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 9ad889fca07..e41a72db94d 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 2647c5974dc..4134dc8ab20 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 8cfe6b13639158011e63b40e2592aa770316e5a0 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 23:46:28 +0100 Subject: [PATCH 088/300] 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 e28ebb31c62..7fe8dd8d08f 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 @@ 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); The other device is already signed in. The other device must be signed in. - The QR code scanned is invalid. + That QR code is invalid. The sign in was cancelled on the other device. The homeserver doesn\'t support sign in with QR code. - Open ${app_name} on your other device - Go to Settings -> Security & Privacy -> Show All Sessions - Select \'Show QR code in this device\' + Open the app on your other device + Go to Settings -> Security & Privacy + Select \'Show QR code\' Start at the sign in screen Select \'Sign in with QR code\' Start at the sign in screen 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 089/300] 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] --- .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 bf948064ed1..628e12593c3 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 1816fe3a784..bb16d8abe8e 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 cf1c7515fb87283915412c7718917a0d9902221f Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 00:35:13 +0100 Subject: [PATCH 090/300] 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 efd23f25302..40fcbbbb852 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 From a3126b0026dc70ad6c9e2cf12a11fe3ba0944179 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 00:35:35 +0100 Subject: [PATCH 091/300] 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 4134dc8ab20..1a8ed945c05 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 Date: Tue, 18 Oct 2022 00:35:43 +0100 Subject: [PATCH 092/300] 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 5daf9069306..81632e951a7 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 093/300] 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] --- 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 ca77e4b86fe..66fdf3fbf4a 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 f297117df2fe66c61e8589e73bc88cf7a99586bb Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 08:48:28 +0100 Subject: [PATCH 094/300] 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 2f368d65206..19c46db34d3 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 a1d2944c324c37bdcf9c92e0ec02864ce6781d71 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 09:08:40 +0100 Subject: [PATCH 095/300] Always check master key when provided by verifying device --- .../android/sdk/api/rendezvous/Rendezvous.kt | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 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 e41a72db94d..f724ac4b621 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) - - Timber.tag(TAG).i("Setting master key as trusted") - crypto.crossSigningService().markMyMasterKeyAsTrusted() - } ?: Timber.tag(TAG).w("No local master key so not verifying") + // verifying device provided us with a master key, so use it to check integrity + + // 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 Date: Tue, 18 Oct 2022 09:31:13 +0100 Subject: [PATCH 096/300] 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 19c46db34d3..f7da0efa92e 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 50cebae12dd..3f7cfe786e6 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 Date: Tue, 18 Oct 2022 09:34:28 +0100 Subject: [PATCH 097/300] 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 3f7cfe786e6..7a5cbe54245 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 6f5fefba56389896ef7bb5b4d0e2e786fb957e1f Mon Sep 17 00:00:00 2001 From: NIkita Fedrunov Date: Tue, 18 Oct 2022 10:44:20 +0200 Subject: [PATCH 098/300] lint --- .../org/matrix/android/sdk/internal/auth/version/Versions.kt | 1 - 1 file changed, 1 deletion(-) 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 9ed89016836..1245d8df4b6 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 @@ -88,7 +88,6 @@ 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 { From 9fb0db3129ff6521ac86aba4bee29c12e6d45d5b Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 12:07:12 +0100 Subject: [PATCH 099/300] Update library/ui-strings/src/main/res/values/strings.xml Co-authored-by: Benoit Marty --- 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 7fe8dd8d08f..7b6b9b914d3 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 @@ The linking wasn’t completed in the required time. The request was denied on the other device. The request failed. - 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); + 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); The other device is already signed in. The other device must be signed in. That QR code is invalid. From 0d1df3f66e5587ba299b909183fa464673694489 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 12:08:09 +0100 Subject: [PATCH 100/300] Update matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt Co-authored-by: Benoit Marty --- .../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 be79569164f..9976c7a8e42 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 Date: Tue, 18 Oct 2022 12:09:06 +0100 Subject: [PATCH 101/300] Update matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt Co-authored-by: Benoit Marty --- .../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 f7da0efa92e..b21d89059b9 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 Date: Tue, 18 Oct 2022 12:09:17 +0100 Subject: [PATCH 102/300] Update matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt Co-authored-by: Benoit Marty --- .../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 9976c7a8e42..0956a5b0a0a 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 Date: Tue, 18 Oct 2022 12:11:41 +0100 Subject: [PATCH 103/300] 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 1a8ed945c05..9bf3e955d3e 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 Date: Tue, 18 Oct 2022 12:23:21 +0100 Subject: [PATCH 104/300] 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 b21d89059b9..6c45aacfaaa 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 1cbaff059ae..b306288c5e1 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 { return getEmojiCodeRepresentation(shortCodeBytes!!) } From f7e0a198335b9830af68404d522c834319adb784 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 12:29:48 +0100 Subject: [PATCH 105/300] 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 6c45aacfaaa..4bbf34280c0 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 0840e1ca2ed..55bac6397e5 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 410c5c10367..575b5d4bfd3 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 593177e625d..04631ce9599 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? = 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? = 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 55b3bbb5d9a..1bde43ab7ea 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 70a441d7608..049aa8b7560 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 67be8c3c40f06a88345436abaf129ce3c4e52344 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Tue, 18 Oct 2022 12:44:05 +0100 Subject: [PATCH 106/300] 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 4bbf34280c0..e844143889f 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 Date: Tue, 18 Oct 2022 15:45:39 +0300 Subject: [PATCH 107/300] 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 c9b8ae00804..a0c113224d0 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 9bf3e955d3e..97cca9d7918 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 4f652f102676e8bd2364149d00009f9d0f1ea656 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Wed, 19 Oct 2022 09:12:09 +0100 Subject: [PATCH 108/300] 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 e844143889f..c1d6b1b70ed 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 7a5cbe54245..620b599e3df 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 b306288c5e1..29b416bb82c 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 9d412ed6ad1689263f9b42e2c2a1864a760ad5c8 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Wed, 19 Oct 2022 12:48:37 +0000 Subject: [PATCH 109/300] Translated using Weblate (Czech) Currently translated at 100.0% (2505 of 2505 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/ --- library/ui-strings/src/main/res/values-cs/strings.xml | 2 ++ 1 file changed, 2 insertions(+) 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 3410858988b..398b7f7b355 100644 --- a/library/ui-strings/src/main/res/values-cs/strings.xml +++ b/library/ui-strings/src/main/res/values-cs/strings.xml @@ -2859,4 +2859,6 @@ 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: Přihlásit se pomocí QR kódu Naskenovat QR kód + Možnost nahrávat a odesílat hlasové vysílání na časové ose místnosti. + Povolit hlasové vysílání (v aktivním vývoji) \ No newline at end of file From 3598d27dfbdaf9bff2bc5e4e25c233e2a51668bc Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 19 Oct 2022 13:24:48 +0000 Subject: [PATCH 110/300] Translated using Weblate (German) Currently translated at 100.0% (2505 of 2505 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, 4 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 fe849a1ddf3..57adb82d3ab 100644 --- a/library/ui-strings/src/main/res/values-de/strings.xml +++ b/library/ui-strings/src/main/res/values-de/strings.xml @@ -2797,11 +2797,13 @@ Benutze dein angemeldetes Gerät um den unten angezeigten QR-Code einzulesen: Mit QR-Code anmelden Benutze die Kamera auf diesem Gerät um den vom anderen Gerät angezeigten QR-Code zu scannen: - QR-Code scannen + QR-Code einlesen 3 2 1 Du kannst dieses Gerät benutzen um ein anderes Gerät per QR-Code anzumelden. Dafür gibt es zwei Wege: Mit QR-Code anmelden - QR-Code scannen + QR-Code einlesen + Zeichne Sprachnachrichten auf, während du sie in Echtzeit in den Raumverlauf sendest. + Sprachübertragung aktivieren (in aktiver Entwicklung) \ No newline at end of file From 54baf13b8299a0333d130e6e19703a40835a2067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 19 Oct 2022 13:42:54 +0000 Subject: [PATCH 111/300] Translated using Weblate (Estonian) Currently translated at 99.6% (2497 of 2505 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/et/ --- library/ui-strings/src/main/res/values-et/strings.xml | 2 ++ 1 file changed, 2 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 7ead21394c4..3ad6feec532 100644 --- a/library/ui-strings/src/main/res/values-et/strings.xml +++ b/library/ui-strings/src/main/res/values-et/strings.xml @@ -2796,4 +2796,6 @@ ${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. + Võimalus salvestada ja postitada ringhäälingukõnesid jututoa ajajoonele. + Võta kasutusele ringhäälingukõned (aktiivses arenduses) \ No newline at end of file From 90e8028c5a61b3451cfa6d96a010354594916741 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 19 Oct 2022 12:36:10 +0000 Subject: [PATCH 112/300] Translated using Weblate (Italian) Currently translated at 100.0% (2505 of 2505 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/it/ --- library/ui-strings/src/main/res/values-it/strings.xml | 2 ++ 1 file changed, 2 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 cea69030bc7..be542cab880 100644 --- a/library/ui-strings/src/main/res/values-it/strings.xml +++ b/library/ui-strings/src/main/res/values-it/strings.xml @@ -2796,4 +2796,6 @@ Puoi usare questo dispositivo per accedere in un dispositivo mobile o web con un codice QR. Ci sono due modi: Accedi con codice QR Scansiona codice QR + Registra e invia broadcast vocali nella linea temporale della stanza. + Attiva broadcast voce (in sviluppo attivo) \ No newline at end of file From 7daa2f7281c8bcefe2e9888a32424972ab08f5e2 Mon Sep 17 00:00:00 2001 From: Nui Harime Date: Wed, 19 Oct 2022 14:14:55 +0000 Subject: [PATCH 113/300] Translated using Weblate (Russian) Currently translated at 95.6% (2396 of 2505 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 | 10 +++++----- 1 file changed, 5 insertions(+), 5 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 0349acedd15..e3edf657209 100644 --- a/library/ui-strings/src/main/res/values-ru/strings.xml +++ b/library/ui-strings/src/main/res/values-ru/strings.xml @@ -367,8 +367,8 @@ Добавить телефон Системные настройки приложения. Сведения о приложении - Включить уведомления для этой учетной записи - Включить уведомления для этой сессии + Уведомления для этой учётной записи + Уведомления для этой сессии В персональных чатах В групповых чатах Когда меня приглашают в комнату @@ -663,7 +663,7 @@ Включить Настройки сессии. Уведомления включены для этой сессии. - Уведомления не включено для этой сессии. + Уведомления не включены для этой сессии. \nПожалуйста, проверьте настройки ${app_name}. Включить Проверка сервисов Play @@ -1572,8 +1572,8 @@ или другой клиент Matrix поддерживающий перекрестную подпись Принудительно отбрасывает текущую групповую сессию для отправки сообщений в зашифрованную комнату Чтобы продолжить, используйте %1$s или %2$s. - Используйте ключ восстановления - Выберите ключ восстановления или введите его вручную, введя или вставив из буфера обмена + Используйте бумажный ключ + Выберите бумажный ключ или введите его вручную, введя или вставив из буфера обмена Не удалось получить доступ к защищенному хранилищу данных Не зашифровано Зашифровано неподтверждённой сессией From 36eb538a9348c5e6028954eb4cb7164f2639595d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Oct 2022 17:17:21 +0200 Subject: [PATCH 114/300] 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 4a6c0edf10b..968d8515ac9 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 ca77e4b86fe..1e8a8b3d3f9 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 399e3b1c5aaa10ccab0410da1e6cb21b11caed03 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 19 Oct 2022 18:29:04 +0000 Subject: [PATCH 115/300] Translated using Weblate (German) Currently translated at 100.0% (2512 of 2512 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/de/ --- .../ui-strings/src/main/res/values-de/strings.xml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 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 57adb82d3ab..10c0e262e37 100644 --- a/library/ui-strings/src/main/res/values-de/strings.xml +++ b/library/ui-strings/src/main/res/values-de/strings.xml @@ -2784,9 +2784,9 @@ Beginne auf dem Anmeldebildschirm Wähle „Mit QR-Code anmelden“ Beginne auf dem Anmeldebildschirm - Wähle \'QR-Code auf diesem Gerät anzeigen\' - Gehe zu Einstellungen -> Sicherheit und Privatsphäre -> Alle Sitzungen anzeigen - Öffne ${app_name} auf deinem anderen Gerät + Wähle \'QR-Code anzeigen\' + Gehe zu Einstellungen -> Sicherheit und Privatsphäre + Öffne die App auf deinem anderen Gerät Die Anfrage wurde auf dem anderen Gerät abgelehnt. Die Verbindung konnte nicht in der erforderlichen Zeit hergestellt werden. Verbindung mit diesem Gerät nicht unterstützt. @@ -2806,4 +2806,11 @@ QR-Code einlesen Zeichne Sprachnachrichten auf, während du sie in Echtzeit in den Raumverlauf sendest. Sprachübertragung aktivieren (in aktiver Entwicklung) + Der Home-Server unterstützt Anmelden mit QR-Code nicht. + Die Anmeldung wurde vom anderen Gerät abgebrochen. + Der QR-Code ist ungültig. + Das andere Gerät muss angemeldet sein. + Das andere Gerät ist bereits angemeldet. + Es ist ein Problem bei der Herstellung der sicheren Kommunikation aufgetreten. Eines der folgenden Dinge könnte kompromitiert sein: Dein Heim-Server; deine Internetverbindung(en); dein(e) Gerät(e); + Die Anfrage ist fehlgeschlagen. \ No newline at end of file From 8b538a79bb3c6239da8666ef5815630b1ed7a761 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Wed, 19 Oct 2022 16:50:12 +0000 Subject: [PATCH 116/300] Translated using Weblate (Czech) Currently translated at 100.0% (2512 of 2512 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 | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 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 398b7f7b355..717da1624c7 100644 --- a/library/ui-strings/src/main/res/values-cs/strings.xml +++ b/library/ui-strings/src/main/res/values-cs/strings.xml @@ -351,7 +351,7 @@ Hovor probíhá… Protější strana hovor nepřijala. Informace - ${app_name} potřebuje oprávnění pro přístup k Vašemu mikrofonu pro uskutečnění hlasových hovorů. + ${app_name} potřebuje oprávnění pro přístup k vašemu mikrofonu pro uskutečnění hlasových hovorů. ANO NE Pokračovat @@ -411,12 +411,12 @@ Hotovo Opravdu se chcete odhlásit\? Video hovor probíhá… - ${app_name} potřebuje oprávnění pro přístup k Vaší kameře a mikrofonu pro uskutečnění video hovoru. + ${app_name} potřebuje oprávnění pro přístup k vaší kameře a mikrofonu pro uskutečnění video hovoru. \n \nProsím, povolte přístup na následující hlášce abyste mohli uskutečnit hovor. Tuto změnu nelze zvrátit, protože povyšujete uživatele na stejnou úroveň, jakou máte vy. \nOpravdu to chcete udělat\? - Toto by mohlo znamenat, že někdo škodlivě zachytává Vaši komunikaci nebo že Váš telefon nedůvěřuje certifikátu poskytnutému vzdáleným serverem. + Toto by mohlo znamenat, že někdo škodlivě zachytává vaši komunikaci nebo že Váš telefon nedůvěřuje certifikátu poskytnutému vzdáleným serverem. Pokud administrátor serveru řekl, že toto je předpokládané, ujistěte se, že otisk níže se shoduje s otiskem který Vám poskytl. Certifikát se změnil z toho, kterému Váš telefon důvěřoval. Toto je VELMI NEOBVYKLÉ. Je doporučeno, abyste NEPŘIJALI tento nový certifikát. Certifikát se změnil z původně důvěryhodného na nyní nedůvěryhodný. Server patrně obnovil svůj certifikát. Kontaktujte administrátora kvůli očekávanému otisku. @@ -578,7 +578,7 @@ Deaktivace účtu Deaktivovat můj účet Objevování - Správa Vašich nastavení pro objevování. + Správa vašich nastavení pro objevování. Analýza Odeslat analytická data ${app_name} sbírá anonymní analytická data pro vylepšení aplikace. @@ -848,7 +848,7 @@ Začít používat zálohu klíčů (Pokročilé) Zabezpečit zálohu přístupovou frází. - Uložíme zašifrovanou kopii Vašich klíčů na Vašem domovském serveru. Chraňte svoji zálohu přístupovou frází, abyste ji udrželi v bezpečí. + Uložíme zašifrovanou kopii vašich klíčů na Vašem domovském serveru. Chraňte svoji zálohu přístupovou frází, abyste ji udrželi v bezpečí. \n \nZ důvodu nejvyšší bezpečnosti by se měla lišit od hesla účtu. Nastavit přístupovou frází @@ -856,7 +856,7 @@ Nebo zabezpečte svoji zálohu pomocí klíče obnovy, uloženého někde v bezpečí. (Pokročilé) Nastavit s klíčem obnovy Podařilo se! - Váš klíč obnovy je záchranná síť - lze jej použít pro obnovu Vašich šifrovaných zpráv, pokud zapomenete svou přístupovou frázi. + Váš klíč obnovy je záchranná síť - lze jej použít pro obnovu vašich šifrovaných zpráv, pokud zapomenete svou přístupovou frázi. \nUchovávejte svůj klíč obnovy velmi bezpečně, např. ve správci hesel (nebo trezoru) Uchovávejte svůj klíč obnovy velmi bezpečně, např. ve správci hesel (nebo trezoru) Hotovo @@ -1106,7 +1106,7 @@ Prémiový hosting pro organizace Zadejte adresu Modular Element nebo serveru, který chcete použít Při načítání stránky došlo k chybě: %1$s (%2$d) - Aplikace se nemůže přihlásit k tomuto homeserveru. Homeserver podporuje následující typy přihlášení: %1$s. + Aplikace se nemůže přihlásit k tomuto domovskému serveru. Domovský server podporuje následující typy přihlášení: %1$s. \n \nChcete se přihlásit webovým klientem\? Omlouváme se, tento server již nepřijímá nové účty. @@ -1279,7 +1279,7 @@ ${app_name} neobstarává události typu \'%1$s\' ${app_name} narazil na chybu při převádění obsahu události s id \'%1$s\' Odignorovat - Tato relace nemůže sdílet toto ověření s jinými z Vašich relací. + Tato relace nemůže sdílet toto ověření s jinými z vašich relací. \nToto ověření bude uloženo místně a sdíleno v budoucí verzi aplikace. Odešle danou zprávu zabarvenou jako duha Odešle daný emote zabarvený jako duha @@ -1475,7 +1475,7 @@ Manuálně ověřit textem Ověřit přihlášení Interaktivně ověřit pomocí Emoji - Potvrďte svou identitu ověřením tohoto přihlášení v některé z Vašich dalších relacích a udělte přístup k zašifrovaným zprávám. + Potvrďte svou identitu ověřením tohoto přihlášení v některé z vašich dalších relacích a udělte přístup k zašifrovaným zprávám. Zvolte si, prosím, uživatelské jméno. Prosím, zvolte heslo. Překontrolovat tento odkaz @@ -1517,7 +1517,7 @@ Nemáte povolení zahájit konferenční hovor v této místnosti Zahájit video schůzku Zahájit hlasovou schůzku - Schůzky používají pravidla zabezpečení a přístupu Jitsi. Všichni lidé nyní v místnosti uvidí pozvánku k připojení, zatímco Vaše schůzka probíhá. + Schůzky používají pravidla zabezpečení a přístupu Jitsi. Všichni lidé nyní v místnosti uvidí pozvánku k připojení, zatímco vaše schůzka probíhá. Nemůžete zahájit hovor se sebou Nemůžete zahájit hovor se sebou, počkejte, až účastníci přijmou pozvánku Přidání widgetu se nezdařilo @@ -1567,9 +1567,9 @@ Důvod k vykázání Zrušit vykázání uživatele Zrušení vykázání uživatele jim opět umožní vstoupit do místnosti. - Žádné telefonní číslo nebylo zadáno do Vašeho účtu + Žádné telefonní číslo nebylo zadáno do vašeho účtu Emailová adresa - Žádná emailová adresa nebyla zadána do Vašeho účtu + Žádná emailová adresa nebyla zadána do vašeho účtu Telefonní čísla Ostranit %s\? Ujistěte se, že kliknete na odkaz v e-mailu, který jsme Vám poslali. @@ -1577,7 +1577,7 @@ Vytvořit bezpečnou zálohu Resetovat bezpečnou zálohu Nastavit na tomto zařízení - Ochrana před ztrátou přístupu k šifrovaným zprávám a datům pomocí zálohy šifrovacích klíčů na Vašem serveru. + Ochrana před ztrátou přístupu k šifrovaným zprávám a datům pomocí zálohy šifrovacích klíčů na vašem serveru. Generovat nový bezpečnostní klíč nebo nastavit novou bezpečnostní frázi pro existující zálohu. To nahradí Váš nynější klíč nebo frázi. Integrace jsou vypnuty @@ -1641,7 +1641,7 @@ Zastavit fotoaparát Spustit fotoaparát Bezpečná záloha - Ochrana před ztrátou přístupu k šifrovaným zprávám a datům pomocí zálohy šifrovacích klíčů na Vašem serveru. + Ochrana před ztrátou přístupu k šifrovaným zprávám a datům pomocí zálohy šifrovacích klíčů na vašem serveru. Nastavit Použít bezpečnostní klíč Generovat bezpečnostní klíč k uložení na bezpečném místě např. správci hesel nebo sejfu. @@ -2104,7 +2104,7 @@ Prohlédnout a spravovat adresy tohoto prostoru. Adresy prostorů Aktualizujte na doporučenou verzi místnosti - Tato místnost používá místnost verze %s, kterou homeserver označil za nestabilní. + Tato místnost používá verzi místnosti %s, kterou domovský server označil za nestabilní. K aktualizaci místnosti potřebujete oprávnění Automaticky aktualizovat mateřský prostor Automaticky pozvat uživatele @@ -2124,7 +2124,7 @@ Raději ověřit porovnáním emoji Oskenovat tímto zařízením Oskenujte kód svým dalším zařízením nebo přepněte a oskenujte tímto zařízením - URL API Homeserveru + URL API domovského serveru Chybějící oprávnění Pro provedení této akce udělte, prosím, oprávnění Fotoaparát v systémových nastaveních. Některá z oprávnění potřebných k provedení akce chybí, prosím, udělte oprávnění v systémových nastaveních. @@ -2839,9 +2839,9 @@ Začněte na přihlašovací obrazovce Vyberte možnost \"Přihlásit se pomocí QR kódu\" Začněte na přihlašovací obrazovce - Vyberte možnost \"Zobrazit QR kód na tomto zařízení\" - Přejděte do Nastavení -> Zabezpečení a soukromí -> Zobrazit všechny relace - Otevřete ${app_name} na vašem druhém zařízení + Vyberte možnost \"Zobrazit QR kód\" + Přejděte do Nastavení -> Zabezpečení a soukromí + Otevřete aplikaci na vašem druhém zařízení Žádost byla na druhém zařízení zamítnuta. Propojení nebylo dokončeno v požadovaném čase. Propojení s tímto zařízením není podporováno. @@ -2861,4 +2861,11 @@ Naskenovat QR kód Možnost nahrávat a odesílat hlasové vysílání na časové ose místnosti. Povolit hlasové vysílání (v aktivním vývoji) + Domovský server nepodporuje přihlášení pomocí QR kódu. + Přihlášení bylo na druhém zařízení zrušeno. + Tento QR kód je neplatný. + Druhé zařízení musí být přihlášeno. + Druhé zařízení je již přihlášeno. + Při nastavování zabezpečeného zasílání zpráv se vyskytl problém se zabezpečením. Může být napadena jedna z následujících věcí: váš domovský server; vaše internetové připojení; vaše zařízení; + Žádost se nezdařila. \ No newline at end of file From a84123ff8bdffcfb10dd4a9d5a988f9656ea0629 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 19 Oct 2022 18:38:09 +0000 Subject: [PATCH 117/300] Translated using Weblate (German) Currently translated at 100.0% (2512 of 2512 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 | 2 +- 1 file changed, 1 insertion(+), 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 10c0e262e37..9e37fb513d2 100644 --- a/library/ui-strings/src/main/res/values-de/strings.xml +++ b/library/ui-strings/src/main/res/values-de/strings.xml @@ -2806,7 +2806,7 @@ QR-Code einlesen Zeichne Sprachnachrichten auf, während du sie in Echtzeit in den Raumverlauf sendest. Sprachübertragung aktivieren (in aktiver Entwicklung) - Der Home-Server unterstützt Anmelden mit QR-Code nicht. + Der Heim-Server unterstützt Anmelden per QR-Code nicht. Die Anmeldung wurde vom anderen Gerät abgebrochen. Der QR-Code ist ungültig. Das andere Gerät muss angemeldet sein. From fd105ae5aaef32be3e17eb4e2bbb2f73ae911b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 19 Oct 2022 17:54:08 +0000 Subject: [PATCH 118/300] Translated using Weblate (Estonian) Currently translated at 99.6% (2504 of 2512 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 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 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 3ad6feec532..05b01c2bdee 100644 --- a/library/ui-strings/src/main/res/values-et/strings.xml +++ b/library/ui-strings/src/main/res/values-et/strings.xml @@ -2754,9 +2754,9 @@ Alusta sisselogimisvaatest Vali „Logi võrku QR-koodi abil“ Alusta sisselogimisvaatest - Vali „Näita selles seadmes QR-koodi“ - Ava Seadistused -> Turvalisus ja privaatsus -> Näita kõiki sessioone - Ava ${app_name} oma teises seades + Vali „Näita QR-koodi“ + Ava Seadistused -> Turvalisus ja privaatsus + Ava sama rakendus oma teises seades Teine seade lükkas päringu tagasi. Sidumine ei lõppenud etteantud aja jooksul. Sidumine selle seadmega ei ole toetatud. @@ -2798,4 +2798,11 @@ \nJärgmistes vaadetes palun anna sellele rakendusele teavituste kuvamiseks vajalikud õigused. Võimalus salvestada ja postitada ringhäälingukõnesid jututoa ajajoonele. Võta kasutusele ringhäälingukõned (aktiivses arenduses) + Koduserver ei toeta muude seadmete võrku logimise võimalust. + Sisselogimine katkestati teises seadmes. + See QR-kood on vigane. + Teine seade peab olema võrku loginud. + Teine seade on juba võrku loginud. + Turvalise sõnumivahetuse ülesseadmisel tekkis turvaviga. Üks kolmest võib olla sattunud vale osapoole kontrolli alla: sinu koduserver, sinu internetiühendus või sinu seade; + Päring ei õnnestunud. \ No newline at end of file From ad81aa764284940de8671819783726f8dacf7843 Mon Sep 17 00:00:00 2001 From: Nui Harime Date: Wed, 19 Oct 2022 16:38:14 +0000 Subject: [PATCH 119/300] Translated using Weblate (Russian) Currently translated at 95.3% (2396 of 2512 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 | 4 ++-- 1 file changed, 2 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 e3edf657209..77a555ca4e3 100644 --- a/library/ui-strings/src/main/res/values-ru/strings.xml +++ b/library/ui-strings/src/main/res/values-ru/strings.xml @@ -830,8 +830,8 @@ Зашифрованная копия ключей будет храниться на вашем сервере. Для безопасности защитите её мнемонической фразой. \n \nДля максимальной безопасности мнемоническая фраза должна отличаться от пароля вашей учётной записи. - Ключ восстановления — это страховка, вы можете использовать его для восстановления доступа к вашим зашифрованным сообщениям, если забудете вашу парольную фразу. -\nХраните ключ восстановления в надёжном месте, например, в диспетчере паролей (или в сейфе) + Бумажный ключ — это подстраховка: вы можете использовать его для восстановления доступа к своим зашифрованным сообщениям, если забудете свою мнемоническую фразу. +\nХраните свой бумажный ключ в очень надёжном месте, например, в менеджере паролей (или в сейфе) Импортирование ключей… Скачивание ключей… Вычисление бумажного ключа… From 6e6bd6e4a2364d0d15dbb8944c977f3420095c5d Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Wed, 19 Oct 2022 16:48:00 +0000 Subject: [PATCH 120/300] Translated using Weblate (Slovak) Currently translated at 100.0% (2512 of 2512 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/ --- .../ui-strings/src/main/res/values-sk/strings.xml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 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 43a8301d58f..289ab52a492 100644 --- a/library/ui-strings/src/main/res/values-sk/strings.xml +++ b/library/ui-strings/src/main/res/values-sk/strings.xml @@ -2839,9 +2839,9 @@ Začnite na prihlasovacej obrazovke Vyberte možnosť \"Prihlásiť sa pomocou QR kódu\" Začnite na prihlasovacej obrazovke - Vyberte možnosť \"Zobraziť QR kód na tomto zariadení\" - Prejdite do Nastavenia -> Zabezpečenie a súkromie -> Zobraziť všetky relácie - Otvorte ${app_name} na vašom druhom zariadení + Vyberte možnosť \"Zobraziť QR kód\" + Prejdite do Nastavenia -> Zabezpečenie a súkromie + Otvorte aplikáciu na vašom druhom zariadení Žiadosť bola na druhom zariadení zamietnutá. Prepojenie nebolo dokončené v požadovanom čase. Prepojenie s týmto zariadením nie je podporované. @@ -2859,4 +2859,13 @@ 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: Prihlásiť sa pomocou QR kódu Skenovať QR kód + Domovský server nepodporuje prihlásenie pomocou QR kódu. + Prihlasovanie bolo zrušené na druhom zariadení. + QR kód nie je platný. + Druhé zariadenie musí byť prihlásené. + Druhé zariadenie je už prihlásené. + Pri nastavovaní zabezpečeného zasielania správ sa vyskytol bezpečnostný problém. Jedna z nasledujúcich možností môže byť kompromitovaná: Váš domovský server; Vaše internetové pripojenie (pripojenia); Vaše zariadenie (zariadenia); + Žiadosť zlyhala. + Možnosť nahrávania a odosielania hlasového vysielania v časovej osi miestnosti. + Zapnúť hlasové vysielanie (v štádiu aktívneho vývoja) \ No newline at end of file From 228a089de830638338bf5d8e733c404f92200926 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Wed, 19 Oct 2022 15:55:38 +0000 Subject: [PATCH 121/300] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2512 of 2512 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- .../ui-strings/src/main/res/values-uk/strings.xml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 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 2a04e58f414..38e500e1fe0 100644 --- a/library/ui-strings/src/main/res/values-uk/strings.xml +++ b/library/ui-strings/src/main/res/values-uk/strings.xml @@ -2892,9 +2892,9 @@ Виберіть «Увійти за допомогою QR-коду» Почніть з екрана входу Почніть з екрана входу - Виберіть «Показати QR-код на цьому пристрої» - Перейдіть до Налаштування -> Безпека й приватність -> Показати всі сеанси - Відкрийте ${app_name} на іншому своєму пристрої + Виберіть «Показати QR-код» + Перейдіть до Налаштування -> Безпека й приватність + Відкрийте застосунок на іншому своєму пристрої Запит на іншому пристрої було відхилено. Пов\'язування не було завершено у встановлені терміни. Пов\'язування з цим пристроєм не підтримується. @@ -2913,4 +2913,13 @@ Сканувати QR-код Сканувати QR-код Сканувати QR-код + Домашній сервер не підтримує вхід за допомогою QR-коду. + Вхід на іншому пристрої було скасовано. + Цей QR-код недійсний. + Повинен бути виконаний вхід з іншого пристрою. + Вхід з іншого пристрою вже виконано. + Під час налаштування захищеного обміну повідомленнями виникла проблема з безпекою. Можливо, порушено одне з таких налаштувань: Ваш домашній сервер; Ваше інтернет-з\'єднання; Ваш пристрій; + Запит не виконаний. + Можливість записувати та надсилати голосові записи до стрічки кімнати. + Увімкнути голосове мовлення (в активній розробці) \ No newline at end of file From 1e3f8231ba165023e6e91d48c0f7fe0e36f9f291 Mon Sep 17 00:00:00 2001 From: jucktnich Date: Wed, 19 Oct 2022 18:48:20 +0000 Subject: [PATCH 122/300] Translated using Weblate (German) Currently translated at 100.0% (2512 of 2512 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 | 2 +- 1 file changed, 1 insertion(+), 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 9e37fb513d2..2d67b127f96 100644 --- a/library/ui-strings/src/main/res/values-de/strings.xml +++ b/library/ui-strings/src/main/res/values-de/strings.xml @@ -2741,7 +2741,7 @@ ⚠ Es befinden sich nicht verifizierte Geräte in diesem Raum. Sie werden deine Nachrichten nicht entschlüsseln können. Niemals verschlüsselte Nachrichten zu unverifizierten Sitzungen in diesem Raum senden. Verstanden - Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus) + Probiere den Rich-Text-Editor aus (Klartext-Modus kommt bald) Aktiviere Rich-Text-Editor Browser Durchgestrichen formatieren From 5b1e29bb4f1beb0f3ce91888e4b5024fd32a32f6 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 19 Oct 2022 18:42:29 +0000 Subject: [PATCH 123/300] Translated using Weblate (German) Currently translated at 100.0% (2512 of 2512 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 | 2 +- 1 file changed, 1 insertion(+), 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 2d67b127f96..e8f720d1f5b 100644 --- a/library/ui-strings/src/main/res/values-de/strings.xml +++ b/library/ui-strings/src/main/res/values-de/strings.xml @@ -2811,6 +2811,6 @@ Der QR-Code ist ungültig. Das andere Gerät muss angemeldet sein. Das andere Gerät ist bereits angemeldet. - Es ist ein Problem bei der Herstellung der sicheren Kommunikation aufgetreten. Eines der folgenden Dinge könnte kompromitiert sein: Dein Heim-Server; deine Internetverbindung(en); dein(e) Gerät(e); + Es ist ein Problem bei der Herstellung der sicheren Kommunikation aufgetreten. Eines der folgenden Dinge könnte kompromittiert sein: Dein Heim-Server; deine Internetverbindung(en); dein(e) Gerät(e); Die Anfrage ist fehlgeschlagen. \ No newline at end of file From 2379ce673be7413fc2446429a8190a19e6ff5580 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 19 Oct 2022 17:02:32 -0400 Subject: [PATCH 124/300] Adds filter event to opt in to thread notifications --- .../sdk/api/session/sync/model/RoomSync.kt | 3 +- .../database/RealmSessionStoreMigration.kt | 4 ++- .../database/migration/MigrateSessionTo041.kt | 30 +++++++++++++++++++ .../query/TimelineEventEntityQueries.kt | 3 +- .../internal/session/filter/FilterFactory.kt | 17 ++--------- .../session/filter/RoomEventFilter.kt | 2 +- .../sdk/internal/session/sync/SyncTask.kt | 4 +-- 7 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo041.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt index 472121269f7..0651b8f3d85 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt @@ -47,11 +47,10 @@ data class RoomSync( */ @Json(name = "unread_notifications") val unreadNotifications: RoomSyncUnreadNotifications? = null, - /** * The count of threads with unread notifications (not the total # of notifications in all threads) */ - @Json(name = "org.matrix.msc3773.unread_thread_notifications") val unreadThreadNotifications: Map? = null, + @Json(name = "unread_thread_notifications") val unreadThreadNotifications: Map? = null, /** * The room summary. 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 9a2c32f97cd..5b2874bdef5 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.MigrateSessionTo041 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 = 41L, ) { /** * 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 < 41) MigrateSessionTo041(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo041.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo041.kt new file mode 100644 index 00000000000..b58d80e50ab --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo041.kt @@ -0,0 +1,30 @@ +/* + * 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.RoomSummaryEntityFields +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +internal class MigrateSessionTo041(realm: DynamicRealm) : RealmMigrator(realm, 41) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("RoomSummaryEntity") + ?.addField(RoomSummaryEntityFields.THREAD_HIGHLIGHT_COUNT, Int::class.java) + ?.addField(RoomSummaryEntityFields.THREAD_NOTIFICATION_COUNT, Int::class.java) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt index 30010f90fd6..1b4b3599165 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt @@ -110,8 +110,7 @@ internal fun RealmQuery.filterEvents(filters: TimelineEvent endGroup() } if (filters.filterUseless) { - not() - .equalTo(TimelineEventEntityFields.ROOT.IS_USELESS, true) + not().equalTo(TimelineEventEntityFields.ROOT.IS_USELESS, true) } if (filters.filterEdits) { not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt index 141144acd8a..77c56497090 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/FilterFactory.kt @@ -29,7 +29,6 @@ internal object FilterFactory { // senders = listOf(userId), // relationSenders = userId?.let { listOf(it) }, relationTypes = listOf(RelationType.THREAD), - enableUnreadThreadNotifications = true, ) } @@ -39,7 +38,6 @@ internal object FilterFactory { containsUrl = true, types = listOf(EventType.MESSAGE), lazyLoadMembers = true, - enableUnreadThreadNotifications = true, ) } @@ -57,32 +55,23 @@ internal object FilterFactory { } fun createDefaultRoomFilter(): RoomEventFilter { - return RoomEventFilter( - lazyLoadMembers = true - ) + return RoomEventFilter(lazyLoadMembers = true) } fun createElementRoomFilter(): RoomEventFilter { return RoomEventFilter( lazyLoadMembers = true, - enableUnreadThreadNotifications = true, // TODO Enable this for optimization // types = (listOfSupportedEventTypes + listOfSupportedStateEventTypes).toMutableList() ) } private fun createElementTimelineFilter(): RoomEventFilter? { - return null // RoomEventFilter().apply { - // TODO Enable this for optimization - // types = listOfSupportedEventTypes.toMutableList() - // } + return RoomEventFilter(enableUnreadThreadNotifications = true) } private fun createElementStateFilter(): RoomEventFilter { - return RoomEventFilter( - lazyLoadMembers = true, - enableUnreadThreadNotifications = true, - ) + return RoomEventFilter(lazyLoadMembers = true) } // Get only managed types by Element diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt index 81d1c042618..082acb63ab4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt @@ -81,7 +81,7 @@ internal data class RoomEventFilter( /** * If true, this will opt-in for the server to return unread threads notifications in [RoomSync] */ - @Json(name = "org.matrix.msc3773.unread_thread_notifications") val enableUnreadThreadNotifications: Boolean? = null, + @Json(name = "unread_thread_notifications") val enableUnreadThreadNotifications: Boolean? = null, ) { fun toJSONString(): String { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index ea296d379dd..bc1a69769d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -140,7 +140,7 @@ internal class DefaultSyncTask @Inject constructor( executeRequest(globalErrorReceiver) { syncAPI.sync( params = requestParams, - readTimeOut = readTimeOut + readTimeOut = readTimeOut, ) } } @@ -178,7 +178,7 @@ internal class DefaultSyncTask @Inject constructor( syncRequestStateTracker.setSyncRequestState( SyncRequestState.IncrementalSyncParsing( rooms = nbRooms, - toDevice = nbToDevice + toDevice = nbToDevice, ) ) syncResponseHandler.handleResponse(syncResponse, token, null) 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 125/300] 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] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index f081e0a8743..0d7ada05874 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 126/300] 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] Signed-off-by: dependabot[bot] 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 d0f093a4517..f162685d7d8 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 12da349316b46ffe56afce3f9204f8e28177fef2 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 19 Oct 2022 20:03:24 +0000 Subject: [PATCH 127/300] Translated using Weblate (German) Currently translated at 100.0% (2512 of 2512 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 | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 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 e8f720d1f5b..cac0a47ad76 100644 --- a/library/ui-strings/src/main/res/values-de/strings.xml +++ b/library/ui-strings/src/main/res/values-de/strings.xml @@ -42,7 +42,7 @@ %s hat diesen Raum aufgewertet. Sende eine Nachricht … Erste Synchronisation: -\nImportiere Benutzerkonto … +\nImportiere Konto … Erste Synchronisation: \nImportiere Kryptoschlüssel Erste Synchronisation: @@ -263,13 +263,13 @@ Nur Matrix-Kontakte Keine Ergebnisse Räume - Logdateien übermitteln + Sende Protokolle Absturzberichte übermitteln Bildschirmfoto übermitteln Problem melden Bitte beschreibe das Problem. Was hast du genau gemacht\? Was sollte passieren\? Was ist tatsächlich passiert\? Problembeschreibung - Um Probleme diagnostizieren zu können, werden Protokolle der Anwendung zusammen mit dem Fehlerbericht übermittelt. Dieser Fehlerbericht wird, wie die Protokolle und das Bildschirmfoto, nicht öffentlich sichtbar sein. Wenn du nur den oben eingegebenen Text senden möchtest, die nachfolgenden Haken entsprechend entfernen: + Um Probleme diagnostizieren zu können, werden Protokolle der Anwendung zusammen mit dem Fehlerbericht übermittelt. Dieser Fehlerbericht wird, inklusive der Protokolle und des Bildschirmfotos, nicht öffentlich sichtbar sein. Wenn du nur den oben eingegebenen Text senden möchtest, entferne die Häkchen: Du scheinst dein Telefon frustriert zu schütteln. Möchtest du das Fenster zum Senden eines Fehlerberichts öffnen\? Dein Fehlerbericht wurde erfolgreich übermittelt Der Fehlerbericht konnte nicht übermittelt werden (%s) @@ -353,7 +353,7 @@ Telefonnummer hinzufügen Anwendungsinformationen in den Systemeinstellungen anzeigen. Anwendungsinformationen - Benachrichtigungen für diesen Account + Benachrichtigungen für dieses Konto Benachrichtigungen für diese Sitzung Direktnachrichten Gruppenunterhaltungen @@ -609,7 +609,7 @@ Schreibbenachrichtigungen senden Lasse andere Benutzer wissen, dass du tippst. Markdown-Formatierung - Formatiere Nachrichten mittels Markdown-Syntax, bevor sie gesendet werden. Dies erlaubt erweiterte Formatierungen wie Sternchen (*), um kursiven Text anzuzeigen. + Formatiere Nachrichten mittels Markdown-Syntax, bevor sie gesendet werden. Dies erlaubt erweiterte Formatierungen wie Sternchen, um kursiven Text anzuzeigen. Lesebestätigungen zeigen Klicke auf die Lesebestätigungen für eine detailliertere Liste. Einladungen, Entfernungen und Verbannungen bleiben sichtbar. @@ -637,7 +637,7 @@ \nBitte überprüfe die Systemeinstellungen. Öffne Einstellungen Kontoeinstellungen. - Benachrichtigungen sind für dein Konto eingeschaltet. + Benachrichtigungen sind für dein Konto aktiviert. Benachrichtigungen sind für dein Konto deaktiviert. \nBitte überprüfe die Kontoeinstellungen. Aktiviere @@ -686,7 +686,7 @@ Fertig Erweiterte Benachrichtigungseinstellungen Angepasste Einstellungen. - Beachte, dass einige Nachrichtentypen leise sind (erzeugen eine Benachrichtigung aber keinen Ton). + Beachte, dass einige Nachrichtentypen leise sind (erzeugen eine Benachrichtigung, aber keinen Ton). Einige Benachrichtigungen sind in deinen erweiterten Einstellungen deaktiviert. Konto hinzufügen Laute Benachrichtigungen einstellen @@ -721,7 +721,7 @@ \nDieser Fehler liegt nicht unter der Kontrolle von ${app_name}. Er kann aus verschiedenen Gründen auftreten. Vielleicht wird es funktionieren, wenn du es später noch einmal probierst. Außerdem kannst Du prüfen, ob die Datennutzung der Google-Play-Dienste unbeschränkt ist und die Geräteuhr richtig eingestellt ist. Der Fehler kann aber auch unter Custom-ROMs auftreten. [%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. - Verwaltung der Kryptoschlüssel + Verwaltung der Verschlüsselungs-Schlüssel Schlüsselsicherung verwalten 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 @@ -759,7 +759,7 @@ Um die Schlüsselsicherung für diese Sitzung zu verwenden, stelle sie jetzt mit deiner Passphrase oder deinem Wiederherstellungsschlüssel wieder her. Deine gesicherten Schlüssel vom Server löschen\? Du wirst deinen Wiederherstellungsschlüssel nicht mehr nutzen können, um deinen verschlüsselten Nachrichtenverlauf zu lesen. Beim Abmelden gehen deine verschlüsselten Nachrichten verloren - Schlüssel-Sicherung wird durchgeführt. Wenn du dich jetzt abmeldest, gehen deine verschlüsselten Nachrichten verloren. + Schlüsselsicherung läuft. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten. Schlüsselsicherung sollte bei allen Sitzungen aktiviert sein, um den Verlust verschlüsselter Nachrichten zu verhindern. Ich möchte meine verschlüsselten Nachrichten nicht Sichere Schlüssel … @@ -781,7 +781,7 @@ (Erweitert) Wiederherstellungsschlüssel einrichten Erfolg! Deine Schlüssel wurden gesichert. - Dein Wiederherstellungsschlüssel ist ein Sicherungsnetz - du kannst es benutzen um den Zugriff auf deine verschlüsselten Nachrichten wiederherzustellen, falls du deine Passphrase vergisst. + 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) Bewahre deinen Wiederherstellungsschlüssel an einem sehr sicheren Ort wie einem Passwortmanager (oder Safe) auf Ich habe eine Kopie angefertigt @@ -1024,7 +1024,7 @@ Es ist unangebracht Benutzerdefinierte Meldung … Diesen Inhalt melden - Meldegrund + Grund für Meldung des Inhalts MELDEN NUTZER IGNORIEREN Inhalt gemeldet @@ -1374,7 +1374,7 @@ Benachrichtigungskonfiguration Nachrichten mit \"@room\" Verschlüsselte Gruppenunterhaltungen - Sendet eine Nachricht als einfachen Text, ohne sie als Markdown zu interpretieren + Sendet eine Nachricht als Klartext, ohne sie als Markdown darzustellen Inkorrekter Benutzername und/oder Passwort. Das eingegebene Passwort beginnt oder endet mit Leerzeichen, bitte kontrolliere es. Nachrichtenschlüssel Wiederherstellungs-Passphrase @@ -1620,7 +1620,7 @@ Neue PIN Um deine PIN zurückzusetzen, musst du dich erneut anmelden und eine neue erstellen. Aktiviere PIN - Wenn du deine PIN zurücksetzen möchtest, tippe \"PIN vergessen\" um dich abzumelden und sie anschließend zurückzusetzen. + Wenn du deine PIN zurücksetzen möchtest, tippe auf „PIN vergessen“, um dich abzumelden und sie zurückzusetzen. Versehentliche Anrufe verhindern Bitte um Bestätigung, bevor du einen Anruf tätigst Einrichten @@ -1661,7 +1661,7 @@ E-Mail und Telefon Verwalte E-Mail-Adressen und Telefonnummern, die mit deinem Matrix-Konto verknüpft sind Code - Verwende das internationale Format (Telefonnummer muss mit \'+\' beginnen) + Bitte nutze das internationale Format (muss mit ‚+‘ beginnen) Bestätige deine Identität, indem du dieses Login verifizierst, um Zugriff auf verschlüsselte Nachrichten zu erhalten. Raum, indem du gebannt wurdest, kann nicht geöffnet werden. Raum kann nicht gefunden werden. Stelle sicher, dass er existiert. @@ -1688,7 +1688,7 @@ Details wie Raumnamen und Nachrichteninhalt zeigen. Inhalt in Benachrichtigungen anzeigen PIN-Code ist die einzige Möglichkeit ${app_name} zu entsperren. - Aktiviere Gerät-spezifische Biometrie wie Fingerabdrücke und Gesichtserkennung. + Aktiviere gerätespezifische Biometrie wie Fingerabdrücke und Gesichtserkennung. Biometrie aktivieren Schutz konfigurieren Zugriffsschutz @@ -1726,10 +1726,10 @@ Du siehst die Benachrichtigung! Klick mich! Benachrichtigungsanzeige Bei jedem Öffnen von ${app_name} ist der PIN-Code erforderlich. - PIN-Code ist erforderlich, nachdem ${app_name} 2 Minuten lang nicht verwendet wurde. - Fordere PIN nach 2 Minuten an + PIN-Code ist erforderlich, nachdem ${app_name} zwei Minuten lang nicht verwendet wurde. + Erfrage PIN nach zwei Minuten Nur die Anzahl ungelesener Nachrichten in der Benachrichtigung zeigen. - Bild hinzufügen mit + Füge Bild hinzu per Der Raum ist noch nicht erstellt. Raumerstellung abbrechen\? Zu niedrige Priorität hinzufügen Thema @@ -1742,7 +1742,7 @@ Raumname Prüfung exportieren Direktnachricht - Verlauf der Anfragen von Schlüsselfreigaben senden + Schlüsselfreigabe-Anfragen übermitteln Keine weiteren Ergebnisse Beginne eine Unterhaltung Autorisieren @@ -1762,7 +1762,7 @@ Änderungen daran, wer die Chronik lesen kann, gelten nur für kommende Nachrichten in diesem Raum. Die Sichtbarkeit der bestehenden Chronik bleibt unverändert. Zurückziehen Hinzufügen - Mit Nachricht teilen + Per Nachricht teilen Erweiterte Optionen ausblenden Erweiterte Optionen anzeigen Die Sichtbarkeit des Raums konnte nicht abgerufen werden (%1$s). @@ -1820,9 +1820,9 @@ Raumname ändern Sichtbarkeit des Verlaufs ändern Raum-Verschlüsselung aktivieren - Haupt-Adresse des Raums ändern + Hauptadresse des Raums ändern Raumbild ändern - Widgets verändern + Widgets ändern Jeden benachrichtigen Von anderen gesendete Nachrichten entfernen Nutzer verbannen @@ -1830,7 +1830,7 @@ Einstellungen ändern Nutzer einladen Nachrichten senden - Standard Rolle + Standard-Rolle Berechtigungen Berechtigungen Du hast nicht die Berechtigung zum Aktualisieren der Rollen, die zum Ändern verschiedener Teile des Raums erforderlich sind @@ -1918,9 +1918,9 @@ Die Obergrenze ist nicht bekannt. Dein Heim-Server akzeptiert Anhänge (wie Dateien, Medien, etc.) mit einer Größe bis zu %s. - Datei-Upload-Obergrenze des Servers + Dateigrößenlimit des Servers Serverversion - Servername + Server-Name Raumeinstellungen Derzeitige Konferenz verlassen und zu einer anderen wechseln\? Raum-Version @@ -1957,8 +1957,8 @@ Spaces Jeder kann im Raum anklopfen, Mitglieder können dann zustimmen oder ablehnen Momentan bist nur du hier. Mit anderen Leuten wird %s noch viel besser. - Diese werden in der Lage sein, %s zu durchsuchen - Diese werden kein Teil von %s sein + Sie wird in der Lage sein, %s zu durchsuchen + Sie wird kein Teil von %s sein Tritt meinem Space %1$s %2$s bei Spaces sind eine neue Möglichkeit, Räume und Personen zu gruppieren. Räume oder Spaces hinzufügen @@ -1979,7 +1979,7 @@ Space beitreten Space erstellen Nur zu diesem Raum - In Space \"%s\" einladen + Zu %s einladen Link teilen Mithilfe einer E-Mail-Adresse einladen Personen einladen @@ -2067,7 +2067,7 @@ Beim Versuch %s beizutreten, ist leider ein Fehler aufgetreten Zur empfohlenen Raumversion upgraden Ersatzraum betreten - Raum zu neuer Version upgraden + Aktualisiert den Raum auf eine neue Version stabil instabil Raumversionen 👓 @@ -2108,7 +2108,7 @@ Spaces wählen Mitglieder von %s können Räume finden, betrachten und betreten. Privat (Zutritt nur mit Einladung) - Raumupgrades + Raumaktualisierungen Nachrichten von Bots Raumeinladungen Verschlüsselte Gruppennachrichten @@ -2192,9 +2192,9 @@ Neuen Space erstellen Zugriff Wer hat Zugriff\? - Benachrichtigungen per Email für %s aktivieren + Benachrichtigungen per E-Mail für %s aktivieren Um Benachrichtigungen per E-Mail zu empfangen, musst du eine E-Mail-Adresse hinzufügen - Emailbenachrichtigungen + E-Mail-Benachrichtigungen Space upgraden Namen vom Space ändern Space verschlüsseln @@ -2224,7 +2224,7 @@ Blockiert eine Person und versteckt deren Nachrichten Jeder kann den Space finden und beitreten Du kannst deine Benachrichtigungen in den %1$s verwalten. - Beachte, dass Benachrichtigungen zu Erwähnungen und Schlüsselwörtern in verschlüsselten Räumen momentan nicht verfügbar sind. + Bitte beachte, dass Benachrichtigungen zu Erwähnungen und Schlüsselwörtern in verschlüsselten Räumen mobil nicht verfügbar sind. Wähle die Berechtigungen der Rollen aus Rollen deren Berechtigungen einsehen und bearbeiten. @@ -2253,7 +2253,7 @@ Externe Bibliotheken Du kannst dies jederzeit in den Einstellungen deaktivieren Wir teilen keine Informationen mit Drittpersonen - Wir erfassen und analysieren keine Accountdaten + Wir erfassen und analysieren keine Kontodaten Hilf uns dabei Probleme zu identifizieren und ${app_name} zu verbessern, indem du anonyme Nutzungsdaten teilst. Um zu verstehen, wie Personen mehrere Geräte benutzen, werden wir eine zufällige Kennung generieren, die zwischen deinen Geräten geteilt wird. \n \n%s kannst du alle unsere Bedingungen lesen. @@ -2269,7 +2269,7 @@ Hilfe Rechtliches Entscheide, welche Spaces Zugriff auf den Raum haben sollen. Die Mitglieder der Spaces können diesen Räumen betreten. - hier + Hier Hilf mit, ${app_name} zu verbessern Aktivieren Farbe des Anzeigenamens ändern @@ -2741,8 +2741,8 @@ ⚠ Es befinden sich nicht verifizierte Geräte in diesem Raum. Sie werden deine Nachrichten nicht entschlüsseln können. Niemals verschlüsselte Nachrichten zu unverifizierten Sitzungen in diesem Raum senden. Verstanden - Probiere den Rich-Text-Editor aus (Klartext-Modus kommt bald) - Aktiviere Rich-Text-Editor + Probiere den Textverarbeitungs-Editor (bald auch mit Klartext-Modus) + Textverarbeitungs-Editor aktivieren Browser Durchgestrichen formatieren Kursiv formatieren From 5f90f135aebd3a33f5fbfc68d2a7ec6fcd630104 Mon Sep 17 00:00:00 2001 From: Glandos Date: Wed, 19 Oct 2022 20:42:29 +0000 Subject: [PATCH 128/300] Translated using Weblate (French) Currently translated at 100.0% (2512 of 2512 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 | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) 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 b77173519d7..67396a390fb 100644 --- a/library/ui-strings/src/main/res/values-fr/strings.xml +++ b/library/ui-strings/src/main/res/values-fr/strings.xml @@ -2772,4 +2772,46 @@ \nVeuillez autoriser l’accès sur la prochaine fenêtre pour pouvoir voir des notifications. Essayer l’éditeur de texte formaté (le mode texte brut arrive bientôt) Activer l’éditeur de texte formaté + Vérifiez l’origine de ce code. En appairant un appareil, vous lui fournissez un accès complet à votre compte. + Confirmer + Réessayez + Pas de correspondance \? + Connexion + Connexion à l’appareil + Scanner le QR code + Connexion sur un appareil mobile \? + Afficher le QR code sur cet appareil + Sélectionnez « Scanner le QR code » + Démarrez à l’écran de connexion + Sélectionnez « Se connecter avec un QR code » + Démarrez à l’écran de connexion + Sélectionnez « Afficher le QR code » + Allez dans Réglages -> Confidentialité et sécurité + Ouvrez l’application sur votre autre appareil + Le serveur d’accueil ne prend pas en charge la connexion avec un QR code. + La connexion a été annulée sur l’autre appareil. + Ce QR code est invalide. + L’autre appareil doit être connecté. + L’autre appareil est déjà connecté. + La configuration de la messagerie sécurisée a rencontré un problème de sécurité. Un des éléments suivants pourrait être compromis : votre serveur d’accueil ; votre connexion Internet ; votre (vos) appareil(s) ; + La requête a échoué. + La requête a été refusée sur l’autre appareil. + L’appairage n’a pas été effectué dans le temps imparti. + L’appairage avec cet appareil n’est pas pris en charge. + Échec de la connexion + Vérifiez votre appareil connecté, le code ci-dessous devrait y être affiché. Confirmez que le code ci-dessous correspond à celui de l’autre appareil : + Connexion sécurisée établie + Scannez le QR code ci-dessous avec l’appareil qui n’est pas connecté. + Utilisez votre appareil connecté pour scanner le QR code ci-dessous : + Se connecter avec un QR code + Utilisez l’appareil photo de cet appareil pour scanner le QR code affiché sur votre autre appareil : + Scanner le QR code + 3 + 2 + 1 + Pouvoir enregistrer et envoyer une diffusion audio dans l’historique du salon. + Activer la diffusion audio (en cours de développement) + Vous pouvez utiliser cet appareil pour connecter un appareil mobile ou un client web avec un QR code. Il y a deux façons de le faire : + Se connecter avec un QR code + Scanner le QR code \ No newline at end of file From 7796d97a98fa9de8fde2172bfe6b22eb437dbe94 Mon Sep 17 00:00:00 2001 From: phardyle Date: Thu, 20 Oct 2022 02:49:51 +0000 Subject: [PATCH 129/300] Translated using Weblate (Chinese (Simplified)) Currently translated at 94.5% (2376 of 2512 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 | 7 +++++++ 1 file changed, 7 insertions(+) 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 ae29132c913..f1fe3a496fe 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 @@ -2625,4 +2625,11 @@ 简化的Element,带有可选的标签 无痕键盘 要求键盘不要基于你在对话中的输入更新任何个性化数据,如输入历史和字典。请注意,某些键盘可能不会遵守此设置。 + ${app_name}需要权限来显示通知。通知可以显示消息、邀请等。 +\n +\n请在下个弹窗允许访问以便查看通知。 + 试用富文本编辑器(纯文本模式即将到来) + 启用富文本编辑器 + 折叠%s孩子 + 展开%s孩子 \ No newline at end of file From e0978a2fc3d5115b1638d1013ae7444f69c07af3 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 20 Oct 2022 01:42:46 +0000 Subject: [PATCH 130/300] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2512 of 2512 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 | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 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 c9057cd2893..d1f81567c2b 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 @@ -2731,9 +2731,9 @@ 從登入畫面開始 選取「使用 QR code 登入」 從登入畫面開始 - 選取「在此裝置上顯示 QR code」 - 到「設定」→「安全與隱私」→「顯示所有工作階段」 - 在您的其他裝置上開啟 ${app_name} + 選取「顯示 QR code」 + 到「設定」→「安全與隱私」 + 在您的其他裝置上開啟應用程式 請求在另一台裝置上被拒絕。 連結未在規定時間內完成。 不支援與其裝置連結。 @@ -2751,4 +2751,13 @@ 您可以使用此裝置透過 QR code 登入移動裝置或網路裝置。有兩種方法可以作到: 使用 QR code 登入 掃描 QR code + 家伺服器不支援使用 QR code 登入。 + 登入已在其他裝置上取消。 + 該 QR code 無效。 + 其他裝置必須登入。 + 其他裝置已登入。 + 設定安全訊息傳遞時遇到安全問題。以下其中一項可能已被駭入:您的家伺服器、您的網際網路連線、您的裝置; + 請求失敗。 + 可以在聊天室時間軸中錄製並傳送語音廣播。 + 啟用語音廣播(正在積極開發中) \ No newline at end of file From a5ad00a06532bff235c163d37c3c89e207bf5181 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 19 Oct 2022 19:02:49 +0000 Subject: [PATCH 131/300] Translated using Weblate (German) Currently translated at 100.0% (79 of 79 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 +- fastlane/metadata/android/de-DE/changelogs/40105040.txt | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/de-DE/changelogs/40105040.txt diff --git a/fastlane/metadata/android/de-DE/changelogs/40105000.txt b/fastlane/metadata/android/de-DE/changelogs/40105000.txt index cd3ec933879..254c0fe0d84 100644 --- a/fastlane/metadata/android/de-DE/changelogs/40105000.txt +++ b/fastlane/metadata/android/de-DE/changelogs/40105000.txt @@ -1,2 +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 +Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases diff --git a/fastlane/metadata/android/de-DE/changelogs/40105020.txt b/fastlane/metadata/android/de-DE/changelogs/40105020.txt index ac08e662db8..af7a8d7ccec 100644 --- a/fastlane/metadata/android/de-DE/changelogs/40105020.txt +++ b/fastlane/metadata/android/de-DE/changelogs/40105020.txt @@ -1,2 +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 +Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases diff --git a/fastlane/metadata/android/de-DE/changelogs/40105040.txt b/fastlane/metadata/android/de-DE/changelogs/40105040.txt new file mode 100644 index 00000000000..017e23cd9e7 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40105040.txt @@ -0,0 +1,2 @@ +Die wichtigste Änderung in dieser Version: Neue Funktionen in den Labor-Einstellungen: Textverarbeitungs-Editor, neue Geräteverwaltung, Sprachübertragung. Noch in aktiver Entwicklung! +Vollständiges Änderungsprotokoll: https://github.com/vector-im/element-android/releases From dc6722ec226e6d8101fe6e03236f06547a8368f4 Mon Sep 17 00:00:00 2001 From: Nui Harime Date: Wed, 19 Oct 2022 20:22:04 +0000 Subject: [PATCH 132/300] Translated using Weblate (Russian) Currently translated at 88.6% (70 of 79 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/ru/ --- fastlane/metadata/android/ru-RU/changelogs/40105020.txt | 2 ++ fastlane/metadata/android/ru-RU/changelogs/40105040.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 fastlane/metadata/android/ru-RU/changelogs/40105020.txt create mode 100644 fastlane/metadata/android/ru-RU/changelogs/40105040.txt diff --git a/fastlane/metadata/android/ru-RU/changelogs/40105020.txt b/fastlane/metadata/android/ru-RU/changelogs/40105020.txt new file mode 100644 index 00000000000..83bf3c747b5 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/40105020.txt @@ -0,0 +1,2 @@ +Основные изменения в этой версии: новый вид приложения включён по умолчанию! +Весь список изменений: https://github.com/vector-im/element-android/releases diff --git a/fastlane/metadata/android/ru-RU/changelogs/40105040.txt b/fastlane/metadata/android/ru-RU/changelogs/40105040.txt new file mode 100644 index 00000000000..c923750bc4b --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/40105040.txt @@ -0,0 +1,2 @@ +Основные изменения в этой версии — новые возможности в настройках лаборатории: наглядный текстовый редактор, новое управление устройствами, голосовая трансляция. Всё это ещё находится в активной разработке! +Весь список изменений: https://github.com/vector-im/element-android/releases From 1b8308c12bd2eda4d7010509f5a7f3f02242be66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 19 Oct 2022 18:40:32 +0000 Subject: [PATCH 133/300] Translated using Weblate (Estonian) Currently translated at 100.0% (79 of 79 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/et/ --- fastlane/metadata/android/et/changelogs/40105040.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/et/changelogs/40105040.txt diff --git a/fastlane/metadata/android/et/changelogs/40105040.txt b/fastlane/metadata/android/et/changelogs/40105040.txt new file mode 100644 index 00000000000..b1c84cad473 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40105040.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: Uued võimalused katsete all: vormindatud teksti põhine toimeti, uus seadmehaldus, ringhäälingukõned (kõik on hetkel aktiivsel arendamisel). +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases From b95ae7d36a2767d7363d92098f8f82cdb4f38ee2 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 20 Oct 2022 01:44:30 +0000 Subject: [PATCH 134/300] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (79 of 79 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/40105040.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40105040.txt diff --git a/fastlane/metadata/android/zh-TW/changelogs/40105040.txt b/fastlane/metadata/android/zh-TW/changelogs/40105040.txt new file mode 100644 index 00000000000..b35b1185b91 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40105040.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:實驗室設定下有新功能:格式化文字編輯器、新裝置管理、語音廣播。仍在積極開發中! +完整的變更紀錄:https://github.com/vector-im/element-android/releases From 08271218e6cc2c50a28f2253fe372dbee5c9d3b2 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Wed, 19 Oct 2022 18:43:55 +0000 Subject: [PATCH 135/300] Translated using Weblate (Czech) Currently translated at 100.0% (79 of 79 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/40105040.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40105040.txt diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40105040.txt b/fastlane/metadata/android/cs-CZ/changelogs/40105040.txt new file mode 100644 index 00000000000..c1bf4fd59aa --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40105040.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Nové funkce v Experimentálních funkcích: Rozšířený editor zpráv, nová správa zařízení, hlasové vysílání. Stále v aktivním vývoji! +Úplný seznam změn: https://github.com/vector-im/element-android/releases From 6554f571f22a45212ee926c55b2da93d63716826 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 19 Oct 2022 11:18:35 +0200 Subject: [PATCH 136/300] 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 dfd50ea5cb9..e93e128686f 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 Date: Wed, 19 Oct 2022 13:04:03 +0200 Subject: [PATCH 137/300] 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 e93e128686f..c7259b12f8c 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 Date: Wed, 19 Oct 2022 13:16:54 +0200 Subject: [PATCH 138/300] 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 c7259b12f8c..db075039272 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() 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 Date: Wed, 19 Oct 2022 13:17:59 +0200 Subject: [PATCH 139/300] 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 db075039272..72ec1819660 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() - 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 Date: Wed, 19 Oct 2022 18:07:44 +0200 Subject: [PATCH 140/300] 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 71daf4cc4f2..1f16041b543 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()?.relatesTo in EventType.BEACON_LOCATION_DATA -> getClearContent().toModel()?.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 20094e4be8a..2d6082f9b56 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() + ?.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 b967afa9cbe..58e7de7f323 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 72ec1819660..7f5e13504ef 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() 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 { + 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) { + 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) { + 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 00000000000..5b3153ea403 --- /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 Date: Thu, 20 Oct 2022 02:22:59 +0200 Subject: [PATCH 141/300] 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 404b1125746..21d12ee9862 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 Date: Thu, 20 Oct 2022 11:24:45 +0200 Subject: [PATCH 142/300] 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 7cb66cd9e57..d5d58f822e8 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 Date: Thu, 20 Oct 2022 14:03:42 +0200 Subject: [PATCH 143/300] 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 1824d5dc6c5..9ac33c05451 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) = Unit From 94390697ae045e620452f6e99331c9dabf8c3e81 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 20 Oct 2022 13:18:46 +0200 Subject: [PATCH 144/300] 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 7f5e13504ef..c55cb8a1d00 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 Date: Thu, 20 Oct 2022 13:32:16 +0200 Subject: [PATCH 145/300] 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 00000000000..06f69dfa7f5 --- /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 Date: Thu, 20 Oct 2022 13:43:50 +0200 Subject: [PATCH 146/300] 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 37ff920c578..c9bb0c5f54d 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 21d12ee9862..a65aae6f8a7 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 a9db63c5385..d882d4049e9 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 835a57c1021..1430dd8c86d 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 6eefa069790..bc6a3e7be6b 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 Date: Thu, 20 Oct 2022 14:36:47 +0200 Subject: [PATCH 147/300] 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 c55cb8a1d00..403675a9ee7 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 { @@ -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 5b3153ea403..d08fa14a95e 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 Date: Thu, 20 Oct 2022 19:30:17 +0200 Subject: [PATCH 148/300] 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 403675a9ee7..62252570c6d 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 Date: Thu, 20 Oct 2022 17:58:03 +0200 Subject: [PATCH 149/300] 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 e6714005a1d..69b4d57e289 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 @@ %1$s (%2$s) (%1$s) + Live + Resume voice broadcast record + Pause voice broadcast record + Stop voice broadcast record + 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. 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. diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml index 52d16eae7de..50d5aaf0143 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 @@ 12dp 22dp + + 48dp + 112dp 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 cb947a67ce9..245d92f95b3 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 f2dfb020a13..1064d2bbc52 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 1927024a36c..00000000000 --- 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() { - - @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(R.id.currentStateText) - val playButton by bind(R.id.playButton) - val pauseButton by bind(R.id.pauseButton) - val stopButton by bind(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 00000000000..d271c55ebb5 --- /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() { + + @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(R.id.liveIndicator) + val roomAvatarImageView by bind(R.id.roomAvatarImageView) + val titleText by bind(R.id.titleText) + val recordButton by bind(R.id.recordButton) + val stopRecordButton by bind(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 c9bb0c5f54d..8b69051823b 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 a65aae6f8a7..cd1a61b9861 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() 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 d5d58f822e8..7934d18e360 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 00000000000..7d427a56d05 --- /dev/null +++ b/vector/src/main/res/drawable/ic_live_broadcast_16.xml @@ -0,0 +1,21 @@ + + + + + + + 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 00000000000..f5d92f9718a --- /dev/null +++ b/vector/src/main/res/drawable/ic_recording_dot.xml @@ -0,0 +1,9 @@ + + + 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 00000000000..459a7cfce21 --- /dev/null +++ b/vector/src/main/res/drawable/ic_stop.xml @@ -0,0 +1,9 @@ + + + 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 00000000000..977de2fd09d --- /dev/null +++ b/vector/src/main/res/drawable/rounded_rect_shape_2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + 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 e35060f72ac..6773280ba5b 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"> + + + tools:src="@sample/user_round_avatars" /> + + + + + + - - + app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" /> + app:layout_constraintStart_toEndOf="@id/recordButton" + app:layout_constraintTop_toTopOf="@id/recordButton" /> From f1b4ebbc37f266702327fc9db517c8d142178387 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 20 Oct 2022 18:10:32 +0200 Subject: [PATCH 150/300] 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 1064d2bbc52..13a38ac4be6 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? { // 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 00000000000..e5d0fd6c301 --- /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() { + + @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(R.id.liveIndicator) + val roomAvatarImageView by bind(R.id.roomAvatarImageView) + val titleText by bind(R.id.titleText) + val recordButton by bind(R.id.recordButton) + val stopRecordButton by bind(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 d271c55ebb5..30475c1659a 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 + + + + + + + + + + + + + + + + + + + From f711a0ea740f14f4d63eff35d67355e5224bdf57 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 20 Oct 2022 19:07:34 +0200 Subject: [PATCH 151/300] 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 | 30 +++--- 5 files changed, 97 insertions(+), 61 deletions(-) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 69b4d57e289..ea9b4b59998 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 @@ Resume voice broadcast record Pause voice broadcast record Stop voice broadcast record + Play or resume voice broadcast + Pause voice broadcast + Buffering 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. 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. 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 13a38ac4be6..af14d532c8f 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? { // 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 e5d0fd6c301..3a090b0eb64 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 + renderState(holder, state) } - voiceBroadcastRecorder?.addListener(recorderListener) + voiceBroadcastPlayer?.addListener(playerListener) renderHeader(holder) } @@ -80,45 +81,59 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem { - 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 + private fun renderState(holder: Holder, state: VoiceBroadcastPlayer.State) { + if (isCurrentMediaActive()) { + renderActiveMedia(holder, state) + } else { + renderInactiveMedia(holder) + } + } - liveIndicator.isVisible = true - liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary)) + @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)) - 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) } + when (state) { + 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.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(R.id.liveIndicator) val roomAvatarImageView by bind(R.id.roomAvatarImageView) val titleText by bind(R.id.titleText) - val recordButton by bind(R.id.recordButton) - val stopRecordButton by bind(R.id.stopRecordButton) + val playPauseButton by bind(R.id.playPauseButton) + val bufferingView by bind(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 62252570c6d..9c118f957d9 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() - 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() 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 6773280ba5b..506ca4ff477 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" /> + app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" + app:tint="?vctr_content_secondary" /> - + app:layout_constraintStart_toEndOf="@id/playPauseButton" + app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" /> From cebc096ac70dad17ebef02f5cc3882ae2b5e93d2 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 20 Oct 2022 21:06:27 +0200 Subject: [PATCH 152/300] 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 af14d532c8f..5a70f66d751 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 3a090b0eb64..bf0f202f0ce 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 { + 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 { @@ -118,7 +141,6 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem Date: Thu, 20 Oct 2022 21:11:00 +0200 Subject: [PATCH 153/300] 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 9c118f957d9..28ed7a58b83 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() - 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 Date: Thu, 20 Oct 2022 21:20:17 +0200 Subject: [PATCH 154/300] 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 506ca4ff477..6a4fcef77aa 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" /> - + From 72a1acec8959a0db909e529f0e7ac585d537cf2f Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 20 Oct 2022 21:26:39 +0200 Subject: [PATCH 155/300] 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 28ed7a58b83..bb5fe5c37d0 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 Date: Thu, 20 Oct 2022 22:38:02 +0200 Subject: [PATCH 156/300] 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 5a70f66d751..98f45cd5db2 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 bf0f202f0ce..130d44202f0 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(R.id.titleText) val playPauseButton by bind(R.id.playPauseButton) val bufferingView by bind(R.id.bufferingView) + val broadcasterNameText by bind(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 6a4fcef77aa..73acde29427 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" /> + tools:src="@sample/rooms.json/data/name" /> + + + + + + + + + + + + + + + app:constraint_referenced_ids="roomAvatarImageView,titleText,broadcasterViewGroup,voiceBroadcastViewGroup" /> From 8869d82dd0f7a3e2aa232456f7cf324daaf04d16 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 20 Oct 2022 22:39:27 +0200 Subject: [PATCH 157/300] 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 00000000000..4a399eee041 --- /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 Date: Thu, 20 Oct 2022 22:45:22 +0200 Subject: [PATCH 158/300] 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 98f45cd5db2..5dc601a91a2 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 Date: Thu, 20 Oct 2022 22:48:09 +0200 Subject: [PATCH 159/300] 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 130d44202f0..5b58dda4e6c 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 { 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 { stopRecordButton.isEnabled = true @@ -104,8 +105,8 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem { recordButton.isEnabled = false From 4a76998c98bec82af2cbe8e436f7edbd07f7bfaa Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 20 Oct 2022 22:50:09 +0200 Subject: [PATCH 160/300] 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 bb5fe5c37d0..2c892c8306f 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() + private var listeners = CopyOnWriteArrayList() 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 cd1a61b9861..5285dc5e3be 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() + private val listeners = CopyOnWriteArrayList() 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 Date: Thu, 20 Oct 2022 23:43:33 +0200 Subject: [PATCH 161/300] 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 73acde29427..248c04a2f62 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 Date: Thu, 20 Oct 2022 23:58:17 +0200 Subject: [PATCH 162/300] 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 41ef1fac9cd..c417053b2a5 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 { stopRecordButton.isEnabled = true + recordButton.isEnabled = true liveIndicator.isVisible = true liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.colorError)) @@ -99,6 +100,7 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem { stopRecordButton.isEnabled = true + recordButton.isEnabled = true liveIndicator.isVisible = true liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary)) From 1086ed367eae93be7443eb9b42b82635244579ed Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 20 Oct 2022 19:42:06 -0400 Subject: [PATCH 163/300] Fixes thread notifications instantly disappearing --- .../room/summary/RoomSummaryUpdater.kt | 8 ++- .../home/room/detail/TimelineViewModel.kt | 51 +++++++++++++------ 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index c740e072573..7c83a4afa79 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -114,15 +114,13 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?: 0 roomSummaryEntity.threadHighlightCount = unreadThreadNotifications - ?.map { it.value.highlightCount.takeIf { count -> (count ?: 0) > 0 } } - ?.size + ?.count { (it.value.highlightCount ?: 0) > 0 } ?: 0 + roomSummaryEntity.threadNotificationCount = unreadThreadNotifications - ?.map { it.value.notificationCount.takeIf { count -> (count ?: 0) > 0 } } - ?.size + ?.count { (it.value.notificationCount ?: 0) > 0 } ?: 0 - if (membership != null) { roomSummaryEntity.membership = membership } 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 511fd597fe1..c30d7a648d4 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 @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail import android.net.Uri import androidx.annotation.IdRes +import androidx.lifecycle.asFlow import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading @@ -407,22 +408,40 @@ class TimelineViewModel @AssistedInject constructor( * Observe local unread threads. */ private fun observeLocalThreadNotifications() { - if (room == null) return - room.flow() - .liveLocalUnreadThreadList() - .execute { - val threadList = it.invoke() - val isUserMentioned = threadList?.firstOrNull { threadRootEvent -> - threadRootEvent.root.threadDetails?.threadNotificationState == ThreadNotificationState.NEW_HIGHLIGHTED_MESSAGE - }?.let { true } ?: false - val numberOfLocalUnreadThreads = threadList?.size ?: 0 - copy( - threadNotificationBadgeState = ThreadNotificationBadgeState( - numberOfLocalUnreadThreads = numberOfLocalUnreadThreads, - isUserMentioned = isUserMentioned - ) - ) - } + val threadNotificationsSupported = session.homeServerCapabilitiesService().getHomeServerCapabilities().canUseThreadReadReceiptsAndNotifications + if (threadNotificationsSupported) { + room?.getRoomSummaryLive() + ?.asFlow() + ?.onEach { + it.getOrNull()?.let { + setState { + copy( + threadNotificationBadgeState = ThreadNotificationBadgeState( + numberOfLocalUnreadThreads = it.threadNotificationCount + it.threadHighlightCount, + isUserMentioned = it.threadHighlightCount > 0, + ) + ) + } + } + } + ?.launchIn(viewModelScope) + } else { + room?.flow() + ?.liveLocalUnreadThreadList() + ?.execute { + val threadList = it.invoke() + val isUserMentioned = threadList?.firstOrNull { threadRootEvent -> + threadRootEvent.root.threadDetails?.threadNotificationState == ThreadNotificationState.NEW_HIGHLIGHTED_MESSAGE + }?.let { true } ?: false + val numberOfLocalUnreadThreads = threadList?.size ?: 0 + copy( + threadNotificationBadgeState = ThreadNotificationBadgeState( + numberOfLocalUnreadThreads = numberOfLocalUnreadThreads, + isUserMentioned = isUserMentioned + ) + ) + } + } } override fun handle(action: RoomDetailAction) { From f76490130cab871b7dade83b7a1a03aaeb0ae48c Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 20 Oct 2022 19:49:37 -0400 Subject: [PATCH 164/300] Adds changelog file --- changelog.d/7424.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7424.misc diff --git a/changelog.d/7424.misc b/changelog.d/7424.misc new file mode 100644 index 00000000000..6c6673eaf07 --- /dev/null +++ b/changelog.d/7424.misc @@ -0,0 +1 @@ +Gets thread notifications from sync response From 8a4d918b250325949c2bb6c0a06bc99c586a7bf9 Mon Sep 17 00:00:00 2001 From: Kat Gerasimova Date: Fri, 21 Oct 2022 09:59:40 +0100 Subject: [PATCH 165/300] 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 18ce26171c6..34e36a55da9 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 166/300] =?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 8c14ca892aa..773e870ffd9 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 75639c6a218..d443d6e3c8c 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 5e133fab9cc..1245d8df4b6 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 9a2c32f97cd..def0f6de7a7 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 63fa101c45c..3528ca0051d 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 00000000000..b3e02342dd1 --- /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 cfa02b2c743..89f1e50b309 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 2c3cb440b62..a5953d870c8 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 Date: Fri, 21 Oct 2022 17:36:31 +0100 Subject: [PATCH 167/300] 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 --- 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 00000000000..8f014af31bd --- /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 1a364744707..2fc55bad0c0 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 2e4336c9422..cbf9e4f11f8 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 { - 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 { + return matrix.debugService().getAllRealmConfigurations() + } + }) + ) + ) + client.start() } - override fun networkInterceptor() = FlipperOkhttpInterceptor(networkFlipperPlugin) + override fun networkInterceptor(): Interceptor? { + if (!isEnabled) return null + + return FlipperOkhttpInterceptor(networkFlipperPlugin) + } } From bec71438247f6fdf67b429499d92731ed2264259 Mon Sep 17 00:00:00 2001 From: Jonny Andrew Date: Thu, 20 Oct 2022 16:13:17 +0100 Subject: [PATCH 168/300] Add new attachments selection dialog --- .../src/main/res/values/strings.xml | 10 ++ .../app/core/di/MavericksViewModelModule.kt | 6 ++ .../core/ui/views/BottomSheetActionButton.kt | 5 + .../features/attachments/AttachmentType.kt | 37 ++++++++ .../AttachmentTypeSelectorBottomSheet.kt | 79 ++++++++++++++++ ...chmentTypeSelectorSharedActionViewModel.kt | 30 ++++++ .../attachments/AttachmentTypeSelectorView.kt | 70 +++++++------- .../AttachmentTypeSelectorViewModel.kt | 59 ++++++++++++ .../features/attachments/AttachmentsHelper.kt | 2 +- .../composer/MessageComposerFragment.kt | 64 ++++++++----- .../bottom_sheet_attachment_type_selector.xml | 92 +++++++++++++++++++ .../AttachmentTypeSelectorViewModelTest.kt | 91 ++++++++++++++++++ .../app/test/fakes/FakeVectorFeatures.kt | 8 ++ 13 files changed, 492 insertions(+), 61 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/attachments/AttachmentType.kt create mode 100644 vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorBottomSheet.kt create mode 100644 vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorSharedActionViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModel.kt create mode 100644 vector/src/main/res/layout/bottom_sheet_attachment_type_selector.xml create mode 100644 vector/src/test/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModelTest.kt diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index ea9b4b59998..9ce87199b07 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3205,6 +3205,16 @@ Share location Start a voice broadcast + Photo library + Stickers + Attachments + Voice broadcast + Polls + Location + Camera + Contact + Text formatting + Show less "%1$d more" 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 97590028d82..2242abb7aa3 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 @@ -22,6 +22,7 @@ import dagger.hilt.InstallIn import dagger.multibindings.IntoMap import im.vector.app.features.analytics.accountdata.AnalyticsAccountDataViewModel import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewModel +import im.vector.app.features.attachments.AttachmentTypeSelectorViewModel import im.vector.app.features.auth.ReAuthViewModel import im.vector.app.features.call.VectorCallViewModel import im.vector.app.features.call.conference.JitsiCallViewModel @@ -677,4 +678,9 @@ interface MavericksViewModelModule { @IntoMap @MavericksViewModelKey(VectorSettingsLabsViewModel::class) fun vectorSettingsLabsViewModelFactory(factory: VectorSettingsLabsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(AttachmentTypeSelectorViewModel::class) + fun attachmentTypeSelectorViewModelFactory(factory: AttachmentTypeSelectorViewModel.Factory): MavericksAssistedViewModelFactory<*, *> } diff --git a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt index a3e8b3780cd..d16bf67a340 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt @@ -38,6 +38,11 @@ class BottomSheetActionButton @JvmOverloads constructor( ) : FrameLayout(context, attrs, defStyleAttr) { val views: ViewBottomSheetActionButtonBinding + override fun setOnClickListener(l: OnClickListener?) { + super.setOnClickListener(l) + views.bottomSheetActionClickableZone.setOnClickListener(l) + } + var title: String? = null set(value) { field = value diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentType.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentType.kt new file mode 100644 index 00000000000..f4b97b9f9c6 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentType.kt @@ -0,0 +1,37 @@ +/* + * 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.attachments + +import im.vector.app.core.utils.PERMISSIONS_EMPTY +import im.vector.app.core.utils.PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING +import im.vector.app.core.utils.PERMISSIONS_FOR_PICKING_CONTACT +import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO +import im.vector.app.core.utils.PERMISSIONS_FOR_VOICE_BROADCAST + +/** + * The all possible types to pick with their required permissions. + */ +enum class AttachmentType(val permissions: List) { + CAMERA(PERMISSIONS_FOR_TAKING_PHOTO), + GALLERY(PERMISSIONS_EMPTY), + FILE(PERMISSIONS_EMPTY), + STICKER(PERMISSIONS_EMPTY), + CONTACT(PERMISSIONS_FOR_PICKING_CONTACT), + POLL(PERMISSIONS_EMPTY), + LOCATION(PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING), + VOICE_BROADCAST(PERMISSIONS_FOR_VOICE_BROADCAST), +} diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorBottomSheet.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorBottomSheet.kt new file mode 100644 index 00000000000..88cad2ab0bc --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorBottomSheet.kt @@ -0,0 +1,79 @@ +/* + * 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.attachments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.viewModels +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.parentFragmentViewModel +import com.airbnb.mvrx.withState +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.app.databinding.BottomSheetAttachmentTypeSelectorBinding +import im.vector.app.features.home.room.detail.TimelineViewModel + +@AndroidEntryPoint +class AttachmentTypeSelectorBottomSheet : VectorBaseBottomSheetDialogFragment() { + + private val viewModel: AttachmentTypeSelectorViewModel by fragmentViewModel() + private val timelineViewModel: TimelineViewModel by parentFragmentViewModel() + private val sharedActionViewModel: AttachmentTypeSelectorSharedActionViewModel by viewModels( + ownerProducer = { requireParentFragment() } + ) + + override val showExpanded = true + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetAttachmentTypeSelectorBinding { + return BottomSheetAttachmentTypeSelectorBinding.inflate(inflater, container, false) + } + + override fun invalidate() = withState(viewModel, timelineViewModel) { viewState, timelineState -> + super.invalidate() + views.location.visibility = if (viewState.isLocationVisible) View.VISIBLE else View.GONE + views.voiceBroadcast.visibility = if (viewState.isVoiceBroadcastVisible) View.VISIBLE else View.GONE + views.poll.visibility = if (!timelineState.isThreadTimeline()) View.VISIBLE else View.GONE + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + views.gallery.debouncedClicks { onAttachmentSelected(AttachmentType.GALLERY) } + views.stickers.debouncedClicks { onAttachmentSelected(AttachmentType.STICKER) } + views.file.debouncedClicks { onAttachmentSelected(AttachmentType.FILE) } + views.voiceBroadcast.debouncedClicks { onAttachmentSelected(AttachmentType.VOICE_BROADCAST) } + views.poll.debouncedClicks { onAttachmentSelected(AttachmentType.POLL) } + views.location.debouncedClicks { onAttachmentSelected(AttachmentType.LOCATION) } + views.camera.debouncedClicks { onAttachmentSelected(AttachmentType.CAMERA) } + views.contact.debouncedClicks { onAttachmentSelected(AttachmentType.CONTACT) } + } + + private fun onAttachmentSelected(attachmentType: AttachmentType) { + val action = AttachmentTypeSelectorSharedAction.SelectAttachmentTypeAction(attachmentType) + sharedActionViewModel.post(action) + dismiss() + } + + companion object { + fun show(fragmentManager: FragmentManager) { + val bottomSheet = AttachmentTypeSelectorBottomSheet() + bottomSheet.show(fragmentManager, "AttachmentTypeSelectorBottomSheet") + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorSharedActionViewModel.kt new file mode 100644 index 00000000000..2b85b6882ab --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorSharedActionViewModel.kt @@ -0,0 +1,30 @@ +/* + * 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.attachments + +import im.vector.app.core.platform.VectorSharedAction +import im.vector.app.core.platform.VectorSharedActionViewModel +import javax.inject.Inject + +class AttachmentTypeSelectorSharedActionViewModel @Inject constructor() : + VectorSharedActionViewModel() + +sealed class AttachmentTypeSelectorSharedAction : VectorSharedAction { + data class SelectAttachmentTypeAction( + val attachmentType: AttachmentType + ) : AttachmentTypeSelectorSharedAction() +} diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt index 8536b765d42..55805a07286 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt @@ -30,17 +30,11 @@ import android.view.animation.TranslateAnimation import android.widget.ImageButton import android.widget.LinearLayout import android.widget.PopupWindow -import androidx.annotation.StringRes import androidx.appcompat.widget.TooltipCompat import androidx.core.view.doOnNextLayout import androidx.core.view.isVisible import im.vector.app.R import im.vector.app.core.epoxy.onClick -import im.vector.app.core.utils.PERMISSIONS_EMPTY -import im.vector.app.core.utils.PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING -import im.vector.app.core.utils.PERMISSIONS_FOR_PICKING_CONTACT -import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO -import im.vector.app.core.utils.PERMISSIONS_FOR_VOICE_BROADCAST import im.vector.app.databinding.ViewAttachmentTypeSelectorBinding import im.vector.app.features.attachments.AttachmentTypeSelectorView.Callback import kotlin.math.max @@ -59,7 +53,7 @@ class AttachmentTypeSelectorView( ) : PopupWindow(context) { interface Callback { - fun onTypeSelected(type: Type) + fun onTypeSelected(type: AttachmentType) } private val views: ViewAttachmentTypeSelectorBinding @@ -69,14 +63,14 @@ class AttachmentTypeSelectorView( init { contentView = inflater.inflate(R.layout.view_attachment_type_selector, null, false) views = ViewAttachmentTypeSelectorBinding.bind(contentView) - views.attachmentGalleryButton.configure(Type.GALLERY) - views.attachmentCameraButton.configure(Type.CAMERA) - views.attachmentFileButton.configure(Type.FILE) - views.attachmentStickersButton.configure(Type.STICKER) - views.attachmentContactButton.configure(Type.CONTACT) - views.attachmentPollButton.configure(Type.POLL) - views.attachmentLocationButton.configure(Type.LOCATION) - views.attachmentVoiceBroadcast.configure(Type.VOICE_BROADCAST) + views.attachmentGalleryButton.configure(AttachmentType.GALLERY) + views.attachmentCameraButton.configure(AttachmentType.CAMERA) + views.attachmentFileButton.configure(AttachmentType.FILE) + views.attachmentStickersButton.configure(AttachmentType.STICKER) + views.attachmentContactButton.configure(AttachmentType.CONTACT) + views.attachmentPollButton.configure(AttachmentType.POLL) + views.attachmentLocationButton.configure(AttachmentType.LOCATION) + views.attachmentVoiceBroadcast.configure(AttachmentType.VOICE_BROADCAST) width = LinearLayout.LayoutParams.MATCH_PARENT height = LinearLayout.LayoutParams.WRAP_CONTENT animationStyle = 0 @@ -127,16 +121,16 @@ class AttachmentTypeSelectorView( } } - fun setAttachmentVisibility(type: Type, isVisible: Boolean) { + fun setAttachmentVisibility(type: AttachmentType, isVisible: Boolean) { when (type) { - Type.CAMERA -> views.attachmentCameraButton - Type.GALLERY -> views.attachmentGalleryButton - Type.FILE -> views.attachmentFileButton - Type.STICKER -> views.attachmentStickersButton - Type.CONTACT -> views.attachmentContactButton - Type.POLL -> views.attachmentPollButton - Type.LOCATION -> views.attachmentLocationButton - Type.VOICE_BROADCAST -> views.attachmentVoiceBroadcast + AttachmentType.CAMERA -> views.attachmentCameraButton + AttachmentType.GALLERY -> views.attachmentGalleryButton + AttachmentType.FILE -> views.attachmentFileButton + AttachmentType.STICKER -> views.attachmentStickersButton + AttachmentType.CONTACT -> views.attachmentContactButton + AttachmentType.POLL -> views.attachmentPollButton + AttachmentType.LOCATION -> views.attachmentLocationButton + AttachmentType.VOICE_BROADCAST -> views.attachmentVoiceBroadcast }.let { it.isVisible = isVisible } @@ -200,13 +194,13 @@ class AttachmentTypeSelectorView( return Pair(x, y) } - private fun ImageButton.configure(type: Type): ImageButton { + private fun ImageButton.configure(type: AttachmentType): ImageButton { this.setOnClickListener(TypeClickListener(type)) - TooltipCompat.setTooltipText(this, context.getString(type.tooltipRes)) + TooltipCompat.setTooltipText(this, context.getString(attachmentTooltipLabels.getValue(type))) return this } - private inner class TypeClickListener(private val type: Type) : View.OnClickListener { + private inner class TypeClickListener(private val type: AttachmentType) : View.OnClickListener { override fun onClick(v: View) { dismiss() @@ -217,14 +211,18 @@ class AttachmentTypeSelectorView( /** * The all possible types to pick with their required permissions and tooltip resource. */ - enum class Type(val permissions: List, @StringRes val tooltipRes: Int) { - CAMERA(PERMISSIONS_FOR_TAKING_PHOTO, R.string.tooltip_attachment_photo), - GALLERY(PERMISSIONS_EMPTY, R.string.tooltip_attachment_gallery), - FILE(PERMISSIONS_EMPTY, R.string.tooltip_attachment_file), - STICKER(PERMISSIONS_EMPTY, R.string.tooltip_attachment_sticker), - CONTACT(PERMISSIONS_FOR_PICKING_CONTACT, R.string.tooltip_attachment_contact), - POLL(PERMISSIONS_EMPTY, R.string.tooltip_attachment_poll), - LOCATION(PERMISSIONS_FOR_FOREGROUND_LOCATION_SHARING, R.string.tooltip_attachment_location), - VOICE_BROADCAST(PERMISSIONS_FOR_VOICE_BROADCAST, R.string.tooltip_attachment_voice_broadcast), + private companion object { + private val attachmentTooltipLabels: Map = AttachmentType.values().associateWith { + when (it) { + AttachmentType.CAMERA -> R.string.tooltip_attachment_photo + AttachmentType.GALLERY -> R.string.tooltip_attachment_gallery + AttachmentType.FILE -> R.string.tooltip_attachment_file + AttachmentType.STICKER -> R.string.tooltip_attachment_sticker + AttachmentType.CONTACT -> R.string.tooltip_attachment_contact + AttachmentType.POLL -> R.string.tooltip_attachment_poll + AttachmentType.LOCATION -> R.string.tooltip_attachment_location + AttachmentType.VOICE_BROADCAST -> R.string.tooltip_attachment_voice_broadcast + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModel.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModel.kt new file mode 100644 index 00000000000..ec45e226bec --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModel.kt @@ -0,0 +1,59 @@ +/* + * 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.attachments + +import com.airbnb.mvrx.MavericksState +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.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.VectorFeatures + +class AttachmentTypeSelectorViewModel @AssistedInject constructor( + @Assisted initialState: AttachmentTypeSelectorViewState, + private val vectorFeatures: VectorFeatures, +) : VectorViewModel(initialState) { + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: AttachmentTypeSelectorViewState): AttachmentTypeSelectorViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + override fun handle(action: EmptyAction) { + // do nothing + } + + init { + setState { + this.copy( + isLocationVisible = vectorFeatures.isLocationSharingEnabled(), + isVoiceBroadcastVisible = vectorFeatures.isVoiceBroadcastEnabled(), + ) + } + } +} + +data class AttachmentTypeSelectorViewState( + val isLocationVisible: Boolean = false, + val isVoiceBroadcastVisible: Boolean = false, +) : MavericksState 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 1a8e10d1026..9692777e157 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 @@ -54,7 +54,7 @@ class AttachmentsHelper( private var captureUri: Uri? = null // The pending type is set if we have to handle permission request. It must be restored if the activity gets killed. - var pendingType: AttachmentTypeSelectorView.Type? = null + var pendingType: AttachmentType? = null // Restorable 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 55ec922a578..afa9c843539 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 @@ -40,6 +40,7 @@ import androidx.core.text.buildSpannedString import androidx.core.view.isGone import androidx.core.view.isInvisible import androidx.core.view.isVisible +import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.parentFragmentViewModel @@ -63,6 +64,10 @@ import im.vector.app.core.utils.onPermissionDeniedDialog import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.databinding.FragmentComposerBinding import im.vector.app.features.VectorFeatures +import im.vector.app.features.attachments.AttachmentType +import im.vector.app.features.attachments.AttachmentTypeSelectorBottomSheet +import im.vector.app.features.attachments.AttachmentTypeSelectorSharedAction +import im.vector.app.features.attachments.AttachmentTypeSelectorSharedActionViewModel import im.vector.app.features.attachments.AttachmentTypeSelectorView import im.vector.app.features.attachments.AttachmentsHelper import im.vector.app.features.attachments.ContactAttachment @@ -92,6 +97,7 @@ import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.share.SharedData import im.vector.app.features.voice.VoiceFailure import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -161,6 +167,7 @@ class MessageComposerFragment : VectorBaseFragment(), A private val timelineViewModel: TimelineViewModel by parentFragmentViewModel() private val messageComposerViewModel: MessageComposerViewModel by parentFragmentViewModel() private lateinit var sharedActionViewModel: MessageSharedActionViewModel + private val attachmentViewModel: AttachmentTypeSelectorSharedActionViewModel by viewModels() private val composer: MessageComposerView get() { return if (vectorPreferences.isRichTextEditorEnabled()) { @@ -219,6 +226,11 @@ class MessageComposerFragment : VectorBaseFragment(), A } } + attachmentViewModel.stream() + .filterIsInstance() + .onEach { onTypeSelected(it.attachmentType) } + .launchIn(lifecycleScope) + if (savedInstanceState != null) { handleShareData() } @@ -299,21 +311,25 @@ class MessageComposerFragment : VectorBaseFragment(), A } composer.callback = object : PlainTextComposerLayout.Callback { override fun onAddAttachment() { - if (!::attachmentTypeSelector.isInitialized) { - attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@MessageComposerFragment) - attachmentTypeSelector.setAttachmentVisibility( - AttachmentTypeSelectorView.Type.LOCATION, - vectorFeatures.isLocationSharingEnabled(), - ) - attachmentTypeSelector.setAttachmentVisibility( - AttachmentTypeSelectorView.Type.POLL, !isThreadTimeLine() - ) - attachmentTypeSelector.setAttachmentVisibility( - AttachmentTypeSelectorView.Type.VOICE_BROADCAST, - vectorPreferences.isVoiceBroadcastEnabled(), // TODO check user permission - ) + if (vectorPreferences.isRichTextEditorEnabled()) { + AttachmentTypeSelectorBottomSheet.show(childFragmentManager) + } else { + if (!::attachmentTypeSelector.isInitialized) { + attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@MessageComposerFragment) + attachmentTypeSelector.setAttachmentVisibility( + AttachmentType.LOCATION, + vectorFeatures.isLocationSharingEnabled(), + ) + attachmentTypeSelector.setAttachmentVisibility( + AttachmentType.POLL, !isThreadTimeLine() + ) + attachmentTypeSelector.setAttachmentVisibility( + AttachmentType.VOICE_BROADCAST, + vectorPreferences.isVoiceBroadcastEnabled(), // TODO check user permission + ) + } + attachmentTypeSelector.show(composer.attachmentButton) } - attachmentTypeSelector.show(composer.attachmentButton) } override fun onExpandOrCompactChange() { @@ -662,20 +678,20 @@ class MessageComposerFragment : VectorBaseFragment(), A } } - private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) { + private fun launchAttachmentProcess(type: AttachmentType) { when (type) { - AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera( + AttachmentType.CAMERA -> attachmentsHelper.openCamera( activity = requireActivity(), vectorPreferences = vectorPreferences, cameraActivityResultLauncher = attachmentCameraActivityResultLauncher, cameraVideoActivityResultLauncher = attachmentCameraVideoActivityResultLauncher ) - AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher) - AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery(attachmentMediaActivityResultLauncher) - AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher) - AttachmentTypeSelectorView.Type.STICKER -> timelineViewModel.handle(RoomDetailAction.SelectStickerAttachment) - AttachmentTypeSelectorView.Type.POLL -> navigator.openCreatePoll(requireContext(), roomId, null, PollMode.CREATE) - AttachmentTypeSelectorView.Type.LOCATION -> { + AttachmentType.FILE -> attachmentsHelper.selectFile(attachmentFileActivityResultLauncher) + AttachmentType.GALLERY -> attachmentsHelper.selectGallery(attachmentMediaActivityResultLauncher) + AttachmentType.CONTACT -> attachmentsHelper.selectContact(attachmentContactActivityResultLauncher) + AttachmentType.STICKER -> timelineViewModel.handle(RoomDetailAction.SelectStickerAttachment) + AttachmentType.POLL -> navigator.openCreatePoll(requireContext(), roomId, null, PollMode.CREATE) + AttachmentType.LOCATION -> { navigator .openLocationSharing( context = requireContext(), @@ -685,11 +701,11 @@ class MessageComposerFragment : VectorBaseFragment(), A locationOwnerId = session.myUserId ) } - AttachmentTypeSelectorView.Type.VOICE_BROADCAST -> timelineViewModel.handle(VoiceBroadcastAction.Recording.Start) + AttachmentType.VOICE_BROADCAST -> timelineViewModel.handle(VoiceBroadcastAction.Recording.Start) } } - override fun onTypeSelected(type: AttachmentTypeSelectorView.Type) { + override fun onTypeSelected(type: AttachmentType) { if (checkPermissions(type.permissions, requireActivity(), typeSelectedActivityResultLauncher)) { launchAttachmentProcess(type) } else { diff --git a/vector/src/main/res/layout/bottom_sheet_attachment_type_selector.xml b/vector/src/main/res/layout/bottom_sheet_attachment_type_selector.xml new file mode 100644 index 00000000000..3aec5b5c203 --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_attachment_type_selector.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vector/src/test/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModelTest.kt b/vector/src/test/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModelTest.kt new file mode 100644 index 00000000000..478f631c062 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/attachments/AttachmentTypeSelectorViewModelTest.kt @@ -0,0 +1,91 @@ +/* + * 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.attachments + +import com.airbnb.mvrx.test.MavericksTestRule +import im.vector.app.test.fakes.FakeVectorFeatures +import im.vector.app.test.test +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +internal class AttachmentTypeSelectorViewModelTest { + + @get:Rule + val mavericksTestRule = MavericksTestRule() + + private val fakeVectorFeatures = FakeVectorFeatures() + private val initialState = AttachmentTypeSelectorViewState() + + @Before + fun setUp() { + // Disable all features by default + fakeVectorFeatures.givenLocationSharing(isEnabled = false) + fakeVectorFeatures.givenVoiceBroadcast(isEnabled = false) + } + + @Test + fun `given features are not enabled, then options are not visible`() { + createViewModel() + .test() + .assertStates( + listOf( + initialState, + ) + ) + .finish() + } + + @Test + fun `given location sharing is enabled, then location sharing option is visible`() { + fakeVectorFeatures.givenLocationSharing(isEnabled = true) + + createViewModel() + .test() + .assertStates( + listOf( + initialState.copy( + isLocationVisible = true + ), + ) + ) + .finish() + } + + @Test + fun `given voice broadcast is enabled, then voice broadcast option is visible`() { + fakeVectorFeatures.givenVoiceBroadcast(isEnabled = true) + + createViewModel() + .test() + .assertStates( + listOf( + initialState.copy( + isVoiceBroadcastVisible = true + ), + ) + ) + .finish() + } + + private fun createViewModel(): AttachmentTypeSelectorViewModel { + return AttachmentTypeSelectorViewModel( + initialState, + vectorFeatures = fakeVectorFeatures, + ) + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt index 4e6b4fc3df6..d989abc2146 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVectorFeatures.kt @@ -42,4 +42,12 @@ class FakeVectorFeatures : VectorFeatures by spyk() { fun givenCombinedLoginDisabled() { every { isOnboardingCombinedLoginEnabled() } returns false } + + fun givenLocationSharing(isEnabled: Boolean) { + every { isLocationSharingEnabled() } returns isEnabled + } + + fun givenVoiceBroadcast(isEnabled: Boolean) { + every { isVoiceBroadcastEnabled() } returns isEnabled + } } From 17c43c91888162d3c7675511ff910c46c3aa32fc Mon Sep 17 00:00:00 2001 From: Jonny Andrew Date: Thu, 20 Oct 2022 16:13:18 +0100 Subject: [PATCH 169/300] Add rounded corners to bottom sheet dialog. Note these are currently only visible in the collapsed state. - [Google issue](https://issuetracker.google.com/issues/144859239) - [Rejected PR](https://github.com/material-components/material-components-android/pull/437) - [Github issue](https://github.com/material-components/material-components-android/issues/1278) --- .../src/main/res/values/styles_bottom_sheet.xml | 11 +++++++++++ .../layout/bottom_sheet_attachment_type_selector.xml | 1 - .../res/layout/view_bottom_sheet_action_button.xml | 3 +-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/library/ui-styles/src/main/res/values/styles_bottom_sheet.xml b/library/ui-styles/src/main/res/values/styles_bottom_sheet.xml index f6c30040d98..7b704a6331e 100644 --- a/library/ui-styles/src/main/res/values/styles_bottom_sheet.xml +++ b/library/ui-styles/src/main/res/values/styles_bottom_sheet.xml @@ -14,6 +14,7 @@ @color/element_content_primary_light @color/element_link_light + @style/BottomSheetStyle + + + + - - - -