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 e5ac0a39b2..472121269f 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 0000000000..70524d299a --- /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 676a4f6a38..141144acd8 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 220c401137..81d1c04261 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 6979d42827..537483193a 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 a2f2251b70..2825be8291 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 471bec59af..650dd3c5cb 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 537483193a..185b7d35d6 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 185b7d35d6..c740e07257 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 ff4977491f..6058564b78 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 72b0f7a043..6e9fff78e1 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 5698a696b6..30b6600c94 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -11,7 +11,7 @@ jobs: - run: | npm install --save-dev @babel/plugin-transform-flow-strip-types - name: Danger - uses: danger/danger-js@11.1.3 + uses: danger/danger-js@11.1.4 with: args: "--dangerfile tools/danger/dangerfile.js" env: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 1692e2e281..9d9e8e76e8 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -66,7 +66,7 @@ jobs: yarn add danger-plugin-lint-report --dev - name: Danger lint if: always() - uses: danger/danger-js@11.1.3 + uses: danger/danger-js@11.1.4 with: args: "--dangerfile tools/danger/dangerfile-lint.js" env: From 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 edff769070..30bd576c21 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -12,7 +12,7 @@ def gradle = "7.2.2" def kotlin = "1.7.20" def kotlinCoroutines = "1.6.4" def dagger = "2.44" -def appDistribution = "16.0.0-beta04" +def appDistribution = "16.0.0-beta05" def retrofit = "2.9.0" def markwon = "4.6.2" def moshi = "1.14.0" From 9198cc7ac072c11fb5d038b0d232ade2102ca2ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Oct 2022 23:07:54 +0000 Subject: [PATCH 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 c59e1a3028..32579d0ccc 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -236,7 +236,7 @@ dependencies { implementation libs.sentry.sentryAndroid // UnifiedPush - implementation 'com.github.UnifiedPush:android-connector:2.1.0' + implementation 'com.github.UnifiedPush:android-connector:2.1.1' implementation "androidx.emoji2:emoji2:1.2.0" From 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 e474b68531..486da53e98 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 edff769070..3b39f72965 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 5ae70e1978..8f2a784d49 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -124,4 +124,18 @@ interface AuthenticationService { initialDeviceName: String, deviceId: String? = null ): Session + + /** + * Authenticate using m.login.token method during sign in with QR code. + * @param homeServerConnectionConfig the information about the homeserver and other configuration + * @param loginToken the m.login.token + * @param initialDeviceName the initial device name + * @param deviceId the device id, optional. If not provided or null, the server will generate one. + */ + suspend fun loginUsingQrLoginToken( + homeServerConnectionConfig: HomeServerConnectionConfig, + loginToken: String, + initialDeviceName: String, + deviceId: String? = null + ): Session } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt index 627a825679..991b7b654d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt @@ -22,7 +22,8 @@ enum class LoginType { UNSUPPORTED, CUSTOM, DIRECT, - UNKNOWN; + UNKNOWN, + QR; companion object { @@ -32,6 +33,7 @@ enum class LoginType { UNSUPPORTED.name -> UNSUPPORTED CUSTOM.name -> CUSTOM DIRECT.name -> DIRECT + QR.name -> QR else -> UNKNOWN } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 446f931847..90dc57b4f0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig import org.matrix.android.sdk.internal.auth.db.PendingSessionData import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard import org.matrix.android.sdk.internal.auth.login.DirectLoginTask +import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices @@ -62,7 +63,8 @@ internal class DefaultAuthenticationService @Inject constructor( private val sessionCreator: SessionCreator, private val pendingSessionStore: PendingSessionStore, private val getWellknownTask: GetWellknownTask, - private val directLoginTask: DirectLoginTask + private val directLoginTask: DirectLoginTask, + private val loginTokenAuthTask: QrLoginTokenTask ) : AuthenticationService { private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() @@ -404,6 +406,17 @@ internal class DefaultAuthenticationService @Inject constructor( ) } + override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session { + return loginTokenAuthTask.execute( + QrLoginTokenTask.Params( + homeServerConnectionConfig = homeServerConnectionConfig, + loginToken = loginToken, + deviceName = initialDeviceName, + deviceId = deviceId + ) + ) + } + private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString()) return retrofit.create(AuthAPI::class.java) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt index ea8578ed8c..8646752083 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt @@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data internal interface LoginParams { val type: String + val deviceDisplayName: String? + val deviceId: String? } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt index 5f0a2298cb..062b2466e5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt @@ -30,8 +30,8 @@ internal data class PasswordLoginParams( @Json(name = "identifier") val identifier: Map, @Json(name = "password") val password: String, @Json(name = "type") override val type: String, - @Json(name = "initial_device_display_name") val deviceDisplayName: String?, - @Json(name = "device_id") val deviceId: String? + @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, + @Json(name = "device_id") override val deviceId: String? ) : LoginParams { companion object { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt index 0c6fb45e78..22cc185fa7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt @@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes @JsonClass(generateAdapter = true) internal data class TokenLoginParams( @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN, - @Json(name = "token") val token: String + @Json(name = "token") val token: String, + @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, + @Json(name = "device_id") override val deviceId: String? ) : LoginParams diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt new file mode 100644 index 0000000000..477719f607 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.auth.login + +import dagger.Lazy +import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.auth.LoginType +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.auth.AuthAPI +import org.matrix.android.sdk.internal.auth.SessionCreator +import org.matrix.android.sdk.internal.auth.data.TokenLoginParams +import org.matrix.android.sdk.internal.di.Unauthenticated +import org.matrix.android.sdk.internal.network.RetrofitFactory +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory +import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface QrLoginTokenTask : Task { + 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 0000000000..3d17076a44 --- /dev/null +++ b/changelog.d/7358.sdk @@ -0,0 +1 @@ +Add support for `m.login.token` auth during QR code based sign in From a71ecee44a5b39d1ac13faa0078e0349d0bbf901 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 90dc57b4f0..7fd730bece 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -406,7 +406,12 @@ internal class DefaultAuthenticationService @Inject constructor( ) } - override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session { + override suspend fun loginUsingQrLoginToken( + homeServerConnectionConfig: HomeServerConnectionConfig, + loginToken: String, + initialDeviceName: String, + deviceId: String?, + ): Session { return loginTokenAuthTask.execute( QrLoginTokenTask.Params( homeServerConnectionConfig = homeServerConnectionConfig, From 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 f97a59b432..d0c34b83af 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor( Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode") confirmationCode ?.let { onConnectionEstablished(it) - rendezvous.completeOnNewDevice() } + rendezvous.completeOnNewDevice() } // if (isValidQrCode(action.qrCode)) { // setState { diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt deleted file mode 100644 index d7d74b08e9..0000000000 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.voicebroadcast - -/** Voice Broadcast State Event. */ -const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info" From 6e09d900070319b356a2b10a1769af8da97a2aef Mon Sep 17 00:00:00 2001 From: Onuray Sahin 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 0000000000..d7d74b08e9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.voicebroadcast + +/** Voice Broadcast State Event. */ +const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info" From 86090086b1344a8e80f8bd3accb1cbbf26a2eab2 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 d0c34b83af..f97a59b432 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -85,8 +85,8 @@ class QrCodeLoginViewModel @AssistedInject constructor( Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode") confirmationCode ?.let { onConnectionEstablished(it) + rendezvous.completeOnNewDevice() } - rendezvous.completeOnNewDevice() } // if (isValidQrCode(action.qrCode)) { // setState { From 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 8f2a784d49..c8065e4524 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -135,7 +135,7 @@ interface AuthenticationService { suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String, + initialDeviceName: String?, deviceId: String? = null ): Session } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 7fd730bece..6c3622ed5d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -409,7 +409,7 @@ internal class DefaultAuthenticationService @Inject constructor( override suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String, + initialDeviceName: String?, deviceId: String?, ): Session { return loginTokenAuthTask.execute( From 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 6c3622ed5d..5b12e3bdc3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -64,7 +64,7 @@ internal class DefaultAuthenticationService @Inject constructor( private val pendingSessionStore: PendingSessionStore, private val getWellknownTask: GetWellknownTask, private val directLoginTask: DirectLoginTask, - private val loginTokenAuthTask: QrLoginTokenTask + private val qrLoginTokenTask: QrLoginTokenTask ) : AuthenticationService { private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() @@ -412,7 +412,7 @@ internal class DefaultAuthenticationService @Inject constructor( initialDeviceName: String?, deviceId: String?, ): Session { - return loginTokenAuthTask.execute( + return qrLoginTokenTask.execute( QrLoginTokenTask.Params( homeServerConnectionConfig = homeServerConnectionConfig, loginToken = loginToken, From 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 463692e574..b1f65194f1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt @@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask +import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask import org.matrix.android.sdk.internal.auth.login.DirectLoginTask +import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.AuthDatabase import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter @@ -94,4 +96,7 @@ internal abstract class AuthModule { @Binds abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService + + @Binds + abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask } From 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 22cc185fa7..52045a1d7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt @@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes internal data class TokenLoginParams( @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN, @Json(name = "token") val token: String, - @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, - @Json(name = "device_id") override val deviceId: String? + @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null, + @Json(name = "device_id") override val deviceId: String? = null ) : LoginParams From 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 c8065e4524..e490311b91 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -135,7 +135,7 @@ interface AuthenticationService { suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String?, + initialDeviceName: String? = null, deviceId: String? = null ): Session } From 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 b1a240e942..a1ed27df1d 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt @@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor( LoginType.SSO -> buildLoginSSOForm() LoginType.DIRECT, LoginType.CUSTOM, + LoginType.QR, LoginType.UNSUPPORTED -> buildLoginUnsupportedForm() LoginType.UNKNOWN -> Unit } From 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 174e3c54c0..4031e9d571 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 e0323fdc2d..3c7b0a4729 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt @@ -26,6 +26,7 @@ import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.platform.SimpleFragmentActivity +import im.vector.app.features.home.HomeActivity import org.matrix.android.sdk.api.extensions.orFalse import timber.log.Timber @@ -75,6 +76,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { when (it) { QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen() QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen() + QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen() } } } @@ -95,6 +97,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { ) } + private fun handleNavigateToHomeScreen() { + val intent = HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true) + startActivity(intent) + } + companion object { private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG" diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt index dc258408e7..0f282fee38 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt @@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewEvents sealed class QrCodeLoginViewEvents : VectorViewEvents { object NavigateToStatusScreen : QrCodeLoginViewEvents() object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents() + object NavigateToHomeScreen : QrCodeLoginViewEvents() } From 1ed082d3cb16943a15664dbb862126942f5539d3 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Thu, 13 Oct 2022 21:15:52 +0100 Subject: [PATCH 025/300] QR login + E2EE set up --- .../sdk/internal/rendezvous/Rendezvous.kt | 95 +++++++++---------- .../features/login/qr/QrCodeLoginViewModel.kt | 73 ++++++-------- 2 files changed, 74 insertions(+), 94 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt index dd7b91582b..2f85a97c55 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt @@ -16,8 +16,11 @@ package org.matrix.android.sdk.internal.rendezvous +import android.net.Uri import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.auth.AuthenticationService +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel @@ -56,9 +59,12 @@ internal data class Payload( private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value -data class Rendezvous( +/** + * Implementation of MSC3906 to sign in + E2EE set up using a QR code. + */ +class Rendezvous( val channel: RendezvousChannel, - val theirIntent: RendezvousIntent + val theirIntent: RendezvousIntent, ) { companion object { fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous { @@ -116,7 +122,7 @@ data class Rendezvous( return checksum } - suspend fun completeOnNewDevice(): Session? { + suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? { Timber.tag(TAG).i("Waiting for login_token"); val loginToken = receive() @@ -143,59 +149,46 @@ data class Rendezvous( Timber.tag(TAG).i("Got login_token: $login_token for $homeserver"); - // TODO: set view to be state logging in? + val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver)) + return authenticationService.loginUsingQrLoginToken(hsConfig, login_token) + } - // use token to login -// const login = await sendLoginRequest(homeserver, undefined, "m.login.token", { token: login_token }); -// -// await setLoggedIn(login); -// -// const { deviceId, userId } = login; -// -// const client = MatrixClientPeg.get(); -// + suspend fun completeVerificationOnNewDevice(session: Session) { + val userId = session.myUserId + val crypto = session.cryptoService() + val deviceId = crypto.getMyDevice().deviceId + val deviceKey = crypto.getMyDevice().fingerprint() + send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey)) - val newSession: Session? = null + // await confirmation of verification - newSession ?.let { - session -> - val userId = session.myUserId - val crypto = session.cryptoService() - val deviceId = crypto.getMyDevice().deviceId - val deviceKey = crypto.getMyDevice().fingerprint() - send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey)) - - // await confirmation of verification - - val verificationResponse = receive() - val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned") - val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) - if (verifyingDeviceFromServer?.fingerprint() == verificationResponse.verifying_device_key) { - // set other device as verified - Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified"); - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) - - verificationResponse.master_key ?.let { - // set master key as trusted - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it) - - } - - // request secrets from the verifying device - Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") - - session.sharedSecretStorageService() .let { - it.requestSecret(verifyingDeviceId, MASTER_KEY_SSSS_NAME) - it.requestSecret(verifyingDeviceId, SELF_SIGNING_KEY_SSSS_NAME) - it.requestSecret(verifyingDeviceId, USER_SIGNING_KEY_SSSS_NAME) - it.requestSecret(verifyingDeviceId, KEYBACKUP_SECRET_SSSS_NAME) - } - } else { - Timber.tag(TAG).i("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer") - } + val verificationResponse = receive() + val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned") + val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) + if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) { + Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer") + return; } - return newSession + // set other device as verified + Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified"); + crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) + + // TODO: what do we do with the master key? +// verificationResponse.master_key ?.let { +// // set master key as trusted +// crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it) +// } + + // request secrets from the verifying device + Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") + + session.sharedSecretStorageService() .let { + it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId) + } } private suspend fun receive(): Payload? { diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index f97a59b432..c729152e44 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -16,24 +16,31 @@ package im.vector.app.features.login.qr +import android.content.Context import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.home.HomeActivity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.rendezvous.Rendezvous import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason import timber.log.Timber class QrCodeLoginViewModel @AssistedInject constructor( - @Assisted private val initialState: QrCodeLoginViewState + @Assisted private val initialState: QrCodeLoginViewState, + private val applicationContext: Context, + private val authenticationService: AuthenticationService, + private val activeSessionHolder: ActiveSessionHolder, ) : VectorViewModel(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 33837bc425..cced29aab4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt @@ -94,7 +94,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu val isInitiator = theirPublicKey == null if (isInitiator) { - Timber.tag(TAG).i("Waiting for other device to send their public key") +// Timber.tag(TAG).i("Waiting for other device to send their public key") val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device") if (res.key == null) { @@ -106,7 +106,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) } else { // send our public key unencrypted - Timber.tag(TAG).i("Sending public key") +// Timber.tag(TAG).i("Sending public key") send(ECDHPayload( algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) @@ -121,10 +121,10 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu aesKey = olmSAS!!.generateShortCode(aesInfo, 32) - Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}") - Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}") - Timber.tag(TAG).i("AES info: $aesInfo") - Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}") +// Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}") +// Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}") +// Timber.tag(TAG).i("AES info: $aesInfo") +// Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}") val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5) return getDecimalCodeRepresentation(rawChecksum) @@ -180,7 +180,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu } private fun encrypt(plainText: ByteArray): ECDHPayload { - Timber.tag(TAG).i("Encrypting: ${plainText.toString(Charsets.UTF_8)}") +// Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}") val iv = ByteArray(16) SecureRandom().nextBytes(iv) @@ -212,7 +212,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu val plainTextBytes = plainText.toByteArray() - Timber.tag(TAG).i("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}") +// Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}") return plainTextBytes } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt index 3e5e1121ea..cc4346d55e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -67,7 +67,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo // TODO: properly determine endpoint val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev" - Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri") +// Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri") val httpClient = okhttp3.OkHttpClient.Builder().build() @@ -123,8 +123,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo val response = httpClient.newCall(request.build()).execute() try { - - Timber.tag(TAG).i("Received polling response: ${response.code} from $uri") +// Timber.tag(TAG).d("Received polling response: ${response.code} from $uri") if (response.code == 404) { cancel(RendezvousFailureReason.Unknown) @@ -142,7 +141,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo etag = it } val data = response.body?.bytes() - Timber.tag(TAG).i("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag") +// Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag") return data } @@ -158,6 +157,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo override suspend fun cancel(reason: RendezvousFailureReason) { var mappedReason = reason + Timber.tag(TAG).i("$expiresAt") if (mappedReason == RendezvousFailureReason.Unknown && expiresAt != null && Date() > expiresAt) { mappedReason = RendezvousFailureReason.Expired From 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 d040f9c67b..6bb47dda5b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -130,4 +130,17 @@ interface AuthenticationService { * Return true if qr code login is supported by the server, false otherwise. */ suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean + * Authenticate using m.login.token method during sign in with QR code. + * @param homeServerConnectionConfig the information about the homeserver and other configuration + * @param loginToken the m.login.token + * @param initialDeviceName the initial device name + * @param deviceId the device id, optional. If not provided or null, the server will generate one. + */ + + suspend fun loginUsingQrLoginToken( + homeServerConnectionConfig: HomeServerConnectionConfig, + loginToken: String, + initialDeviceName: String, + deviceId: String? = null + ): Session } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt index 627a825679..991b7b654d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt @@ -22,7 +22,8 @@ enum class LoginType { UNSUPPORTED, CUSTOM, DIRECT, - UNKNOWN; + UNKNOWN, + QR; companion object { @@ -32,6 +33,7 @@ enum class LoginType { UNSUPPORTED.name -> UNSUPPORTED CUSTOM.name -> CUSTOM DIRECT.name -> DIRECT + QR.name -> QR else -> UNKNOWN } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 4f45f807df..5b6f0686c4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -40,6 +40,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig import org.matrix.android.sdk.internal.auth.db.PendingSessionData import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard import org.matrix.android.sdk.internal.auth.login.DirectLoginTask +import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices @@ -64,7 +65,8 @@ internal class DefaultAuthenticationService @Inject constructor( private val sessionCreator: SessionCreator, private val pendingSessionStore: PendingSessionStore, private val getWellknownTask: GetWellknownTask, - private val directLoginTask: DirectLoginTask + private val directLoginTask: DirectLoginTask, + private val loginTokenAuthTask: QrLoginTokenTask ) : AuthenticationService { private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() @@ -420,6 +422,17 @@ internal class DefaultAuthenticationService @Inject constructor( } } + override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session { + return loginTokenAuthTask.execute( + QrLoginTokenTask.Params( + homeServerConnectionConfig = homeServerConnectionConfig, + loginToken = loginToken, + deviceName = initialDeviceName, + deviceId = deviceId + ) + ) + } + private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString()) return retrofit.create(AuthAPI::class.java) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt index ea8578ed8c..8646752083 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/LoginParams.kt @@ -18,4 +18,6 @@ package org.matrix.android.sdk.internal.auth.data internal interface LoginParams { val type: String + val deviceDisplayName: String? + val deviceId: String? } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt index 5f0a2298cb..062b2466e5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/PasswordLoginParams.kt @@ -30,8 +30,8 @@ internal data class PasswordLoginParams( @Json(name = "identifier") val identifier: Map, @Json(name = "password") val password: String, @Json(name = "type") override val type: String, - @Json(name = "initial_device_display_name") val deviceDisplayName: String?, - @Json(name = "device_id") val deviceId: String? + @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, + @Json(name = "device_id") override val deviceId: String? ) : LoginParams { companion object { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt index 0c6fb45e78..22cc185fa7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt @@ -23,5 +23,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes @JsonClass(generateAdapter = true) internal data class TokenLoginParams( @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN, - @Json(name = "token") val token: String + @Json(name = "token") val token: String, + @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, + @Json(name = "device_id") override val deviceId: String? ) : LoginParams diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt new file mode 100644 index 0000000000..477719f607 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/QrLoginTokenTask.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.auth.login + +import dagger.Lazy +import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.auth.LoginType +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.auth.AuthAPI +import org.matrix.android.sdk.internal.auth.SessionCreator +import org.matrix.android.sdk.internal.auth.data.TokenLoginParams +import org.matrix.android.sdk.internal.di.Unauthenticated +import org.matrix.android.sdk.internal.network.RetrofitFactory +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory +import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface QrLoginTokenTask : Task { + 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 0000000000..3d17076a44 --- /dev/null +++ b/changelog.d/7358.sdk @@ -0,0 +1 @@ +Add support for `m.login.token` auth during QR code based sign in From d0898a2b89e7ff0c2858fece93656a2b0427a19e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 5b6f0686c4..773f2118b1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -422,7 +422,12 @@ internal class DefaultAuthenticationService @Inject constructor( } } - override suspend fun loginUsingQrLoginToken(homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, initialDeviceName: String, deviceId: String?): Session { + override suspend fun loginUsingQrLoginToken( + homeServerConnectionConfig: HomeServerConnectionConfig, + loginToken: String, + initialDeviceName: String, + deviceId: String?, + ): Session { return loginTokenAuthTask.execute( QrLoginTokenTask.Params( homeServerConnectionConfig = homeServerConnectionConfig, From 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 da3348653c..05344f0a67 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -59,13 +59,28 @@ class QrCodeLoginViewModel @AssistedInject constructor( } private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) { - if (isValidQrCode(action.qrCode)) { - setState { - copy( - connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice - ) + Timber.tag(TAG).d("Scanned code: ${action.qrCode}") + + val rendezvous = Rendezvous.buildChannelFromCode(action.qrCode) { reason -> + Timber.tag(TAG).d("Rendezvous cancelled: $reason") + onFailed(reason) + } + + setState { + copy( + connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice + ) + } + + _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen) + + viewModelScope.launch(Dispatchers.IO) { + val confirmationCode = rendezvous.startAfterScanningCode() + Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode") + confirmationCode ?.let { + onConnectionEstablished(it) } - _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen) + rendezvous.completeOnNewDevice() } // TODO. UI test purpose. Fixme remove! diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt deleted file mode 100644 index d7d74b08e9..0000000000 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.voicebroadcast - -/** Voice Broadcast State Event. */ -const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info" From ef574bd82fbc5e33f0d4da51958158de9773a45c Mon Sep 17 00:00:00 2001 From: Onuray Sahin 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 0000000000..d7d74b08e9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastConstants.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.voicebroadcast + +/** Voice Broadcast State Event. */ +const val STATE_ROOM_VOICE_BROADCAST_INFO = "io.element.voice_broadcast_info" From b03240330d9905fc422a2fd3d166376c5d80f3a7 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 05344f0a67..877d32a11d 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -79,8 +79,8 @@ class QrCodeLoginViewModel @AssistedInject constructor( Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode") confirmationCode ?.let { onConnectionEstablished(it) + rendezvous.completeOnNewDevice() } - rendezvous.completeOnNewDevice() } // TODO. UI test purpose. Fixme remove! From 1e60f3c25b457e2cb40e5c8770e673c25f2fa941 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 6bb47dda5b..9fde6d9326 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -140,7 +140,7 @@ interface AuthenticationService { suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String, + initialDeviceName: String?, deviceId: String? = null ): Session } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 773f2118b1..7aeeacd10b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -425,7 +425,7 @@ internal class DefaultAuthenticationService @Inject constructor( override suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String, + initialDeviceName: String?, deviceId: String?, ): Session { return loginTokenAuthTask.execute( From e2f3dde5c1579c88521ae7b2e16b725f7e7c48e5 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 7aeeacd10b..5449c0a735 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -66,7 +66,7 @@ internal class DefaultAuthenticationService @Inject constructor( private val pendingSessionStore: PendingSessionStore, private val getWellknownTask: GetWellknownTask, private val directLoginTask: DirectLoginTask, - private val loginTokenAuthTask: QrLoginTokenTask + private val qrLoginTokenTask: QrLoginTokenTask ) : AuthenticationService { private var pendingSessionData: PendingSessionData? = pendingSessionStore.getPendingSessionData() @@ -428,7 +428,7 @@ internal class DefaultAuthenticationService @Inject constructor( initialDeviceName: String?, deviceId: String?, ): Session { - return loginTokenAuthTask.execute( + return qrLoginTokenTask.execute( QrLoginTokenTask.Params( homeServerConnectionConfig = homeServerConnectionConfig, loginToken = loginToken, From ca7a6efadee7430c4ac431a7cb89733db7486cc6 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 463692e574..b1f65194f1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt @@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.auth.db.AuthRealmModule import org.matrix.android.sdk.internal.auth.db.RealmPendingSessionStore import org.matrix.android.sdk.internal.auth.db.RealmSessionParamsStore import org.matrix.android.sdk.internal.auth.login.DefaultDirectLoginTask +import org.matrix.android.sdk.internal.auth.login.DefaultQrLoginTokenTask import org.matrix.android.sdk.internal.auth.login.DirectLoginTask +import org.matrix.android.sdk.internal.auth.login.QrLoginTokenTask import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.AuthDatabase import org.matrix.android.sdk.internal.legacy.DefaultLegacySessionImporter @@ -94,4 +96,7 @@ internal abstract class AuthModule { @Binds abstract fun bindHomeServerHistoryService(service: DefaultHomeServerHistoryService): HomeServerHistoryService + + @Binds + abstract fun bindQrLoginTokenTask(task: DefaultQrLoginTokenTask): QrLoginTokenTask } From ac80ae5632c4543b199189b90662ed4c971373eb Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 22cc185fa7..52045a1d7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/data/TokenLoginParams.kt @@ -24,6 +24,6 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes internal data class TokenLoginParams( @Json(name = "type") override val type: String = LoginFlowTypes.TOKEN, @Json(name = "token") val token: String, - @Json(name = "initial_device_display_name") override val deviceDisplayName: String?, - @Json(name = "device_id") override val deviceId: String? + @Json(name = "initial_device_display_name") override val deviceDisplayName: String? = null, + @Json(name = "device_id") override val deviceId: String? = null ) : LoginParams From bc0843eddf1712983dd4990ac6835dd68fc0bbb5 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 9fde6d9326..1cc53b311c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -140,7 +140,7 @@ interface AuthenticationService { suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, - initialDeviceName: String?, + initialDeviceName: String? = null, deviceId: String? = null ): Session } From 991eeb1de6c29515aa43d4c551b8cf12580f5f53 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 b1a240e942..a1ed27df1d 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt @@ -152,6 +152,7 @@ class SoftLogoutController @Inject constructor( LoginType.SSO -> buildLoginSSOForm() LoginType.DIRECT, LoginType.CUSTOM, + LoginType.QR, LoginType.UNSUPPORTED -> buildLoginUnsupportedForm() LoginType.UNKNOWN -> Unit } From 9a72d6529b0a76bc83c28ed217f79c8a69c6acc4 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 042f885231..fac31ce4e3 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt @@ -25,6 +25,7 @@ import com.airbnb.mvrx.viewModel import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.SimpleFragmentActivity +import im.vector.app.features.home.HomeActivity import org.matrix.android.sdk.api.extensions.orFalse import timber.log.Timber @@ -74,6 +75,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { when (it) { QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen() QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen() + QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen() } } } @@ -94,6 +96,11 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { ) } + private fun handleNavigateToHomeScreen() { + val intent = HomeActivity.newIntent(this, firstStartMainActivity = false, existingSession = true) + startActivity(intent) + } + companion object { private const val FRAGMENT_QR_CODE_INSTRUCTIONS_TAG = "FRAGMENT_QR_CODE_INSTRUCTIONS_TAG" diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt index dc258408e7..0f282fee38 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt @@ -21,4 +21,5 @@ import im.vector.app.core.platform.VectorViewEvents sealed class QrCodeLoginViewEvents : VectorViewEvents { object NavigateToStatusScreen : QrCodeLoginViewEvents() object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents() + object NavigateToHomeScreen : QrCodeLoginViewEvents() } From dd47297dfd4bc774c509d06ccc1316fbb70e6514 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 0000000000..2f85a97c55 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous + +import android.net.Uri +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.auth.AuthenticationService +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.logger.LoggerTag +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel +import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME +import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME +import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME +import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME +import org.matrix.android.sdk.api.util.MatrixJsonParser +import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel +import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent +import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport +import timber.log.Timber + +internal enum class PayloadType(val value: String) { + @Json(name = "m.login.start") Start("m.login.start"), + @Json(name = "m.login.finish") Finish("m.login.finish"), + @Json(name = "m.login.progress") Progress("m.login.progress") +} + +@JsonClass(generateAdapter = true) +internal data class Payload( + @Json val type: PayloadType, + @Json val intent: RendezvousIntent? = null, + @Json val outcome: String? = null, + @Json val protocols: List? = null, + @Json val protocol: String? = null, + @Json val homeserver: String? = null, + @Json val login_token: String? = null, + @Json val device_id: String? = null, + @Json val device_key: String? = null, + @Json val verifying_device_id: String? = null, + @Json val verifying_device_key: String? = null, + @Json val master_key: String? = null +) + +private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value + +/** + * Implementation of MSC3906 to sign in + E2EE set up using a QR code. + */ +class Rendezvous( + val channel: RendezvousChannel, + val theirIntent: RendezvousIntent, +) { + companion object { + fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous { + val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code") + + val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri) + + return Rendezvous( + ECDHRendezvousChannel(transport, parsed.rendezvous.key), + parsed.intent + ) + } + } + + private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java) + // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE + val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE + + private suspend fun areIntentsIncompatible(): Boolean { + val incompatible = theirIntent == ourIntent + + Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible") + + if (incompatible) { + send(Payload(PayloadType.Finish, intent = ourIntent)) + val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn + channel.cancel(reason) + } + + return incompatible + } + + suspend fun startAfterScanningCode(): String? { + val checksum = channel.connect(); + + Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum") + + if (areIntentsIncompatible()) { + return null + } + + // get protocols + Timber.tag(TAG).i("Waiting for protocols"); + val protocolsResponse = receive() + + if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) { + send(Payload(PayloadType.Finish, outcome = "unsupported")) + Timber.tag(TAG).i("No supported protocol") + cancel(RendezvousFailureReason.Unknown) + return null + } + + send(Payload(PayloadType.Progress, protocol = "login_token")) + + return checksum + } + + suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? { + Timber.tag(TAG).i("Waiting for login_token"); + + val loginToken = receive() + + if (loginToken?.type == PayloadType.Finish) { + when (loginToken.outcome) { + "declined" -> { + Timber.tag(TAG).i("Login declined by other device") + channel.cancel(RendezvousFailureReason.UserDeclined) + return null + } + "unsupported" -> { + Timber.tag(TAG).i("Not supported") + channel.cancel(RendezvousFailureReason.HomeserverLacksSupport) + return null + } + } + channel.cancel(RendezvousFailureReason.Unknown) + return null + } + + val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned") + val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned") + + Timber.tag(TAG).i("Got login_token: $login_token for $homeserver"); + + val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver)) + return authenticationService.loginUsingQrLoginToken(hsConfig, login_token) + } + + suspend fun completeVerificationOnNewDevice(session: Session) { + val userId = session.myUserId + val crypto = session.cryptoService() + val deviceId = crypto.getMyDevice().deviceId + val deviceKey = crypto.getMyDevice().fingerprint() + send(Payload(PayloadType.Progress, outcome = "success", device_id = deviceId, device_key = deviceKey)) + + // await confirmation of verification + + val verificationResponse = receive() + val verifyingDeviceId = verificationResponse?.verifying_device_id ?: throw RuntimeException("No verifying device id returned") + val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) + if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) { + Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer") + return; + } + + // set other device as verified + Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified"); + crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) + + // TODO: what do we do with the master key? +// verificationResponse.master_key ?.let { +// // set master key as trusted +// crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it) +// } + + // request secrets from the verifying device + Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") + + session.sharedSecretStorageService() .let { + it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId) + } + } + + private suspend fun receive(): Payload? { + val data = channel.receive()?: return null + return adapter.fromJson(data.toString(Charsets.UTF_8)) + } + + private suspend fun send(payload: Payload) { + channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8)); + } + + suspend fun cancel(reason: RendezvousFailureReason) { + channel.cancel(reason) + } + + suspend fun close() { + channel.close() + } +} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index 877d32a11d..d9c30690a7 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -16,19 +16,32 @@ package im.vector.app.features.login.qr +import android.content.Context import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.platform.VectorViewModel -import kotlinx.coroutines.delay +import im.vector.app.features.home.HomeActivity +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.auth.AuthenticationService +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.rendezvous.Rendezvous +import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason +import timber.log.Timber class QrCodeLoginViewModel @AssistedInject constructor( @Assisted private val initialState: QrCodeLoginViewState, + private val applicationContext: Context, + private val authenticationService: AuthenticationService, + private val activeSessionHolder: ActiveSessionHolder, ) : VectorViewModel(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() - // TODO. UI test purpose. Fixme remove! - viewModelScope.launch { - delay(3000) - onFailed(QrCodeLoginErrorType.TIMEOUT, true) - delay(3000) - onConnectionEstablished("1234-ABCD-5678-EFGH") - delay(3000) - onSigningIn() - delay(3000) - onFailed(QrCodeLoginErrorType.DEVICE_IS_NOT_SUPPORTED, false) + session.configureAndStart(applicationContext) + + rendezvous.completeVerificationOnNewDevice(session) + + _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen) + } + } + } catch (failure: Throwable) { + Timber.tag(TAG).e(failure, "Error occurred during sign in") + onFailed(RendezvousFailureReason.Unknown) + } } } - private fun onFailed(errorType: QrCodeLoginErrorType, canTryAgain: Boolean) { + private fun onFailed(reason: RendezvousFailureReason) { setState { copy( connectionStatus = QrCodeLoginConnectionStatus.Failed(errorType, canTryAgain) @@ -121,13 +138,6 @@ class QrCodeLoginViewModel @AssistedInject constructor( } } - /** - * TODO. UI test purpose. Fixme accordingly. - */ - private fun isValidQrCode(qrCode: String): Boolean { - return qrCode.startsWith("http") - } - /** * TODO. UI test purpose. Fixme accordingly. */ From 7bc0bd3b57d1db71742051fe65d6b1003ad2037e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 0000000000..cced29aab4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.channels + +import android.util.Base64 +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import okhttp3.MediaType.Companion.toMediaType +import org.matrix.android.sdk.api.logger.LoggerTag +import org.matrix.android.sdk.api.util.MatrixJsonParser +import org.matrix.android.sdk.internal.extensions.toUnsignedInt +import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel +import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport +import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous +import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError +import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent +import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm +import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails +import org.matrix.olm.OlmSAS +import timber.log.Timber +import java.security.SecureRandom +import java.util.LinkedList +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +@JsonClass(generateAdapter = true) +data class ECDHPayload( + @Json val algorithm: SecureRendezvousChannelAlgorithm? = null, + @Json val key: String? = null, + @Json val ciphertext: String? = null, + @Json val iv: String? = null +) + +private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value + +fun getDecimalCodeRepresentation(byteArray: ByteArray): String { + val b0 = byteArray[0].toUnsignedInt() // need unsigned byte + val b1 = byteArray[1].toUnsignedInt() // need unsigned byte + val b2 = byteArray[2].toUnsignedInt() // need unsigned byte + val b3 = byteArray[3].toUnsignedInt() // need unsigned byte + val b4 = byteArray[4].toUnsignedInt() // need unsigned byte + // (B0 << 5 | B1 >> 3) + 1000 + val first = (b0.shl(5) or b1.shr(3)) + 1000 + // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 + val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 + // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 + val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 + return "$first-$second-$third" +} + +const val ALGORITHM_SPEC = "AES/GCM/NoPadding" +const val KEY_SPEC = "AES" + +/** + * Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903: + * https://github.com/matrix-org/matrix-spec-proposals/pull/3903 + */ +class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel { + private var olmSAS: OlmSAS? + private val ourPublicKey: ByteArray + private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java) + private var theirPublicKey: ByteArray? = null + private var aesKey: ByteArray? = null + + init { + theirPublicKeyBase64 ?.let { + theirPublicKey = Base64.decode(it, Base64.NO_WRAP) + } + olmSAS = OlmSAS() + ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP) + } + + override suspend fun connect(): String { + if (olmSAS == null) { + throw RuntimeException("Channel closed") + } + val isInitiator = theirPublicKey == null + + if (isInitiator) { +// Timber.tag(TAG).i("Waiting for other device to send their public key") + val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device") + + if (res.key == null) { + throw RendezvousError( + "Unsupported algorithm: ${res.algorithm}", + RendezvousFailureReason.UnsupportedAlgorithm, + ) + } + theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) + } else { + // send our public key unencrypted +// Timber.tag(TAG).i("Sending public key") + send(ECDHPayload( + algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, + key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) + )) + } + + olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) + + val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP) + val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP) + val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey" + + aesKey = olmSAS!!.generateShortCode(aesInfo, 32) + +// Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}") +// Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}") +// Timber.tag(TAG).i("AES info: $aesInfo") +// Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}") + + val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5) + return getDecimalCodeRepresentation(rawChecksum) + } + + private suspend fun send(payload: ECDHPayload) { + transport.send("application/json".toMediaType(), ecdhAdapter.toJson(payload).toByteArray(Charsets.UTF_8)) + } + + override suspend fun send(data: ByteArray) { + if (aesKey == null) { + throw RuntimeException("Shared secret not established") + } + send(encrypt(data)) + } + + private suspend fun receiveAsPayload(): ECDHPayload? { + transport.receive()?.toString(Charsets.UTF_8) ?.let { + return ecdhAdapter.fromJson(it) + } ?: return null + } + + override suspend fun receive(): ByteArray? { + if (aesKey == null) { + throw RuntimeException("Shared secret not established") + } + val payload = receiveAsPayload() ?: return null + return decrypt(payload) + } + + override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode { + return ECDHRendezvousCode( + intent, + rendezvous = ECDHRendezvous( + transport.details() as SimpleHttpRendezvousTransportDetails, + SecureRendezvousChannelAlgorithm.ECDH_V1, + key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) + ) + ) + } + + override suspend fun cancel(reason: RendezvousFailureReason) { + try { + transport.cancel(reason) + } finally { + close() + } + } + + override suspend fun close() { + olmSAS?.releaseSas() + olmSAS = null + } + + private fun encrypt(plainText: ByteArray): ECDHPayload { +// Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}") + val iv = ByteArray(16) + SecureRandom().nextBytes(iv) + + val cipherText = LinkedList() + + 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 0000000000..cc4346d55e --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.transports + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import kotlinx.coroutines.delay +import okhttp3.MediaType +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import org.matrix.android.sdk.api.logger.LoggerTag +import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport +import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails +import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType +import timber.log.Timber +import java.text.SimpleDateFormat +import java.util.Date + +private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value + +@JsonClass(generateAdapter = true) +data class SimpleHttpRendezvousTransportDetails( + @Json val uri: String +): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1) + +/** + * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886 + */ +class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport { + override var ready = false + private var cancelled = false + private var uri: String? + private var etag: String? = null + private var expiresAt: Date? = null + + init { + uri = rendezvousUri + } + + override suspend fun details(): RendezvousTransportDetails { + val uri = uri ?: throw IllegalStateException("Rendezvous not set up") + + return SimpleHttpRendezvousTransportDetails(uri) + } + + override suspend fun send(contentType: MediaType, data: ByteArray) { + if (cancelled) { + return + } + + val method = if (uri != null) "PUT" else "POST" + // TODO: properly determine endpoint + val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev" + +// Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri") + + val httpClient = okhttp3.OkHttpClient.Builder().build() + + val request = Request.Builder() + .url(uri) + .method(method, data.toRequestBody()) + .header("content-type", contentType.toString()) + + etag ?.let { + request.header("if-match", it) + } + + val response = httpClient.newCall(request.build()).execute() + + if (response.code == 404) { + cancel(RendezvousFailureReason.Unknown) + } + etag = response.header("etag") + + Timber.tag(TAG).i("Sent data to $uri new etag $etag") + + if (method == "POST") { + val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response") + + response.header("expires") ?.let { + val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz") + expiresAt = format.parse(it) + } + + // resolve location header which could be relative or absolute + this.uri = response.request.url.toUri().resolve(location).toString() + ready = true + } + } + + override suspend fun receive(): ByteArray? { + val uri = uri ?: throw IllegalStateException("Rendezvous not set up") + var done = false + val httpClient = okhttp3.OkHttpClient.Builder().build() + while (!done) { + if (cancelled) { + return null + } + Timber.tag(TAG).i("Polling: $uri after etag $etag") + val request = Request.Builder() + .url(uri) + .get() + + etag ?.let { + request.header("if-none-match", it) + } + + val response = httpClient.newCall(request.build()).execute() + + try { +// Timber.tag(TAG).d("Received polling response: ${response.code} from $uri") + + if (response.code == 404) { + cancel(RendezvousFailureReason.Unknown) + return null + } + + // rely on server expiring the channel rather than checking ourselves + + if (response.header("content-type") != "application/json") { + response.header("etag")?.let { + etag = it + } + } else if (response.code == 200) { + response.header("etag")?.let { + etag = it + } + val data = response.body?.bytes() +// Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag") + return data + } + + done = false + delay(1000) + } finally { + response.close() + } + } + + return null + } + + override suspend fun cancel(reason: RendezvousFailureReason) { + var mappedReason = reason + Timber.tag(TAG).i("$expiresAt") + if (mappedReason == RendezvousFailureReason.Unknown && + expiresAt != null && Date() > expiresAt) { + mappedReason = RendezvousFailureReason.Expired + } + + cancelled = true + ready = false + onCancelled ?.let { it(mappedReason) } + + if (mappedReason == RendezvousFailureReason.UserDeclined) { + uri ?.let { + try { + val httpClient = okhttp3.OkHttpClient.Builder().build() + val request = Request.Builder() + .url(it) + .delete() + .build() + httpClient.newCall(request).execute() + } catch (e: Exception) { + Timber.tag(TAG).w(e, "Failed to delete channel") + } + } + } + } +} From 6399032312c743793fc79ec1f7863c95aec0db1a Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 1cc53b311c..252c33a8c4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -130,13 +130,14 @@ interface AuthenticationService { * Return true if qr code login is supported by the server, false otherwise. */ suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean + + /** * Authenticate using m.login.token method during sign in with QR code. * @param homeServerConnectionConfig the information about the homeserver and other configuration * @param loginToken the m.login.token * @param initialDeviceName the initial device name * @param deviceId the device id, optional. If not provided or null, the server will generate one. */ - suspend fun loginUsingQrLoginToken( homeServerConnectionConfig: HomeServerConnectionConfig, loginToken: String, From 958ee2d3561fb4b871ba83c6cb3be2ce4fbeb943 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 ae3ba9574b..17b4ff2409 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt @@ -83,6 +83,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment 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 ae65963f37..22af8cebbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt @@ -27,6 +27,7 @@ open class LoggerTag(name: String, parentTag: LoggerTag? = null) { object SYNC : LoggerTag("SYNC") object VOIP : LoggerTag("VOIP") object CRYPTO : LoggerTag("CRYPTO") + object RENDEZVOUS : LoggerTag("RZ") val value: String = if (parentTag == null) { name diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt new file mode 100644 index 0000000000..43552f46be --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous + +import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent + +interface RendezvousChannel { + var transport: RendezvousTransport; + /** + * @returns the checksum/confirmation digits to be shown to the user + */ + suspend fun connect(): String + /** + * Send a payload via the channel. + * @param data payload to send + */ + suspend fun send(data: ByteArray) + /** + * Receive a payload from the channel. + * @returns the received payload + */ + suspend fun receive(): ByteArray? + /** + * @returns a representation of the channel that can be encoded in a QR or similar + */ + suspend fun close() + // TODO: this should be transport independent in the future + suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode + suspend fun cancel(reason: RendezvousFailureReason) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt new file mode 100644 index 0000000000..0e2ea8c758 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous + +enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) { + UserDeclined("user_declined"), + OtherDeviceNotSignedIn("other_device_not_signed_in"), + OtherDeviceAlreadySignedIn("other_device_already_signed_in"), + Unknown("unknown"), + Expired("expired"), + UserCancelled("user_cancelled"), + InvalidCode("invalid_code"), + UnsupportedAlgorithm("unsupported_algorithm", false), + DataMismatch("data_mismatch"), + UnsupportedTransport("unsupported_transport", false), + HomeserverLacksSupport("homeserver_lacks_support", false) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt new file mode 100644 index 0000000000..753b0bc6fa --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous + +import okhttp3.MediaType +import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails + +interface RendezvousTransport { + var ready: Boolean; + var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?; + suspend fun details(): RendezvousTransportDetails; + suspend fun send(contentType: MediaType, data: ByteArray); + suspend fun receive(): ByteArray?; + suspend fun cancel(reason: RendezvousFailureReason); +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt new file mode 100644 index 0000000000..e296dce09d --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails + +@JsonClass(generateAdapter = true) +data class ECDHRendezvous( + @Json val transport: SimpleHttpRendezvousTransportDetails, + @Json val algorithm: SecureRendezvousChannelAlgorithm, + @Json val key: String +) + +@JsonClass(generateAdapter = true) +data class ECDHRendezvousCode( + @Json val intent: RendezvousIntent, + @Json val rendezvous: ECDHRendezvous +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt new file mode 100644 index 0000000000..d490de0133 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +open class EmbeddedRendezvous( + @Json(name = "transport") val transport: RendezvousTransportDetails, + @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt new file mode 100644 index 0000000000..ead273e8ce --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.model + +import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason + +class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) { +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt new file mode 100644 index 0000000000..6285c1e57a --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.model + +import com.squareup.moshi.Json + +enum class RendezvousIntent { + @Json(name = "login.start") LOGIN_ON_NEW_DEVICE, + @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt new file mode 100644 index 0000000000..1b1826194f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +open class RendezvousTransportDetails( + @Json val type: RendezvousTransportType +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt new file mode 100644 index 0000000000..c3b6ba7ac8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.model + +import com.squareup.moshi.Json + +enum class RendezvousTransportType(val value: String) { + @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1") +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt new file mode 100644 index 0000000000..ddc0ae20e7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.rendezvous.model + +import com.squareup.moshi.Json + +enum class SecureRendezvousChannelAlgorithm(val value: String) { + @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256") +} From f04f0e6fac25345b7cdc39181e0ac4743fdb37c5 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 822e9d3865..108fe7db7c 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3368,6 +3368,7 @@ 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 330562b874..4de191f863 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt @@ -16,9 +16,11 @@ package im.vector.app.features.login.qr +import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason + sealed class QrCodeLoginConnectionStatus { object ConnectingToDevice : QrCodeLoginConnectionStatus() data class Connected(val securityCode: String, val canConfirmSecurityCode: Boolean) : QrCodeLoginConnectionStatus() object SigningIn : QrCodeLoginConnectionStatus() - data class Failed(val errorType: QrCodeLoginErrorType, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus() + data class Failed(val errorType: RendezvousFailureReason, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus() } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt deleted file mode 100644 index 9a6cc13de0..0000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginErrorType.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.login.qr - -enum class QrCodeLoginErrorType { - DEVICE_IS_NOT_SUPPORTED, - TIMEOUT, - REQUEST_WAS_DENIED, -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt index 1c0841aa11..fb372cbb2f 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -27,6 +27,7 @@ import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding import im.vector.app.features.themes.ThemeUtils +import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason @AndroidEntryPoint class QrCodeLoginStatusFragment : VectorBaseFragment() { @@ -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 d9c30690a7..8461d8d88f 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -39,7 +39,7 @@ class QrCodeLoginViewModel @AssistedInject constructor( @Assisted private val initialState: QrCodeLoginViewState, private val applicationContext: Context, private val authenticationService: AuthenticationService, - private val activeSessionHolder: ActiveSessionHolder, + private val activeSessionHolder: ActiveSessionHolder ) : VectorViewModel(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 e474b68531..32a7c2c26f 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ plugins { // Detekt id "io.gitlab.arturbosch.detekt" version "1.21.0" // Ksp - id "com.google.devtools.ksp" version "1.7.20-1.0.6" + id "com.google.devtools.ksp" version "1.7.20-1.0.7" // Dependency Analysis id 'com.autonomousapps.dependency-analysis' version "1.13.1" From 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 2f85a97c55..d8fd323056 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt index 43552f46be..6655468808 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt index 0e2ea8c758..0920bf224f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt index 753b0bc6fa..f054b7cb73 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt index cced29aab4..9f347b8536 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt index e296dce09d..14976ec836 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt index d490de0133..5a609d497e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt index ead273e8ce..feb7d0cf35 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt index 6285c1e57a..d317531835 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt index 1b1826194f..ee9058daa3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt index c3b6ba7ac8..51371ddb2f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt index ddc0ae20e7..122b6ede82 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt index cc4346d55e..7918946d75 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From c18439f99b7056619fcf1d7f7df3697d1ee5b63c Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 d8fd323056..534d5df1ca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous +package org.matrix.android.sdk.api.rendezvous import android.net.Uri import com.squareup.moshi.Json @@ -29,10 +29,10 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NA import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.util.MatrixJsonParser -import org.matrix.android.sdk.internal.rendezvous.channels.ECDHRendezvousChannel -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent -import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransport +import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel +import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent +import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport import timber.log.Timber internal enum class PayloadType(val value: String) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt similarity index 84% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt index 6655468808..2a73e8f112 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous +package org.matrix.android.sdk.api.rendezvous -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent +import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent interface RendezvousChannel { var transport: RendezvousTransport; diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt index 0920bf224f..a607dc7f38 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousFailureReason.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous +package org.matrix.android.sdk.api.rendezvous enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) { UserDeclined("user_declined"), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt similarity index 83% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt index f054b7cb73..11471288f6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/RendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous +package org.matrix.android.sdk.api.rendezvous import okhttp3.MediaType -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails +import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails interface RendezvousTransport { var ready: Boolean; diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt index 9f347b8536..0f69bd7eda 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.channels +package org.matrix.android.sdk.api.rendezvous.channels import android.util.Base64 import com.squareup.moshi.Json @@ -23,15 +23,15 @@ import okhttp3.MediaType.Companion.toMediaType import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.util.MatrixJsonParser import org.matrix.android.sdk.internal.extensions.toUnsignedInt -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason -import org.matrix.android.sdk.internal.rendezvous.RendezvousChannel -import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvous -import org.matrix.android.sdk.internal.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousError -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousIntent -import org.matrix.android.sdk.internal.rendezvous.model.SecureRendezvousChannelAlgorithm -import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails +import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.api.rendezvous.RendezvousChannel +import org.matrix.android.sdk.api.rendezvous.RendezvousTransport +import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous +import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.api.rendezvous.model.RendezvousError +import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent +import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm +import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails import org.matrix.olm.OlmSAS import timber.log.Timber import java.security.SecureRandom @@ -107,10 +107,12 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu } else { // send our public key unencrypted // Timber.tag(TAG).i("Sending public key") - send(ECDHPayload( + send( + ECDHPayload( algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) - )) + ) + ) } olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt similarity index 83% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt index 14976ec836..b203101b66 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/ECDHRendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,11 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.model +package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.internal.rendezvous.transports.SimpleHttpRendezvousTransportDetails +import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails @JsonClass(generateAdapter = true) data class ECDHRendezvous( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt similarity index 72% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt index 5a609d497e..785ce1fed7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/EmbeddedRendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.model +package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) open class EmbeddedRendezvous( - @Json(name = "transport") val transport: RendezvousTransportDetails, - @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm + @Json(name = "transport") val transport: RendezvousTransportDetails, + @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt similarity index 79% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt index feb7d0cf35..f731c89649 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousError.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.model +package org.matrix.android.sdk.api.rendezvous.model -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) { } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt similarity index 87% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt index d317531835..1c070599b0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousIntent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.model +package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt similarity index 87% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt index ee9058daa3..55b3bbb5d9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportDetails.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.model +package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt similarity index 86% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt index 51371ddb2f..9c3e44f25b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/RendezvousTransportType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.model +package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt similarity index 87% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt index 122b6ede82..9a9db58a41 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/model/SecureRendezvousChannelAlgorithm.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.model +package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index 7918946d75..ba60459544 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2022 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.rendezvous.transports +package org.matrix.android.sdk.api.rendezvous.transports import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -23,10 +23,10 @@ import okhttp3.MediaType import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import org.matrix.android.sdk.api.logger.LoggerTag -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason -import org.matrix.android.sdk.internal.rendezvous.RendezvousTransport -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportDetails -import org.matrix.android.sdk.internal.rendezvous.model.RendezvousTransportType +import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.api.rendezvous.RendezvousTransport +import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails +import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportType import timber.log.Timber import java.text.SimpleDateFormat import java.util.Date diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt index 4de191f863..4bef41b6c1 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt @@ -16,7 +16,7 @@ package im.vector.app.features.login.qr -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason sealed class QrCodeLoginConnectionStatus { object ConnectingToDevice : QrCodeLoginConnectionStatus() diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt index fb372cbb2f..5451a16b44 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -27,7 +27,7 @@ import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding import im.vector.app.features.themes.ThemeUtils -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason @AndroidEntryPoint class QrCodeLoginStatusFragment : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index 8461d8d88f..276cedad43 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -31,8 +31,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.rendezvous.Rendezvous -import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.api.rendezvous.Rendezvous +import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason import timber.log.Timber class QrCodeLoginViewModel @AssistedInject constructor( From c00ce91214cea1a0308d7885d46b5ea5224f5aea Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 534d5df1ca..e33130e529 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -22,6 +22,10 @@ import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.logger.LoggerTag +import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel +import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent +import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME @@ -29,10 +33,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NA import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME import org.matrix.android.sdk.api.util.MatrixJsonParser -import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel -import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent -import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport import timber.log.Timber internal enum class PayloadType(val value: String) { @@ -80,6 +80,7 @@ class Rendezvous( } private val adapter = MatrixJsonParser.getMoshi().adapter(Payload::class.java) + // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE @@ -98,7 +99,7 @@ class Rendezvous( } suspend fun startAfterScanningCode(): String? { - val checksum = channel.connect(); + val checksum = channel.connect() Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum") @@ -107,7 +108,7 @@ class Rendezvous( } // get protocols - Timber.tag(TAG).i("Waiting for protocols"); + Timber.tag(TAG).i("Waiting for protocols") val protocolsResponse = receive() if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) { @@ -123,7 +124,7 @@ class Rendezvous( } suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? { - Timber.tag(TAG).i("Waiting for login_token"); + Timber.tag(TAG).i("Waiting for login_token") val loginToken = receive() @@ -147,7 +148,7 @@ class Rendezvous( val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned") val login_token = loginToken.login_token ?: throw RuntimeException("No login token returned") - Timber.tag(TAG).i("Got login_token: $login_token for $homeserver"); + Timber.tag(TAG).i("Got login_token: $login_token for $homeserver") val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver)) return authenticationService.loginUsingQrLoginToken(hsConfig, login_token) @@ -167,11 +168,11 @@ class Rendezvous( val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifying_device_key) { Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer") - return; + return } // set other device as verified - Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified"); + Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) // TODO: what do we do with the master key? @@ -183,7 +184,7 @@ class Rendezvous( // request secrets from the verifying device Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") - session.sharedSecretStorageService() .let { + session.sharedSecretStorageService().let { it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId) it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) @@ -192,12 +193,12 @@ class Rendezvous( } private suspend fun receive(): Payload? { - val data = channel.receive()?: return null + val data = channel.receive() ?: return null return adapter.fromJson(data.toString(Charsets.UTF_8)) } private suspend fun send(payload: Payload) { - channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8)); + channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8)) } suspend fun cancel(reason: RendezvousFailureReason) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt index 2a73e8f112..588d034f10 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt @@ -20,25 +20,30 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent interface RendezvousChannel { - var transport: RendezvousTransport; + var transport: RendezvousTransport + /** * @returns the checksum/confirmation digits to be shown to the user */ suspend fun connect(): String + /** * Send a payload via the channel. * @param data payload to send */ suspend fun send(data: ByteArray) + /** * Receive a payload from the channel. * @returns the received payload */ suspend fun receive(): ByteArray? + /** * @returns a representation of the channel that can be encoded in a QR or similar */ suspend fun close() + // TODO: this should be transport independent in the future suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode suspend fun cancel(reason: RendezvousFailureReason) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt index 11471288f6..de0aed7efc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt @@ -20,10 +20,10 @@ import okhttp3.MediaType import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails interface RendezvousTransport { - var ready: Boolean; - var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?; - suspend fun details(): RendezvousTransportDetails; - suspend fun send(contentType: MediaType, data: ByteArray); - suspend fun receive(): ByteArray?; - suspend fun cancel(reason: RendezvousFailureReason); + var ready: Boolean + var onCancelled: ((reason: RendezvousFailureReason) -> Unit)? + suspend fun details(): RendezvousTransportDetails + suspend fun send(contentType: MediaType, data: ByteArray) + suspend fun receive(): ByteArray? + suspend fun cancel(reason: RendezvousFailureReason) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt index 0f69bd7eda..1c8bca5d1c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -21,10 +21,8 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import okhttp3.MediaType.Companion.toMediaType import org.matrix.android.sdk.api.logger.LoggerTag -import org.matrix.android.sdk.api.util.MatrixJsonParser -import org.matrix.android.sdk.internal.extensions.toUnsignedInt -import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason import org.matrix.android.sdk.api.rendezvous.RendezvousChannel +import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason import org.matrix.android.sdk.api.rendezvous.RendezvousTransport import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode @@ -32,8 +30,9 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousError import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails +import org.matrix.android.sdk.api.util.MatrixJsonParser +import org.matrix.android.sdk.internal.extensions.toUnsignedInt import org.matrix.olm.OlmSAS -import timber.log.Timber import java.security.SecureRandom import java.util.LinkedList import javax.crypto.Cipher @@ -72,7 +71,7 @@ const val KEY_SPEC = "AES" * Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903: * https://github.com/matrix-org/matrix-spec-proposals/pull/3903 */ -class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?): RendezvousChannel { +class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?) : RendezvousChannel { private var olmSAS: OlmSAS? private val ourPublicKey: ByteArray private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt index f731c89649..fec55ffb67 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt @@ -18,5 +18,4 @@ package org.matrix.android.sdk.api.rendezvous.model import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason -class RendezvousError(val description: String, val reason: RendezvousFailureReason): RuntimeException(description) { -} +class RendezvousError(val description: String, val reason: RendezvousFailureReason) : RuntimeException(description) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index ba60459544..475a4fbe6c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -36,7 +36,7 @@ private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName @JsonClass(generateAdapter = true) data class SimpleHttpRendezvousTransportDetails( @Json val uri: String -): RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1) +) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1) /** * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886 diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index 276cedad43..7a51098439 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -26,11 +26,9 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.platform.VectorViewModel -import im.vector.app.features.home.HomeActivity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.AuthenticationService -import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.rendezvous.Rendezvous import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason import timber.log.Timber From 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 17b4ff2409..ae3ba9574b 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt @@ -83,7 +83,6 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment 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 3a302feba0..1d8171a9e5 100644 --- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt +++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt @@ -95,6 +95,16 @@ class DebugFeaturesStateFactory @Inject constructor( key = DebugFeatureKeys.qrCodeLoginEnabled, factory = VectorFeatures::isQrCodeLoginEnabled ), + createBooleanFeature( + label = "Allow QR Code Login for all servers", + key = DebugFeatureKeys.allowQrCodeLoginForAllServers, + factory = VectorFeatures::allowQrCodeLoginForAllServers + ), + createBooleanFeature( + label = "Show QR Code Login in Device Manager", + key = DebugFeatureKeys.allowReciprocateQrCodeLogin, + factory = VectorFeatures::allowReciprocateQrCodeLogin + ), createBooleanFeature( label = "Enable Voice Broadcast", key = DebugFeatureKeys.voiceBroadcastEnabled, diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt index b5ffa28db7..701f2dcab8 100644 --- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt +++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt @@ -82,6 +82,12 @@ class DebugVectorFeatures( override fun isQrCodeLoginEnabled() = read(DebugFeatureKeys.qrCodeLoginEnabled) ?: vectorFeatures.isQrCodeLoginEnabled() + override fun allowQrCodeLoginForAllServers() = read(DebugFeatureKeys.allowQrCodeLoginForAllServers) + ?: vectorFeatures.allowQrCodeLoginForAllServers() + + override fun allowReciprocateQrCodeLogin() = read(DebugFeatureKeys.allowReciprocateQrCodeLogin) + ?: vectorFeatures.allowReciprocateQrCodeLogin() + override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled) ?: vectorFeatures.isVoiceBroadcastEnabled() @@ -147,5 +153,7 @@ object DebugFeatureKeys { val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled") val newDeviceManagementEnabled = booleanPreferencesKey("new-device-management-enabled") val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled") + val allowQrCodeLoginForAllServers = booleanPreferencesKey("allow-qr-code-login-for-all-servers") + val allowReciprocateQrCodeLogin = booleanPreferencesKey("allow-reciprocate-qr-code-login") val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled") } diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt index 62eb0523b0..061276ba21 100644 --- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -42,6 +42,8 @@ interface VectorFeatures { fun isNewAppLayoutFeatureEnabled(): Boolean fun isNewDeviceManagementEnabled(): Boolean fun isQrCodeLoginEnabled(): Boolean + fun allowQrCodeLoginForAllServers(): Boolean + fun allowReciprocateQrCodeLogin(): Boolean fun isVoiceBroadcastEnabled(): Boolean } @@ -60,5 +62,7 @@ class DefaultVectorFeatures : VectorFeatures { override fun isNewAppLayoutFeatureEnabled(): Boolean = true override fun isNewDeviceManagementEnabled(): Boolean = false override fun isQrCodeLoginEnabled(): Boolean = false + override fun allowQrCodeLoginForAllServers(): Boolean = false + override fun allowReciprocateQrCodeLogin(): Boolean = false override fun isVoiceBroadcastEnabled(): Boolean = false } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 03cf2f43e6..8e64948c6a 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -124,7 +124,16 @@ class OnboardingViewModel @AssistedInject constructor( canLoginWithQrCode = false ) } + } else if (vectorFeatures.allowQrCodeLoginForAllServers()) { + // allow for all servers + setState { + copy( + canLoginWithQrCode = true + ) + } } else { + // check if selected server supports MSC3882 first + // FIXME: this should be checking the selected homeserver not defaultHomeserverUrl homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let { val canLoginWithQrCode = authenticationService.isQrLoginSupported(it) setState { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index 48f66cfc75..7e6da851dd 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -158,7 +158,7 @@ class VectorSettingsDevicesFragment : } private fun initQrLoginView() { - if (!vectorFeatures.isQrCodeLoginEnabled()) { + if (!vectorFeatures.allowReciprocateQrCodeLogin()) { views.deviceListHeaderSignInWithQrCode.isVisible = false views.deviceListHeaderScanQrCodeButton.isVisible = false views.deviceListHeaderShowQrCodeButton.isVisible = false From de4232dff519b05063e0e671789802bbd96830cd Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 5451a16b44..05ed070915 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -115,7 +115,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment 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 174e3c54c0..18ce26171c 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -48,7 +48,13 @@ jobs: # Skip in forks if: > github.repository == 'vector-im/element-android' && - contains(github.event.issue.labels.*.name, 'X-Needs-Design') + contains(github.event.issue.labels.*.name, 'X-Needs-Design') && + (contains(github.event.issue.labels.*.name, 'S-Critical') && + (contains(github.event.issue.labels.*.name, 'O-Frequent') || + contains(github.event.issue.labels.*.name, 'O-Occasional')) || + contains(github.event.issue.labels.*.name, 'S-Major') && + contains(github.event.issue.labels.*.name, 'O-Frequent') || + contains(github.event.issue.labels.*.name, 'A11y')) steps: - uses: octokit/graphql-action@v2.x id: add_to_project From 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 e33130e529..17f3a73181 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -176,10 +176,17 @@ class Rendezvous( crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) // TODO: what do we do with the master key? -// verificationResponse.master_key ?.let { -// // set master key as trusted -// crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, it) -// } + verificationResponse.master_key ?.let { masterKeyFromVerifyingDevice -> + // set master key as trusted + crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey -> + if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) { + Timber.tag(TAG).i("Setting master key as trusted") + crypto.crossSigningService().markMyMasterKeyAsTrusted() + } else { + Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey") + } + } ?: Timber.tag(TAG).i("No local master key") + } ?: Timber.tag(TAG).i("No master key given by verifying device") // request secrets from the verifying device Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") From f999e7275952633a87d9cff123a4211546e7e735 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 0000000000..240fac3516 --- /dev/null +++ b/changelog.d/7369.feature @@ -0,0 +1 @@ +Add logic for sign in with QR code From 411b766890ab33722b10be177bb1f3e64d963e75 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 17f3a73181..67bdeb2235 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -49,12 +49,12 @@ internal data class Payload( @Json val protocols: List? = 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 67bdeb2235..6403d17031 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -91,7 +91,11 @@ class Rendezvous( if (incompatible) { send(Payload(PayloadType.Finish, intent = ourIntent)) - val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) RendezvousFailureReason.OtherDeviceNotSignedIn else RendezvousFailureReason.OtherDeviceAlreadySignedIn + val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) { + RendezvousFailureReason.OtherDeviceNotSignedIn + } else { + RendezvousFailureReason.OtherDeviceAlreadySignedIn + } channel.cancel(reason) } @@ -175,7 +179,6 @@ class Rendezvous( Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) - // TODO: what do we do with the master key? verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice -> // set master key as trusted crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey -> From 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 588d034f10..9d2843ce66 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt @@ -19,6 +19,9 @@ package org.matrix.android.sdk.api.rendezvous import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent +/** + * Representation of a rendezvous channel such as that described by MSC3903 + */ interface RendezvousChannel { var transport: RendezvousTransport @@ -44,7 +47,7 @@ interface RendezvousChannel { */ suspend fun close() - // TODO: this should be transport independent in the future + // In future we probably want this to be a more generic RendezvousCode but it is suffice for now suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode suspend fun cancel(reason: RendezvousFailureReason) } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt index 556be3af4d..f0030d6763 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -80,7 +80,8 @@ class QrCodeLoginStatusFragment : VectorBaseFragment 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 7a51098439..af0df22ca3 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -24,8 +24,8 @@ import dagger.assisted.AssistedInject import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory -import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.session.ConfigureAndStartSessionUseCase import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.AuthenticationService @@ -37,7 +37,8 @@ class QrCodeLoginViewModel @AssistedInject constructor( @Assisted private val initialState: QrCodeLoginViewState, private val applicationContext: Context, private val authenticationService: AuthenticationService, - private val activeSessionHolder: ActiveSessionHolder + private val activeSessionHolder: ActiveSessionHolder, + private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase ) : VectorViewModel(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 af0df22ca3..81a00cf548 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -98,7 +98,7 @@ class QrCodeLoginViewModel @AssistedInject constructor( activeSessionHolder.setActiveSession(session) authenticationService.reset() - configureAndStartSessionUseCase.execute(session, startSyncing) + configureAndStartSessionUseCase.execute(session) rendezvous.completeVerificationOnNewDevice(session) From 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 6403d17031..b43b122cba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -36,9 +36,14 @@ import org.matrix.android.sdk.api.util.MatrixJsonParser import timber.log.Timber internal enum class PayloadType(val value: String) { - @Json(name = "m.login.start") Start("m.login.start"), - @Json(name = "m.login.finish") Finish("m.login.finish"), - @Json(name = "m.login.progress") Progress("m.login.progress") + @Json(name = "m.login.start") + Start("m.login.start"), + + @Json(name = "m.login.finish") + Finish("m.login.finish"), + + @Json(name = "m.login.progress") + Progress("m.login.progress") } @JsonClass(generateAdapter = true) @@ -150,12 +155,12 @@ class Rendezvous( } val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned") - val login_token = loginToken.loginToken ?: throw RuntimeException("No login token returned") + val token = loginToken.loginToken ?: throw RuntimeException("No login token returned") - Timber.tag(TAG).i("Got login_token: $login_token for $homeserver") + Timber.tag(TAG).i("Got login_token now attempting to sign in with $homeserver") val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver)) - return authenticationService.loginUsingQrLoginToken(hsConfig, login_token) + return authenticationService.loginUsingQrLoginToken(hsConfig, token) } suspend fun completeVerificationOnNewDevice(session: Session) { @@ -171,8 +176,8 @@ class Rendezvous( val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned") val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) { - Timber.tag(TAG).w("Verifying device $verifyingDeviceId doesn't match: $verifyingDeviceFromServer") - return + Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})") + throw RuntimeException("Key from verifying device doesn't match") } // set other device as verified @@ -187,6 +192,7 @@ class Rendezvous( crypto.crossSigningService().markMyMasterKeyAsTrusted() } else { Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey") + throw RuntimeException("Master key from verifying device doesn't match") } } ?: Timber.tag(TAG).i("No local master key") } ?: Timber.tag(TAG).i("No master key given by verifying device") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt index 1c8bca5d1c..4d5ed30ac5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTran import org.matrix.android.sdk.api.util.MatrixJsonParser import org.matrix.android.sdk.internal.extensions.toUnsignedInt import org.matrix.olm.OlmSAS +import timber.log.Timber import java.security.SecureRandom import java.util.LinkedList import javax.crypto.Cipher @@ -93,7 +94,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu val isInitiator = theirPublicKey == null if (isInitiator) { -// Timber.tag(TAG).i("Waiting for other device to send their public key") + Timber.tag(TAG).i("Waiting for other device to send their public key") val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device") if (res.key == null) { @@ -105,7 +106,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) } else { // send our public key unencrypted -// Timber.tag(TAG).i("Sending public key") + Timber.tag(TAG).i("Sending public key") send( ECDHPayload( algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, @@ -122,11 +123,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu aesKey = olmSAS!!.generateShortCode(aesInfo, 32) -// Timber.tag(TAG).i("Our public key: ${Base64.encodeToString(ourPublicKey, Base64.NO_WRAP)}") -// Timber.tag(TAG).i("Their public key: ${Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)}") -// Timber.tag(TAG).i("AES info: $aesInfo") -// Timber.tag(TAG).i("AES key: ${Base64.encodeToString(aesKey, Base64.NO_WRAP)}") - val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5) return getDecimalCodeRepresentation(rawChecksum) } @@ -181,7 +177,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu } private fun encrypt(plainText: ByteArray): ECDHPayload { -// Timber.tag(TAG).d("Encrypting: ${plainText.toString(Charsets.UTF_8)}") val iv = ByteArray(16) SecureRandom().nextBytes(iv) @@ -211,9 +206,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu plainText.addAll(encryptCipher.update(Base64.decode(payload.ciphertext, Base64.NO_WRAP)).toList()) plainText.addAll(encryptCipher.doFinal().toList()) - val plainTextBytes = plainText.toByteArray() - -// Timber.tag(TAG).d("Decrypted: ${plainTextBytes.toString(Charsets.UTF_8)}") - return plainTextBytes + return plainText.toByteArray() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index 475a4fbe6c..004cf38e24 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -67,8 +67,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo // TODO: properly determine endpoint val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev" -// Timber.tag(TAG).i("Sending data: ${data.toString(Charsets.UTF_8)} to $uri") - val httpClient = okhttp3.OkHttpClient.Builder().build() val request = Request.Builder() @@ -123,8 +121,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo val response = httpClient.newCall(request.build()).execute() try { -// Timber.tag(TAG).d("Received polling response: ${response.code} from $uri") - if (response.code == 404) { cancel(RendezvousFailureReason.Unknown) return null @@ -140,9 +136,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo response.header("etag")?.let { etag = it } - val data = response.body?.bytes() -// Timber.tag(TAG).d("Received data: ${data?.toString(Charsets.UTF_8)} from $uri with etag $etag") - return data + return response.body?.bytes() } done = false @@ -159,7 +153,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo var mappedReason = reason Timber.tag(TAG).i("$expiresAt") if (mappedReason == RendezvousFailureReason.Unknown && - expiresAt != null && Date() > expiresAt) { + expiresAt != null && Date() > expiresAt + ) { mappedReason = RendezvousFailureReason.Expired } From ed6bc01bef48b4266dbe34e600f8038b0dcc6a3a Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 004cf38e24..dde5edcb93 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -64,8 +64,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo } val method = if (uri != null) "PUT" else "POST" - // TODO: properly determine endpoint - val uri = if (uri != null) uri!! else "https://rendezvous.lab.element.dev" + val uri = this.uri ?: throw RuntimeException("No rendezvous URI") val httpClient = okhttp3.OkHttpClient.Builder().build() From 33be5c257dcaa8c674486a77c5851d1a6a626f7e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 b43b122cba..4270d4a09c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -17,13 +17,13 @@ package org.matrix.android.sdk.api.rendezvous import android.net.Uri -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.api.rendezvous.model.Payload +import org.matrix.android.sdk.api.rendezvous.model.PayloadType import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport import org.matrix.android.sdk.api.session.Session @@ -35,35 +35,6 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_S import org.matrix.android.sdk.api.util.MatrixJsonParser import timber.log.Timber -internal enum class PayloadType(val value: String) { - @Json(name = "m.login.start") - Start("m.login.start"), - - @Json(name = "m.login.finish") - Finish("m.login.finish"), - - @Json(name = "m.login.progress") - Progress("m.login.progress") -} - -@JsonClass(generateAdapter = true) -internal data class Payload( - @Json val type: PayloadType, - @Json val intent: RendezvousIntent? = null, - @Json val outcome: String? = null, - @Json val protocols: List? = null, - @Json val protocol: String? = null, - @Json val homeserver: String? = null, - @Json(name = "login_token") val loginToken: String? = null, - @Json(name = "device_id") val deviceId: String? = null, - @Json(name = "device_key") val deviceKey: String? = null, - @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null, - @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null, - @Json(name = "master_key") val masterKey: String? = null -) - -private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value - /** * Implementation of MSC3906 to sign in + E2EE set up using a QR code. */ @@ -72,6 +43,8 @@ class Rendezvous( val theirIntent: RendezvousIntent, ) { companion object { + private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value + fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous { val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code") @@ -176,7 +149,9 @@ class Rendezvous( val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned") val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) { - Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})") + Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${ + verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})" + ) throw RuntimeException("Key from verifying device doesn't match") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt index 4d5ed30ac5..5dbac894ae 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode import org.matrix.android.sdk.api.rendezvous.model.RendezvousError import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm -import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails +import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails import org.matrix.android.sdk.api.util.MatrixJsonParser import org.matrix.android.sdk.internal.extensions.toUnsignedInt import org.matrix.olm.OlmSAS @@ -40,39 +40,40 @@ import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec -@JsonClass(generateAdapter = true) -data class ECDHPayload( - @Json val algorithm: SecureRendezvousChannelAlgorithm? = null, - @Json val key: String? = null, - @Json val ciphertext: String? = null, - @Json val iv: String? = null -) - -private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value - -fun getDecimalCodeRepresentation(byteArray: ByteArray): String { - val b0 = byteArray[0].toUnsignedInt() // need unsigned byte - val b1 = byteArray[1].toUnsignedInt() // need unsigned byte - val b2 = byteArray[2].toUnsignedInt() // need unsigned byte - val b3 = byteArray[3].toUnsignedInt() // need unsigned byte - val b4 = byteArray[4].toUnsignedInt() // need unsigned byte - // (B0 << 5 | B1 >> 3) + 1000 - val first = (b0.shl(5) or b1.shr(3)) + 1000 - // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 - val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 - // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 - val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 - return "$first-$second-$third" -} - -const val ALGORITHM_SPEC = "AES/GCM/NoPadding" -const val KEY_SPEC = "AES" - /** * Implements X25519 ECDH key agreement and AES-256-GCM encryption channel as per MSC3903: * https://github.com/matrix-org/matrix-spec-proposals/pull/3903 */ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPublicKeyBase64: String?) : RendezvousChannel { + companion object { + private const val ALGORITHM_SPEC = "AES/GCM/NoPadding" + private const val KEY_SPEC = "AES" + private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value + + private fun getDecimalCodeRepresentation(byteArray: ByteArray): String { + val b0 = byteArray[0].toUnsignedInt() // need unsigned byte + val b1 = byteArray[1].toUnsignedInt() // need unsigned byte + val b2 = byteArray[2].toUnsignedInt() // need unsigned byte + val b3 = byteArray[3].toUnsignedInt() // need unsigned byte + val b4 = byteArray[4].toUnsignedInt() // need unsigned byte + // (B0 << 5 | B1 >> 3) + 1000 + val first = (b0.shl(5) or b1.shr(3)) + 1000 + // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 + val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 + // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 + val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 + return "$first-$second-$third" + } + } + + @JsonClass(generateAdapter = true) + internal data class ECDHPayload( + @Json val algorithm: SecureRendezvousChannelAlgorithm? = null, + @Json val key: String? = null, + @Json val ciphertext: String? = null, + @Json val iv: String? = null + ) + private var olmSAS: OlmSAS? private val ourPublicKey: ByteArray private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt index b203101b66..0840e1ca2e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransportDetails @JsonClass(generateAdapter = true) data class ECDHRendezvous( @@ -26,9 +25,3 @@ data class ECDHRendezvous( @Json val algorithm: SecureRendezvousChannelAlgorithm, @Json val key: String ) - -@JsonClass(generateAdapter = true) -data class ECDHRendezvousCode( - @Json val intent: RendezvousIntent, - @Json val rendezvous: ECDHRendezvous -) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt new file mode 100644 index 0000000000..410c5c1036 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.rendezvous.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class ECDHRendezvousCode( + @Json val intent: RendezvousIntent, + @Json val rendezvous: ECDHRendezvous +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt new file mode 100644 index 0000000000..5627452232 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.rendezvous.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class Payload( + @Json val type: PayloadType, + @Json val intent: RendezvousIntent? = null, + @Json val outcome: String? = null, + @Json val protocols: List? = null, + @Json val protocol: String? = null, + @Json val homeserver: String? = null, + @Json(name = "login_token") val loginToken: String? = null, + @Json(name = "device_id") val deviceId: String? = null, + @Json(name = "device_key") val deviceKey: String? = null, + @Json(name = "verifying_device_id") val verifyingDeviceId: String? = null, + @Json(name = "verifying_device_key") val verifyingDeviceKey: String? = null, + @Json(name = "master_key") val masterKey: String? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt new file mode 100644 index 0000000000..9854e7c070 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.rendezvous.model + +import com.squareup.moshi.Json + +internal enum class PayloadType(val value: String) { + @Json(name = "m.login.start") + Start("m.login.start"), + + @Json(name = "m.login.finish") + Finish("m.login.finish"), + + @Json(name = "m.login.progress") + Progress("m.login.progress") +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt new file mode 100644 index 0000000000..70a441d760 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.rendezvous.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class SimpleHttpRendezvousTransportDetails( + @Json val uri: String +) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index dde5edcb93..03e8b0cda7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -26,22 +26,19 @@ import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason import org.matrix.android.sdk.api.rendezvous.RendezvousTransport import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails -import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportType +import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails import timber.log.Timber import java.text.SimpleDateFormat import java.util.Date -private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value - -@JsonClass(generateAdapter = true) -data class SimpleHttpRendezvousTransportDetails( - @Json val uri: String -) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1) - /** * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886 */ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport { + companion object { + private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value + } + override var ready = false private var cancelled = false private var uri: String? From 3be4a0ca2137c339b66b9b5aa12bb0617c15d935 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 81a00cf548..8b70f61e80 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -35,7 +35,6 @@ import timber.log.Timber class QrCodeLoginViewModel @AssistedInject constructor( @Assisted private val initialState: QrCodeLoginViewState, - private val applicationContext: Context, private val authenticationService: AuthenticationService, private val activeSessionHolder: ActiveSessionHolder, private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase From 48de8f4e345105668298ec238bbad5600c057ab9 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 d65e629b71..42bba5fe88 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -133,6 +133,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( homeServerCapabilitiesEntity.roomVersionsJson = capabilities?.roomVersions?.let { MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it) } + homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */ + getVersionResult?.doesServerSupportThreads().orFalse() } if (getMediaConfigResult != null) { From 506fa729ea8f9d77357cc0f1347d3227e6b51702 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 4270d4a09c..e467ff06e3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -22,8 +22,10 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode +import org.matrix.android.sdk.api.rendezvous.model.Outcome import org.matrix.android.sdk.api.rendezvous.model.Payload import org.matrix.android.sdk.api.rendezvous.model.PayloadType +import org.matrix.android.sdk.api.rendezvous.model.Protocol import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport import org.matrix.android.sdk.api.session.Session @@ -68,7 +70,7 @@ class Rendezvous( Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible") if (incompatible) { - send(Payload(PayloadType.Finish, intent = ourIntent)) + send(Payload(PayloadType.FINISH, intent = ourIntent)) val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) { RendezvousFailureReason.OtherDeviceNotSignedIn } else { @@ -93,14 +95,14 @@ class Rendezvous( Timber.tag(TAG).i("Waiting for protocols") val protocolsResponse = receive() - if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains("login_token")) { - send(Payload(PayloadType.Finish, outcome = "unsupported")) + if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) { + send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED)) Timber.tag(TAG).i("No supported protocol") cancel(RendezvousFailureReason.Unknown) return null } - send(Payload(PayloadType.Progress, protocol = "login_token")) + send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN)) return checksum } @@ -110,21 +112,23 @@ class Rendezvous( val loginToken = receive() - if (loginToken?.type == PayloadType.Finish) { + if (loginToken?.type == PayloadType.FINISH) { when (loginToken.outcome) { - "declined" -> { + Outcome.DECLINED -> { Timber.tag(TAG).i("Login declined by other device") channel.cancel(RendezvousFailureReason.UserDeclined) return null } - "unsupported" -> { + Outcome.UNSUPPORTED -> { Timber.tag(TAG).i("Not supported") channel.cancel(RendezvousFailureReason.HomeserverLacksSupport) return null } + else -> { + channel.cancel(RendezvousFailureReason.Unknown) + return null + } } - channel.cancel(RendezvousFailureReason.Unknown) - return null } val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned") @@ -141,7 +145,7 @@ class Rendezvous( val crypto = session.cryptoService() val deviceId = crypto.getMyDevice().deviceId val deviceKey = crypto.getMyDevice().fingerprint() - send(Payload(PayloadType.Progress, outcome = "success", deviceId = deviceId, deviceKey = deviceKey)) + send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey)) // await confirmation of verification @@ -149,8 +153,9 @@ class Rendezvous( val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned") val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) { - Timber.tag(TAG).w("Verifying device $verifyingDeviceId key doesn't match: ${ - verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})" + Timber.tag(TAG).w( + "Verifying device $verifyingDeviceId key doesn't match: ${ + verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})" ) throw RuntimeException("Key from verifying device doesn't match") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt index 5dbac894ae..489d20e588 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -50,6 +50,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu private const val KEY_SPEC = "AES" private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value + // n.b. we are only aver processing byte array that we have generated, so we can make assumptions about the length private fun getDecimalCodeRepresentation(byteArray: ByteArray): String { val b0 = byteArray[0].toUnsignedInt() // need unsigned byte val b1 = byteArray[1].toUnsignedInt() // need unsigned byte diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt new file mode 100644 index 0000000000..2dd6e7be28 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.rendezvous.model + +import com.squareup.moshi.Json + +enum class Outcome(val value: String) { + @Json(name = "success") + SUCCESS("success"), + + @Json(name = "declined") + DECLINED("declined"), + + @Json(name = "unsupported") + UNSUPPORTED("unsupported") +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt index 5627452232..593177e625 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt @@ -23,9 +23,9 @@ import com.squareup.moshi.JsonClass internal data class Payload( @Json val type: PayloadType, @Json val intent: RendezvousIntent? = null, - @Json val outcome: String? = null, - @Json val protocols: List? = 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 9854e7c070..5ff4cd7cfa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt @@ -20,11 +20,11 @@ import com.squareup.moshi.Json internal enum class PayloadType(val value: String) { @Json(name = "m.login.start") - Start("m.login.start"), + START("m.login.start"), @Json(name = "m.login.finish") - Finish("m.login.finish"), + FINISH("m.login.finish"), @Json(name = "m.login.progress") - Progress("m.login.progress") + PROGRESS("m.login.progress") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt new file mode 100644 index 0000000000..18381984a5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.api.rendezvous.model + +import com.squareup.moshi.Json + +enum class Protocol(val value: String) { + @Json(name = "login_token") + LOGIN_TOKEN("login_token") +} From 4306c57236dccd50531bf20e8732031a84d6bb0e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith Date: Mon, 17 Oct 2022 12:01:12 +0100 Subject: [PATCH 069/300] Thread safe use of OlmSAS --- .../channels/ECDHRendezvousChannel.kt | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt index 489d20e588..ca7083b297 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -90,43 +90,45 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu } override suspend fun connect(): String { - if (olmSAS == null) { - throw RuntimeException("Channel closed") - } - val isInitiator = theirPublicKey == null + olmSAS ?.let { olmSAS -> + val isInitiator = theirPublicKey == null - if (isInitiator) { - Timber.tag(TAG).i("Waiting for other device to send their public key") - val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device") + if (isInitiator) { + Timber.tag(TAG).i("Waiting for other device to send their public key") + val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device") - if (res.key == null) { - throw RendezvousError( - "Unsupported algorithm: ${res.algorithm}", - RendezvousFailureReason.UnsupportedAlgorithm, + if (res.key == null) { + throw RendezvousError( + "Unsupported algorithm: ${res.algorithm}", + RendezvousFailureReason.UnsupportedAlgorithm, + ) + } + theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) + } else { + // send our public key unencrypted + Timber.tag(TAG).i("Sending public key") + send( + ECDHPayload( + algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, + key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) + ) ) } - theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) - } else { - // send our public key unencrypted - Timber.tag(TAG).i("Sending public key") - send( - ECDHPayload( - algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, - key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) - ) - ) - } - olmSAS!!.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) + synchronized(olmSAS) { + olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) + olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) - val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP) - val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP) - val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey" + val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP) + val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP) + val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey" - aesKey = olmSAS!!.generateShortCode(aesInfo, 32) + aesKey = olmSAS.generateShortCode(aesInfo, 32) - val rawChecksum = olmSAS!!.generateShortCode(aesInfo, 5) - return getDecimalCodeRepresentation(rawChecksum) + val rawChecksum = olmSAS.generateShortCode(aesInfo, 5) + return getDecimalCodeRepresentation(rawChecksum) + } + } ?: throw RuntimeException("Channel closed") } private suspend fun send(payload: ECDHPayload) { @@ -174,8 +176,13 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu } override suspend fun close() { - olmSAS?.releaseSas() - olmSAS = null + olmSAS ?.let { + synchronized(it) { + // this does a double release check already so we don't re-check ourselves + it.releaseSas() + olmSAS = null + } + } } private fun encrypt(plainText: ByteArray): ECDHPayload { From fb86ab70a270e124b15b3e7af888381f32b78dd8 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 03e8b0cda7..e899a64e99 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -99,9 +99,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo override suspend fun receive(): ByteArray? { val uri = uri ?: throw IllegalStateException("Rendezvous not set up") - var done = false val httpClient = okhttp3.OkHttpClient.Builder().build() - while (!done) { + while (true) { if (cancelled) { return null } @@ -117,8 +116,9 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo val response = httpClient.newCall(request.build()).execute() try { + // expired if (response.code == 404) { - cancel(RendezvousFailureReason.Unknown) + cancel(RendezvousFailureReason.Expired) return null } @@ -135,7 +135,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo return response.body?.bytes() } - done = false + // sleep for a second before polling again + // we rely on the server expiring the channel rather than checking it ourselves delay(1000) } finally { response.close() From 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 9d2843ce66..31014e392d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt @@ -20,7 +20,7 @@ import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent /** - * Representation of a rendezvous channel such as that described by MSC3903 + * Representation of a rendezvous channel such as that described by MSC3903. */ interface RendezvousChannel { var transport: RendezvousTransport diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index e899a64e99..3e9b11a68b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -16,8 +16,6 @@ package org.matrix.android.sdk.api.rendezvous.transports -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass import kotlinx.coroutines.delay import okhttp3.MediaType import okhttp3.Request @@ -142,8 +140,6 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo response.close() } } - - return null } override suspend fun cancel(reason: RendezvousFailureReason) { diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index 8b70f61e80..36e88b284c 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -16,7 +16,6 @@ package im.vector.app.features.login.qr -import android.content.Context import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory From eb30ef166acff6fca015b321c71ce1b10a811c49 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 3e9b11a68b..ca2a3425cd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -75,7 +75,9 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo val response = httpClient.newCall(request.build()).execute() if (response.code == 404) { + // we set to unknown and the cancel method will rewrite the reason to expired if applicable cancel(RendezvousFailureReason.Unknown) + return } etag = response.header("etag") @@ -116,7 +118,8 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo try { // expired if (response.code == 404) { - cancel(RendezvousFailureReason.Expired) + // we set to unknown and the cancel method will rewrite the reason to expired if applicable + cancel(RendezvousFailureReason.Unknown) return null } From 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 9147c4e1cd..2c3cb440b6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -132,8 +132,6 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( homeServerCapabilitiesEntity.roomVersionsJson = capabilities?.roomVersions?.let { MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it) } - homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */ - getVersionResult?.doesServerSupportThreads().orFalse() } if (getMediaConfigResult != null) { From 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 | 57 ++++--- .../login/qr/QrCodeLoginStatusFragment.kt | 6 + .../features/login/qr/QrCodeLoginViewModel.kt | 27 +++- 11 files changed, 171 insertions(+), 146 deletions(-) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 7d0173f341..e28ebb31c6 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3383,6 +3383,12 @@ 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 e467ff06e3..9ad889fca0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.rendezvous.model.Outcome import org.matrix.android.sdk.api.rendezvous.model.Payload import org.matrix.android.sdk.api.rendezvous.model.PayloadType import org.matrix.android.sdk.api.rendezvous.model.Protocol +import org.matrix.android.sdk.api.rendezvous.model.RendezvousError import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent import org.matrix.android.sdk.api.rendezvous.transports.SimpleHttpRendezvousTransport import org.matrix.android.sdk.api.session.Session @@ -47,10 +48,16 @@ class Rendezvous( companion object { private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value - fun buildChannelFromCode(code: String, onCancelled: (reason: RendezvousFailureReason) -> Unit): Rendezvous { - val parsed = MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) ?: throw RuntimeException("Invalid code") + @Throws(RendezvousError::class) + fun buildChannelFromCode(code: String): Rendezvous { + val parsed = try { + // we rely on moshi validating the code and throwing exception if invalid JSON or doesn't + MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) + } catch (a: Throwable) { + throw RendezvousError("Invalid code", RendezvousFailureReason.InvalidCode) + } ?: throw RendezvousError("Invalid code", RendezvousFailureReason.InvalidCode) - val transport = SimpleHttpRendezvousTransport(onCancelled, parsed.rendezvous.transport.uri) + val transport = SimpleHttpRendezvousTransport(parsed.rendezvous.transport.uri) return Rendezvous( ECDHRendezvousChannel(transport, parsed.rendezvous.key), @@ -64,32 +71,30 @@ class Rendezvous( // not yet implemented: RendezvousIntent.RECIPROCATE_LOGIN_ON_EXISTING_DEVICE val ourIntent: RendezvousIntent = RendezvousIntent.LOGIN_ON_NEW_DEVICE - private suspend fun areIntentsIncompatible(): Boolean { + @Throws(RendezvousError::class) + private suspend fun checkCompatibility() { val incompatible = theirIntent == ourIntent Timber.tag(TAG).d("ourIntent: $ourIntent, theirIntent: $theirIntent, incompatible: $incompatible") if (incompatible) { + // inform the other side send(Payload(PayloadType.FINISH, intent = ourIntent)) - val reason = if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) { - RendezvousFailureReason.OtherDeviceNotSignedIn + if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) { + throw RendezvousError("The other device isn't signed in", RendezvousFailureReason.OtherDeviceNotSignedIn) } else { - RendezvousFailureReason.OtherDeviceAlreadySignedIn + throw RendezvousError("The other device is already signed in", RendezvousFailureReason.OtherDeviceAlreadySignedIn) } - channel.cancel(reason) } - - return incompatible } + @Throws(RendezvousError::class) suspend fun startAfterScanningCode(): String? { val checksum = channel.connect() Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum") - if (areIntentsIncompatible()) { - return null - } + checkCompatibility() // get protocols Timber.tag(TAG).i("Waiting for protocols") @@ -97,9 +102,7 @@ class Rendezvous( if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) { send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED)) - Timber.tag(TAG).i("No supported protocol") - cancel(RendezvousFailureReason.Unknown) - return null + throw RendezvousError("Unsupported protocols", RendezvousFailureReason.UnsupportedHomeserver) } send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN)) @@ -107,6 +110,7 @@ class Rendezvous( return checksum } + @Throws(RendezvousError::class) suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? { Timber.tag(TAG).i("Waiting for login_token") @@ -115,24 +119,19 @@ class Rendezvous( if (loginToken?.type == PayloadType.FINISH) { when (loginToken.outcome) { Outcome.DECLINED -> { - Timber.tag(TAG).i("Login declined by other device") - channel.cancel(RendezvousFailureReason.UserDeclined) - return null + throw RendezvousError("Login declined by other device", RendezvousFailureReason.UserDeclined) } Outcome.UNSUPPORTED -> { - Timber.tag(TAG).i("Not supported") - channel.cancel(RendezvousFailureReason.HomeserverLacksSupport) - return null + throw RendezvousError("Homeserver lacks support", RendezvousFailureReason.UnsupportedHomeserver) } else -> { - channel.cancel(RendezvousFailureReason.Unknown) - return null + throw RendezvousError("Unknown error", RendezvousFailureReason.Unknown) } } } - val homeserver = loginToken?.homeserver ?: throw RuntimeException("No homeserver returned") - val token = loginToken.loginToken ?: throw RuntimeException("No login token returned") + val homeserver = loginToken?.homeserver ?: throw RendezvousError("No homeserver returned", RendezvousFailureReason.ProtocolError) + val token = loginToken.loginToken ?: throw RendezvousError("No login token returned", RendezvousFailureReason.ProtocolError) Timber.tag(TAG).i("Got login_token now attempting to sign in with $homeserver") @@ -140,6 +139,7 @@ class Rendezvous( return authenticationService.loginUsingQrLoginToken(hsConfig, token) } + @Throws(RendezvousError::class) suspend fun completeVerificationOnNewDevice(session: Session) { val userId = session.myUserId val crypto = session.cryptoService() @@ -148,59 +148,77 @@ class Rendezvous( send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey)) // await confirmation of verification - val verificationResponse = receive() - val verifyingDeviceId = verificationResponse?.verifyingDeviceId ?: throw RuntimeException("No verifying device id returned") - val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) - if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) { - Timber.tag(TAG).w( - "Verifying device $verifyingDeviceId key doesn't match: ${ - verifyingDeviceFromServer?.fingerprint()} vs ${verificationResponse.verifyingDeviceKey})" - ) - throw RuntimeException("Key from verifying device doesn't match") - } + if (verificationResponse?.outcome == Outcome.VERIFIED) { + val verifyingDeviceId = verificationResponse.verifyingDeviceId + ?: throw RendezvousError("No verifying device id returned", RendezvousFailureReason.ProtocolError) + val verifyingDeviceFromServer = crypto.getCryptoDeviceInfo(userId, verifyingDeviceId) + if (verifyingDeviceFromServer?.fingerprint() != verificationResponse.verifyingDeviceKey) { + Timber.tag(TAG).w( + "Verifying device $verifyingDeviceId key doesn't match: ${ + verifyingDeviceFromServer?.fingerprint() + } vs ${verificationResponse.verifyingDeviceKey})" + ) + // inform the other side + send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR)) + throw RendezvousError("Key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue) + } - // set other device as verified - Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) + verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice -> + // check master key againt what the homeserver told us + crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey -> + if (localMasterKey.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) { + Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey") + // inform the other side + send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR)) + throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue) + } + // set other device as verified + Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") + crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) - verificationResponse.masterKey ?.let { masterKeyFromVerifyingDevice -> - // set master key as trusted - crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey -> - if (localMasterKey.unpaddedBase64PublicKey == masterKeyFromVerifyingDevice) { Timber.tag(TAG).i("Setting master key as trusted") crypto.crossSigningService().markMyMasterKeyAsTrusted() - } else { - Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey") - throw RuntimeException("Master key from verifying device doesn't match") - } - } ?: Timber.tag(TAG).i("No local master key") - } ?: Timber.tag(TAG).i("No master key given by verifying device") + } ?: Timber.tag(TAG).w("No local master key so not verifying") + } ?: run { + // set other device as verified anyway + Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") + crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) - // request secrets from the verifying device - Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") + Timber.tag(TAG).i("No master key given by verifying device") + } - session.sharedSecretStorageService().let { - it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId) - it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) - it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) - it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId) + // request secrets from the verifying device + Timber.tag(TAG).i("Requesting secrets from $verifyingDeviceId") + + session.sharedSecretStorageService().let { + it.requestSecret(MASTER_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(SELF_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(USER_SIGNING_KEY_SSSS_NAME, verifyingDeviceId) + it.requestSecret(KEYBACKUP_SECRET_SSSS_NAME, verifyingDeviceId) + } + } else { + Timber.tag(TAG).i("Not doing verification") } } + @Throws(RendezvousError::class) private suspend fun receive(): Payload? { val data = channel.receive() ?: return null - return adapter.fromJson(data.toString(Charsets.UTF_8)) + val payload = try { + adapter.fromJson(data.toString(Charsets.UTF_8)) + } catch (e: Exception) { + Timber.tag(TAG).w(e, "Failed to parse payload") + throw RendezvousError("Invalid payload received", RendezvousFailureReason.Unknown) + } + + return payload } private suspend fun send(payload: Payload) { channel.send(adapter.toJson(payload).toByteArray(Charsets.UTF_8)) } - suspend fun cancel(reason: RendezvousFailureReason) { - channel.cancel(reason) - } - suspend fun close() { channel.close() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt index 31014e392d..be79569164 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt @@ -16,8 +16,7 @@ package org.matrix.android.sdk.api.rendezvous -import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode -import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent +import org.matrix.android.sdk.api.rendezvous.model.RendezvousError /** * Representation of a rendezvous channel such as that described by MSC3903. @@ -28,26 +27,25 @@ interface RendezvousChannel { /** * @returns the checksum/confirmation digits to be shown to the user */ + @Throws(RendezvousError::class) suspend fun connect(): String /** * Send a payload via the channel. * @param data payload to send */ + @Throws(RendezvousError::class) suspend fun send(data: ByteArray) /** * Receive a payload from the channel. * @returns the received payload */ + @Throws(RendezvousError::class) suspend fun receive(): ByteArray? /** - * @returns a representation of the channel that can be encoded in a QR or similar + * @returns closes the channel and cleans up */ suspend fun close() - - // In future we probably want this to be a more generic RendezvousCode but it is suffice for now - suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode - suspend fun cancel(reason: RendezvousFailureReason) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt index a607dc7f38..18e625d825 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt @@ -16,16 +16,17 @@ package org.matrix.android.sdk.api.rendezvous -enum class RendezvousFailureReason(val value: String, val canRetry: Boolean = true) { - UserDeclined("user_declined"), - OtherDeviceNotSignedIn("other_device_not_signed_in"), - OtherDeviceAlreadySignedIn("other_device_already_signed_in"), - Unknown("unknown"), - Expired("expired"), - UserCancelled("user_cancelled"), - InvalidCode("invalid_code"), - UnsupportedAlgorithm("unsupported_algorithm", false), - DataMismatch("data_mismatch"), - UnsupportedTransport("unsupported_transport", false), - HomeserverLacksSupport("homeserver_lacks_support", false) +enum class RendezvousFailureReason(val canRetry: Boolean = true) { + UserDeclined, + OtherDeviceNotSignedIn, + OtherDeviceAlreadySignedIn, + Unknown, + Expired, + UserCancelled, + InvalidCode, + UnsupportedAlgorithm(false), + UnsupportedTransport(false), + UnsupportedHomeserver(false), + ProtocolError, + E2EESecurityIssue(false) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt index de0aed7efc..5daf906930 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt @@ -17,13 +17,16 @@ package org.matrix.android.sdk.api.rendezvous import okhttp3.MediaType +import org.matrix.android.sdk.api.rendezvous.model.RendezvousError import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails interface RendezvousTransport { var ready: Boolean - var onCancelled: ((reason: RendezvousFailureReason) -> Unit)? + @Throws(RendezvousError::class) suspend fun details(): RendezvousTransportDetails + @Throws(RendezvousError::class) suspend fun send(contentType: MediaType, data: ByteArray) + @Throws(RendezvousError::class) suspend fun receive(): ByteArray? - suspend fun cancel(reason: RendezvousFailureReason) + suspend fun close() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt index ca7083b297..9a5c5e865a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -89,13 +89,14 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu ourPublicKey = Base64.decode(olmSAS!!.publicKey, Base64.NO_WRAP) } + @Throws(RendezvousError::class) override suspend fun connect(): String { olmSAS ?.let { olmSAS -> val isInitiator = theirPublicKey == null if (isInitiator) { Timber.tag(TAG).i("Waiting for other device to send their public key") - val res = this.receiveAsPayload() ?: throw RuntimeException("No reply from other device") + val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError) if (res.key == null) { throw RendezvousError( @@ -137,7 +138,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu override suspend fun send(data: ByteArray) { if (aesKey == null) { - throw RuntimeException("Shared secret not established") + throw IllegalStateException("Shared secret not established") } send(encrypt(data)) } @@ -150,31 +151,12 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu override suspend fun receive(): ByteArray? { if (aesKey == null) { - throw RuntimeException("Shared secret not established") + throw IllegalStateException("Shared secret not established") } val payload = receiveAsPayload() ?: return null return decrypt(payload) } - override suspend fun generateCode(intent: RendezvousIntent): ECDHRendezvousCode { - return ECDHRendezvousCode( - intent, - rendezvous = ECDHRendezvous( - transport.details() as SimpleHttpRendezvousTransportDetails, - SecureRendezvousChannelAlgorithm.ECDH_V1, - key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) - ) - ) - } - - override suspend fun cancel(reason: RendezvousFailureReason) { - try { - transport.cancel(reason) - } finally { - close() - } - } - override suspend fun close() { olmSAS ?.let { synchronized(it) { @@ -183,6 +165,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu olmSAS = null } } + transport.close() } private fun encrypt(plainText: ByteArray): ECDHPayload { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt index 2dd6e7be28..2ef24e9cb7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt @@ -26,5 +26,11 @@ enum class Outcome(val value: String) { DECLINED("declined"), @Json(name = "unsupported") - UNSUPPORTED("unsupported") + UNSUPPORTED("unsupported"), + + @Json(name = "verified") + VERIFIED("verified"), + + @Json(name = "e2ee_security_error") + E2EE_SECURITY_ERROR("e2ee_security_error") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt index fec55ffb67..c52b11a322 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt @@ -18,4 +18,4 @@ package org.matrix.android.sdk.api.rendezvous.model import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason -class RendezvousError(val description: String, val reason: RendezvousFailureReason) : RuntimeException(description) +class RendezvousError(val description: String, val reason: RendezvousFailureReason) : Exception(description) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index ca2a3425cd..fa0e5d8e2a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -23,6 +23,7 @@ import okhttp3.RequestBody.Companion.toRequestBody import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason import org.matrix.android.sdk.api.rendezvous.RendezvousTransport +import org.matrix.android.sdk.api.rendezvous.model.RendezvousError import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails import timber.log.Timber @@ -32,7 +33,7 @@ import java.util.Date /** * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886 */ -class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: RendezvousFailureReason) -> Unit)?, rendezvousUri: String?) : RendezvousTransport { +class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTransport { companion object { private val TAG = LoggerTag(SimpleHttpRendezvousTransport::class.java.simpleName, LoggerTag.RENDEZVOUS).value } @@ -55,7 +56,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo override suspend fun send(contentType: MediaType, data: ByteArray) { if (cancelled) { - return + throw IllegalStateException("Rendezvous cancelled") } val method = if (uri != null) "PUT" else "POST" @@ -75,9 +76,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo val response = httpClient.newCall(request.build()).execute() if (response.code == 404) { - // we set to unknown and the cancel method will rewrite the reason to expired if applicable - cancel(RendezvousFailureReason.Unknown) - return + throw get404Error() } etag = response.header("etag") @@ -98,12 +97,12 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo } override suspend fun receive(): ByteArray? { + if (cancelled) { + throw IllegalStateException("Rendezvous cancelled") + } val uri = uri ?: throw IllegalStateException("Rendezvous not set up") val httpClient = okhttp3.OkHttpClient.Builder().build() while (true) { - if (cancelled) { - return null - } Timber.tag(TAG).i("Polling: $uri after etag $etag") val request = Request.Builder() .url(uri) @@ -118,9 +117,7 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo try { // expired if (response.code == 404) { - // we set to unknown and the cancel method will rewrite the reason to expired if applicable - cancel(RendezvousFailureReason.Unknown) - return null + throw get404Error() } // rely on server expiring the channel rather than checking ourselves @@ -145,31 +142,27 @@ class SimpleHttpRendezvousTransport(override var onCancelled: ((reason: Rendezvo } } - override suspend fun cancel(reason: RendezvousFailureReason) { - var mappedReason = reason - Timber.tag(TAG).i("$expiresAt") - if (mappedReason == RendezvousFailureReason.Unknown && - expiresAt != null && Date() > expiresAt - ) { - mappedReason = RendezvousFailureReason.Expired - } + private fun get404Error(): RendezvousError { + return if (expiresAt != null && Date() > expiresAt) + RendezvousError("Expired", RendezvousFailureReason.Expired) + else + RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown) + } + override suspend fun close() { cancelled = true ready = false - onCancelled ?.let { it(mappedReason) } - if (mappedReason == RendezvousFailureReason.UserDeclined) { - uri ?.let { - try { - val httpClient = okhttp3.OkHttpClient.Builder().build() - val request = Request.Builder() - .url(it) - .delete() - .build() - httpClient.newCall(request).execute() - } catch (e: Exception) { - Timber.tag(TAG).w(e, "Failed to delete channel") - } + uri ?.let { + try { + val httpClient = okhttp3.OkHttpClient.Builder().build() + val request = Request.Builder() + .url(it) + .delete() + .build() + httpClient.newCall(request).execute() + } catch (e: Throwable) { + Timber.tag(TAG).w(e, "Failed to delete channel") } } } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt index 0691e2367e..617d620c27 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -70,8 +70,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment getString(R.string.qr_code_login_header_failed_device_is_not_supported_description) + RendezvousFailureReason.UnsupportedHomeserver -> getString(R.string.qr_code_login_header_failed_homeserver_is_not_supported_description) RendezvousFailureReason.Expired -> getString(R.string.qr_code_login_header_failed_timeout_description) RendezvousFailureReason.UserDeclined -> getString(R.string.qr_code_login_header_failed_denied_description) + RendezvousFailureReason.E2EESecurityIssue -> getString(R.string.qr_code_login_header_failed_e2ee_security_issue_description) + RendezvousFailureReason.OtherDeviceAlreadySignedIn -> getString(R.string.qr_code_login_header_failed_other_device_already_signed_in_description) + RendezvousFailureReason.OtherDeviceNotSignedIn -> getString(R.string.qr_code_login_header_failed_other_device_not_signed_in_description) + RendezvousFailureReason.InvalidCode -> getString(R.string.qr_code_login_header_failed_invalid_qr_code_description) + RendezvousFailureReason.UserCancelled -> getString(R.string.qr_code_login_header_failed_user_cancelled_description) else -> getString(R.string.qr_code_login_header_failed_other_description) } } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index 9d4e5e9870..7f95fad485 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.rendezvous.Rendezvous import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason +import org.matrix.android.sdk.api.rendezvous.model.RendezvousError import timber.log.Timber class QrCodeLoginViewModel @AssistedInject constructor( @@ -38,14 +39,15 @@ class QrCodeLoginViewModel @AssistedInject constructor( private val activeSessionHolder: ActiveSessionHolder, private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase ) : VectorViewModel(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 785ce1fed7..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/EmbeddedRendezvous.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2022 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.matrix.android.sdk.api.rendezvous.model - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -open class EmbeddedRendezvous( - @Json(name = "transport") val transport: RendezvousTransportDetails, - @Json(name = "algorithm") val algorithm: SecureRendezvousChannelAlgorithm -) From 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 2ef24e9cb7..a0b2cb31b4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt @@ -17,7 +17,9 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) enum class Outcome(val value: String) { @Json(name = "success") SUCCESS("success"), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt index 5ff4cd7cfa..9e0dbd9562 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt @@ -17,7 +17,9 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) internal enum class PayloadType(val value: String) { @Json(name = "m.login.start") START("m.login.start"), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt index 18381984a5..3442d4abf4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt @@ -17,7 +17,9 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) enum class Protocol(val value: String) { @Json(name = "login_token") LOGIN_TOKEN("login_token") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt index 1c070599b0..8ce6430212 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt @@ -17,7 +17,9 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) enum class RendezvousIntent { @Json(name = "login.start") LOGIN_ON_NEW_DEVICE, @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt index 9c3e44f25b..2814cad77c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt @@ -17,7 +17,9 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) enum class RendezvousTransportType(val value: String) { @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt index 9a9db58a41..d9f73a0b99 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt @@ -17,7 +17,9 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +@JsonClass(generateAdapter = true) enum class SecureRendezvousChannelAlgorithm(val value: String) { @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256") } From 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 9a5c5e865a..2f368d6520 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -24,12 +24,8 @@ import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.rendezvous.RendezvousChannel import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason import org.matrix.android.sdk.api.rendezvous.RendezvousTransport -import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvous -import org.matrix.android.sdk.api.rendezvous.model.ECDHRendezvousCode import org.matrix.android.sdk.api.rendezvous.model.RendezvousError -import org.matrix.android.sdk.api.rendezvous.model.RendezvousIntent import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm -import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransportDetails import org.matrix.android.sdk.api.util.MatrixJsonParser import org.matrix.android.sdk.internal.extensions.toUnsignedInt import org.matrix.olm.OlmSAS diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index fa0e5d8e2a..50cebae12d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -143,10 +143,11 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor } private fun get404Error(): RendezvousError { - return if (expiresAt != null && Date() > expiresAt) - RendezvousError("Expired", RendezvousFailureReason.Expired) - else - RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown) + if (expiresAt != null && Date() > expiresAt) { + return RendezvousError("Expired", RendezvousFailureReason.Expired) + } + + return RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown) } override suspend fun close() { From 3d37e0b2a557cf121efe01935a5a2980c9177ff2 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 a0b2cb31b4..0ebd1f88b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -@JsonClass(generateAdapter = true) +@JsonClass(generateAdapter = false) enum class Outcome(val value: String) { @Json(name = "success") SUCCESS("success"), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt index 9e0dbd9562..33beb1f525 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -@JsonClass(generateAdapter = true) +@JsonClass(generateAdapter = false) internal enum class PayloadType(val value: String) { @Json(name = "m.login.start") START("m.login.start"), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt index 3442d4abf4..7c020da641 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -@JsonClass(generateAdapter = true) +@JsonClass(generateAdapter = false) enum class Protocol(val value: String) { @Json(name = "login_token") LOGIN_TOKEN("login_token") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt index 8ce6430212..65037e1252 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -@JsonClass(generateAdapter = true) +@JsonClass(generateAdapter = false) enum class RendezvousIntent { @Json(name = "login.start") LOGIN_ON_NEW_DEVICE, @Json(name = "login.reciprocate") RECIPROCATE_LOGIN_ON_EXISTING_DEVICE diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt index 2814cad77c..aa578e3660 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -@JsonClass(generateAdapter = true) +@JsonClass(generateAdapter = false) enum class RendezvousTransportType(val value: String) { @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1") } From 552fb9de9a30d8295256329f0c63fc525d1031cb Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 7f95fad485..69deb44c70 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -147,9 +147,10 @@ class QrCodeLoginViewModel @AssistedInject constructor( } /** - * TODO. UI test purpose. Fixme accordingly. + * QR code generation is not currently supported and this is a placeholder for future + * functionality. */ private fun generateQrCodeData(): String { - return "TODO" + return "NOT SUPPORTED" } } From b2dc0b33b5fb07bf47a5cc697589479d138e5515 Mon Sep 17 00:00:00 2001 From: Onuray Sahin 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 | 44 +++++++++++-------- .../login/qr/QrCodeLoginStatusFragment.kt | 7 +++ .../login/qr/QrCodeLoginViewEvents.kt | 1 + .../features/login/qr/QrCodeLoginViewModel.kt | 5 +++ 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt index 2c3cb440b6..4b56d8e756 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -20,6 +20,7 @@ import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.MatrixPatterns.getServerName import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.wellknown.WellknownResult +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.internal.auth.version.Versions diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt index 8854d0720f..5ea46d3dcd 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt @@ -22,4 +22,5 @@ sealed class QrCodeLoginAction : VectorViewModelAction { data class OnQrCodeScanned(val qrCode: String) : QrCodeLoginAction() object GenerateQrCode : QrCodeLoginAction() object ShowQrCode : QrCodeLoginAction() + object TryAgain : QrCodeLoginAction() } diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt index 2f30261890..c9b8ae0080 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt @@ -38,30 +38,33 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { super.onCreate(savedInstanceState) views.toolbar.visibility = View.GONE - val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) if (isFirstCreation()) { - when (qrCodeLoginArgs?.loginType) { - QrCodeLoginType.LOGIN -> { - showInstructionsFragment(qrCodeLoginArgs) - } - QrCodeLoginType.LINK_A_DEVICE -> { - if (qrCodeLoginArgs.showQrCodeImmediately) { - handleNavigateToShowQrCodeScreen() - } else { - showInstructionsFragment(qrCodeLoginArgs) - } - } - null -> { - Timber.i("QrCodeLoginArgs is null. This is not expected.") - finish() - return - } - } + navigateToInitialFragment() } observeViewEvents() } + private fun navigateToInitialFragment() { + val qrCodeLoginArgs: QrCodeLoginArgs? = intent?.extras?.getParcelableCompat(Mavericks.KEY_ARG) + when (qrCodeLoginArgs?.loginType) { + QrCodeLoginType.LOGIN -> { + showInstructionsFragment(qrCodeLoginArgs) + } + QrCodeLoginType.LINK_A_DEVICE -> { + if (qrCodeLoginArgs.showQrCodeImmediately) { + handleNavigateToShowQrCodeScreen() + } else { + showInstructionsFragment(qrCodeLoginArgs) + } + } + null -> { + Timber.i("QrCodeLoginArgs is null. This is not expected.") + finish() + } + } + } + private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) { addFragment( views.container, @@ -77,10 +80,15 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen() QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen() QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen() + QrCodeLoginViewEvents.NavigateToInitialScreen -> handleNavigateToInitialScreen() } } } + private fun handleNavigateToInitialScreen() { + navigateToInitialFragment() + } + private fun handleNavigateToShowQrCodeScreen() { addFragment( views.container, diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt index 617d620c27..097c956f2c 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -42,6 +42,13 @@ class QrCodeLoginStatusFragment : VectorBaseFragment 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 7c020da641..6fce2fa11c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt @@ -21,6 +21,6 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = false) enum class Protocol(val value: String) { - @Json(name = "login_token") - LOGIN_TOKEN("login_token") + @Json(name = "org.matrix.msc3906.login_token") + LOGIN_TOKEN("org.matrix.msc3906.login_token") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt index aa578e3660..6fca7efa71 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt @@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = false) enum class RendezvousTransportType(val value: String) { - @Json(name = "http.v1") MSC3886_SIMPLE_HTTP_V1("http.v1") + @Json(name = "org.matrix.msc3886.http.v1") + MSC3886_SIMPLE_HTTP_V1("org.matrix.msc3886.http.v1") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt index d9f73a0b99..ab58179f5a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt @@ -21,5 +21,6 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) enum class SecureRendezvousChannelAlgorithm(val value: String) { - @Json(name = "m.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("m.rendezvous.v1.curve25519-aes-sha256") + @Json(name = "org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256") + ECDH_V1("org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256") } From 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 8c14ca892a..773e870ffd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt @@ -65,6 +65,11 @@ data class HomeServerCapabilities( * True if the home server supports login via qr code, false otherwise. */ val canLoginWithQrCode: Boolean = false, + + /** + * True if the home server supports threaded read receipts and unread notifications. + */ + val canUseThreadReadReceiptsAndNotifications: Boolean = false, ) { enum class RoomCapabilitySupport { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt index 75639c6a21..d443d6e3c8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt @@ -60,5 +60,6 @@ internal data class HomeServerVersion( val r0_6_0 = HomeServerVersion(major = 0, minor = 6, patch = 0) val r0_6_1 = HomeServerVersion(major = 0, minor = 6, patch = 1) val v1_3_0 = HomeServerVersion(major = 1, minor = 3, patch = 0) + val v1_4_0 = HomeServerVersion(major = 1, minor = 4, patch = 0) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt index 5e133fab9c..9ed8901683 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 9a2c32f97c..def0f6de7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -56,6 +56,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo039 +import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo040 import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import javax.inject.Inject @@ -64,7 +65,7 @@ internal class RealmSessionStoreMigration @Inject constructor( private val normalizer: Normalizer ) : MatrixRealmMigration( dbName = "Session", - schemaVersion = 39L, + schemaVersion = 40L, ) { /** * Forces all RealmSessionStoreMigration instances to be equal. @@ -113,5 +114,6 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldVersion < 37) MigrateSessionTo037(realm).perform() if (oldVersion < 38) MigrateSessionTo038(realm).perform() if (oldVersion < 39) MigrateSessionTo039(realm).perform() + if (oldVersion < 40) MigrateSessionTo040(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt index 63fa101c45..3528ca0051 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt @@ -45,6 +45,7 @@ internal object HomeServerCapabilitiesMapper { canUseThreading = entity.canUseThreading, canControlLogoutDevices = entity.canControlLogoutDevices, canLoginWithQrCode = entity.canLoginWithQrCode, + canUseThreadReadReceiptsAndNotifications = entity.canUseThreadReadReceiptsAndNotifications ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt new file mode 100644 index 0000000000..b3e02342dd --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.database.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields +import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +internal class MigrateSessionTo040(realm: DynamicRealm) : RealmMigrator(realm, 40) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("HomeServerCapabilitiesEntity") + ?.addField(HomeServerCapabilitiesEntityFields.CAN_USE_THREAD_READ_RECEIPTS_AND_NOTIFICATIONS, Boolean::class.java) + ?.transform { obj -> + obj.set(HomeServerCapabilitiesEntityFields.CAN_USE_THREAD_READ_RECEIPTS_AND_NOTIFICATIONS, false) + } + ?.forceRefreshOfHomeServerCapabilities() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt index cfa02b2c74..89f1e50b30 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt @@ -32,6 +32,7 @@ internal open class HomeServerCapabilitiesEntity( var canUseThreading: Boolean = false, var canControlLogoutDevices: Boolean = false, var canLoginWithQrCode: Boolean = false, + var canUseThreadReadReceiptsAndNotifications: Boolean = false, ) : RealmObject() { companion object diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt index 2c3cb440b6..a5953d870c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin +import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreadUnreadNotifications import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity @@ -144,6 +145,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( homeServerCapabilitiesEntity.canControlLogoutDevices = getVersionResult.doesServerSupportLogoutDevices() homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */ getVersionResult.doesServerSupportThreads() + homeServerCapabilitiesEntity.canUseThreadReadReceiptsAndNotifications = + getVersionResult.doesServerSupportThreadUnreadNotifications() homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult.doesServerSupportQrCodeLogin() } From 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 ab58179f5a..75f0024fda 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.rendezvous.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -@JsonClass(generateAdapter = true) +@JsonClass(generateAdapter = false) enum class SecureRendezvousChannelAlgorithm(val value: String) { @Json(name = "org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256") ECDH_V1("org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256") From 41dbdbcd7bc5c492620612f4e7f3bcd05c89e7e1 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 ed2fb6e0f5..2647c5974d 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -37,7 +37,7 @@ class QrCodeLoginViewModel @AssistedInject constructor( @Assisted private val initialState: QrCodeLoginViewState, private val authenticationService: AuthenticationService, private val activeSessionHolder: ActiveSessionHolder, - private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase + private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase, ) : VectorViewModel(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 097c956f2c..6ef261e6d9 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt @@ -66,14 +66,14 @@ class QrCodeLoginStatusFragment : VectorBaseFragment 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 4b56d8e756..2c3cb440b6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -20,7 +20,6 @@ import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.MatrixPatterns.getServerName import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.wellknown.WellknownResult -import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.internal.auth.version.Versions From 6d17d51fe9e7a5483288f833106f4e572fafcb46 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 9ad889fca0..e41a72db94 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -89,7 +89,7 @@ class Rendezvous( } @Throws(RendezvousError::class) - suspend fun startAfterScanningCode(): String? { + suspend fun startAfterScanningCode(): String { val checksum = channel.connect() Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum") @@ -111,7 +111,7 @@ class Rendezvous( } @Throws(RendezvousError::class) - suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session? { + suspend fun waitForLoginOnNewDevice(authenticationService: AuthenticationService): Session { Timber.tag(TAG).i("Waiting for login_token") val loginToken = receive() diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index 2647c5974d..4134dc8ab2 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -100,21 +100,19 @@ class QrCodeLoginViewModel @AssistedInject constructor( try { val confirmationCode = rendezvous.startAfterScanningCode() Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode") - confirmationCode?.let { - onConnectionEstablished(it) - val session = rendezvous.waitForLoginOnNewDevice(authenticationService) - onSigningIn() - session?.let { - activeSessionHolder.setActiveSession(session) - authenticationService.reset() - configureAndStartSessionUseCase.execute(session) + onConnectionEstablished(confirmationCode) - rendezvous.completeVerificationOnNewDevice(session) + val session = rendezvous.waitForLoginOnNewDevice(authenticationService) + onSigningIn() - _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen) - } - } + activeSessionHolder.setActiveSession(session) + authenticationService.reset() + configureAndStartSessionUseCase.execute(session) + + rendezvous.completeVerificationOnNewDevice(session) + + _viewEvents.post(QrCodeLoginViewEvents.NavigateToHomeScreen) } catch (t: Throwable) { Timber.tag(TAG).e(t, "Error occurred during sign in") if (t is RendezvousError) { From 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 e28ebb31c6..7fe8dd8d08 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3386,12 +3386,12 @@ 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 bf948064ed..628e12593c 100644 --- a/.github/workflows/post-pr.yml +++ b/.github/workflows/post-pr.yml @@ -52,7 +52,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: Start synapse server - uses: michaelkaye/setup-matrix-synapse@v1.0.3 + uses: michaelkaye/setup-matrix-synapse@v1.0.4 with: uploadLogs: true httpPort: 8080 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1816fe3a78..bb16d8abe8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,7 +50,7 @@ jobs: - uses: actions/setup-python@v4 with: python-version: 3.8 - - uses: michaelkaye/setup-matrix-synapse@v1.0.3 + - uses: michaelkaye/setup-matrix-synapse@v1.0.4 with: uploadLogs: true httpPort: 8080 From 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 efd23f2530..40fcbbbb85 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt @@ -63,6 +63,7 @@ class QrCodeLoginInstructionsFragment : VectorBaseFragment 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 4134dc8ab2..1a8ed945c0 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -125,6 +125,8 @@ class QrCodeLoginViewModel @AssistedInject constructor( } private fun onFailed(reason: RendezvousFailureReason) { + _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen) + setState { copy( connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry) From 8a62dfb34aca1c3ab52d05d240ea74cc2b5a6d54 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 5daf906930..81632e951a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt @@ -22,11 +22,15 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousTransportDetails interface RendezvousTransport { var ready: Boolean + @Throws(RendezvousError::class) suspend fun details(): RendezvousTransportDetails + @Throws(RendezvousError::class) suspend fun send(contentType: MediaType, data: ByteArray) + @Throws(RendezvousError::class) suspend fun receive(): ByteArray? + suspend fun close() } From afa55649c53e31511758cd09030d98b92869f540 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 00:52:02 +0000 Subject: [PATCH 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 ca77e4b86f..66fdf3fbf4 100644 --- a/vector-app/build.gradle +++ b/vector-app/build.gradle @@ -372,7 +372,7 @@ dependencies { debugImplementation 'com.facebook.soloader:soloader:0.10.4' debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0" - gplayImplementation "com.google.android.gms:play-services-location:20.0.0" + gplayImplementation "com.google.android.gms:play-services-location:21.0.0" // UnifiedPush gplay flavor only gplayImplementation('com.google.firebase:firebase-messaging:23.1.0') { exclude group: 'com.google.firebase', module: 'firebase-core' From 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 2f368d6520..19c46db34d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -19,6 +19,8 @@ package org.matrix.android.sdk.api.rendezvous.channels import android.util.Base64 import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import okhttp3.MediaType.Companion.toMediaType import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.rendezvous.RendezvousChannel @@ -71,6 +73,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu @Json val iv: String? = null ) + private var olmSASMutex = Mutex() private var olmSAS: OlmSAS? private val ourPublicKey: ByteArray private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java) @@ -87,45 +90,44 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu @Throws(RendezvousError::class) override suspend fun connect(): String { - olmSAS ?.let { olmSAS -> - val isInitiator = theirPublicKey == null + val sas = olmSAS ?: throw RendezvousError("Channel closed", RendezvousFailureReason.Unknown) + val isInitiator = theirPublicKey == null - if (isInitiator) { - Timber.tag(TAG).i("Waiting for other device to send their public key") - val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError) + if (isInitiator) { + Timber.tag(TAG).i("Waiting for other device to send their public key") + val res = this.receiveAsPayload() ?: throw RendezvousError("No reply from other device", RendezvousFailureReason.ProtocolError) - if (res.key == null) { - throw RendezvousError( - "Unsupported algorithm: ${res.algorithm}", - RendezvousFailureReason.UnsupportedAlgorithm, - ) - } - theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) - } else { - // send our public key unencrypted - Timber.tag(TAG).i("Sending public key") - send( - ECDHPayload( - algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, - key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) - ) + if (res.key == null) { + throw RendezvousError( + "Unsupported algorithm: ${res.algorithm}", + RendezvousFailureReason.UnsupportedAlgorithm, ) } + theirPublicKey = Base64.decode(res.key, Base64.NO_WRAP) + } else { + // send our public key unencrypted + Timber.tag(TAG).i("Sending public key") + send( + ECDHPayload( + algorithm = SecureRendezvousChannelAlgorithm.ECDH_V1, + key = Base64.encodeToString(ourPublicKey, Base64.NO_WRAP) + ) + ) + } - synchronized(olmSAS) { - olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) - olmSAS.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) + olmSASMutex.withLock { + sas.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) + sas.setTheirPublicKey(Base64.encodeToString(theirPublicKey, Base64.NO_WRAP)) - val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP) - val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP) - val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey" + val initiatorKey = Base64.encodeToString(if (isInitiator) ourPublicKey else theirPublicKey, Base64.NO_WRAP) + val recipientKey = Base64.encodeToString(if (isInitiator) theirPublicKey else ourPublicKey, Base64.NO_WRAP) + val aesInfo = "${SecureRendezvousChannelAlgorithm.ECDH_V1.value}|$initiatorKey|$recipientKey" - aesKey = olmSAS.generateShortCode(aesInfo, 32) + aesKey = sas.generateShortCode(aesInfo, 32) - val rawChecksum = olmSAS.generateShortCode(aesInfo, 5) - return getDecimalCodeRepresentation(rawChecksum) - } - } ?: throw RuntimeException("Channel closed") + val rawChecksum = sas.generateShortCode(aesInfo, 5) + return getDecimalCodeRepresentation(rawChecksum) + } } private suspend fun send(payload: ECDHPayload) { @@ -154,12 +156,11 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu } override suspend fun close() { - olmSAS ?.let { - synchronized(it) { - // this does a double release check already so we don't re-check ourselves - it.releaseSas() - olmSAS = null - } + val sas = olmSAS ?: throw IllegalStateException("Channel already closed") + olmSASMutex.withLock { + // this does a double release check already so we don't re-check ourselves + sas.releaseSas() + olmSAS = null } transport.close() } From 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 | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt index e41a72db94..f724ac4b62 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt @@ -165,21 +165,25 @@ class Rendezvous( } verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice -> - // check master key againt what the homeserver told us - crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey()?.let { localMasterKey -> - if (localMasterKey.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) { - Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey") - // inform the other side - send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR)) - throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue) - } - // set other device as verified - Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) + // verifying device provided us with a master key, so use it to check integrity - Timber.tag(TAG).i("Setting master key as trusted") - crypto.crossSigningService().markMyMasterKeyAsTrusted() - } ?: Timber.tag(TAG).w("No local master key so not verifying") + // see what the homeserver told us + val localMasterKey = crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey() + + // n.b. if no local master key this is a problem, as well as it not matching + if (localMasterKey?.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) { + Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey") + // inform the other side + send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR)) + throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue) + } + + // set other device as verified + Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") + crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) + + Timber.tag(TAG).i("Setting master key as trusted") + crypto.crossSigningService().markMyMasterKeyAsTrusted() } ?: run { // set other device as verified anyway Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") From 57a8dd4a1f61b50e180830d96264d5c3b1f1fe72 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 19c46db34d..f7da0efa92 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -81,7 +81,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu private var aesKey: ByteArray? = null init { - theirPublicKeyBase64 ?.let { + theirPublicKeyBase64?.let { theirPublicKey = Base64.decode(it, Base64.NO_WRAP) } olmSAS = OlmSAS() @@ -142,7 +142,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu } private suspend fun receiveAsPayload(): ECDHPayload? { - transport.receive()?.toString(Charsets.UTF_8) ?.let { + transport.receive()?.toString(Charsets.UTF_8)?.let { return ecdhAdapter.fromJson(it) } ?: return null } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index 50cebae12d..3f7cfe786e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -69,7 +69,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor .method(method, data.toRequestBody()) .header("content-type", contentType.toString()) - etag ?.let { + etag?.let { request.header("if-match", it) } @@ -85,7 +85,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor if (method == "POST") { val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response") - response.header("expires") ?.let { + response.header("expires")?.let { val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz") expiresAt = format.parse(it) } @@ -108,7 +108,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor .url(uri) .get() - etag ?.let { + etag?.let { request.header("if-none-match", it) } @@ -154,7 +154,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor cancelled = true ready = false - uri ?.let { + uri?.let { try { val httpClient = okhttp3.OkHttpClient.Builder().build() val request = Request.Builder() From 376cd1cb367c8fa3d3b9f9f2da898a56e0f3636e Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 3f7cfe786e..7a5cbe5424 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -54,6 +54,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor return SimpleHttpRendezvousTransportDetails(uri) } + @Throws(RendezvousError::class) override suspend fun send(contentType: MediaType, data: ByteArray) { if (cancelled) { throw IllegalStateException("Rendezvous cancelled") @@ -96,6 +97,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor } } + @Throws(RendezvousError::class) override suspend fun receive(): ByteArray? { if (cancelled) { throw IllegalStateException("Rendezvous cancelled") From 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 9ed8901683..1245d8df4b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt @@ -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 7fe8dd8d08..7b6b9b914d 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3383,7 +3383,7 @@ 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 be79569164..9976c7a8e4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt @@ -45,7 +45,7 @@ interface RendezvousChannel { suspend fun receive(): ByteArray? /** - * @returns closes the channel and cleans up + * Closes the channel and cleans up. */ suspend fun close() } From 8530f8f28049dae1be38c2cf0503e8cfac04f2f6 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 f7da0efa92..b21d89059b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -73,7 +73,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu @Json val iv: String? = null ) - private var olmSASMutex = Mutex() + private val olmSASMutex = Mutex() private var olmSAS: OlmSAS? private val ourPublicKey: ByteArray private val ecdhAdapter = MatrixJsonParser.getMoshi().adapter(ECDHPayload::class.java) From a83fb8bf83c10759c1e25e126e1fae5319cc4dad Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 9976c7a8e4..0956a5b0a0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.rendezvous.model.RendezvousError * Representation of a rendezvous channel such as that described by MSC3903. */ interface RendezvousChannel { - var transport: RendezvousTransport + val transport: RendezvousTransport /** * @returns the checksum/confirmation digits to be shown to the user From 916ae654e790bc001a1f5f86ef30d32657da952d Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 1a8ed945c0..9bf3e955d3 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -76,7 +76,7 @@ class QrCodeLoginViewModel @AssistedInject constructor( } private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) { - Timber.tag(TAG).d("Scanned code: ${action.qrCode}") + Timber.tag(TAG).d("Scanned code of length ${action.qrCode.length}") val rendezvous = try { Rendezvous.buildChannelFromCode(action.qrCode) } catch (t: Throwable) { Timber.tag(TAG).e(t, "Error occurred during sign in") From 811d6d87ae50595726494abe772a8931d27da0c2 Mon Sep 17 00:00:00 2001 From: Hugh Nimmo-Smith 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 b21d89059b..6c45aacfaa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.RendezvousTransport import org.matrix.android.sdk.api.rendezvous.model.RendezvousError import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm import org.matrix.android.sdk.api.util.MatrixJsonParser -import org.matrix.android.sdk.internal.extensions.toUnsignedInt +import org.matrix.android.sdk.internal.crypto.verification.SASDefaultVerificationTransaction import org.matrix.olm.OlmSAS import timber.log.Timber import java.security.SecureRandom @@ -48,20 +48,9 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu private const val KEY_SPEC = "AES" private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value - // n.b. we are only aver processing byte array that we have generated, so we can make assumptions about the length + // this is the same representation as for SAS but we delimit by dashes instead of spaces for readability private fun getDecimalCodeRepresentation(byteArray: ByteArray): String { - val b0 = byteArray[0].toUnsignedInt() // need unsigned byte - val b1 = byteArray[1].toUnsignedInt() // need unsigned byte - val b2 = byteArray[2].toUnsignedInt() // need unsigned byte - val b3 = byteArray[3].toUnsignedInt() // need unsigned byte - val b4 = byteArray[4].toUnsignedInt() // need unsigned byte - // (B0 << 5 | B1 >> 3) + 1000 - val first = (b0.shl(5) or b1.shr(3)) + 1000 - // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 - val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 - // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 - val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 - return "$first-$second-$third" + return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(byteArray).replace(" ", "-") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt index 1cbaff059a..b306288c5e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt @@ -82,6 +82,33 @@ internal abstract class SASDefaultVerificationTransaction( // older devices have limited support of emoji but SDK offers images for the 64 verification emojis // so always send that we support EMOJI val KNOWN_SHORT_CODES = listOf(SasMode.EMOJI, SasMode.DECIMAL) + + /** + * decimal: generate five bytes by using HKDF. + * Take the first 13 bits and convert it to a decimal number (which will be a number between 0 and 8191 inclusive), + * and add 1000 (resulting in a number between 1000 and 9191 inclusive). + * Do the same with the second 13 bits, and the third 13 bits, giving three 4-digit numbers. + * In other words, if the five bytes are B0, B1, B2, B3, and B4, then the first number is (B0 << 5 | B1 >> 3) + 1000, + * the second number is ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000, and the third number is ((B3 & 0x3f) << 7 | B4 >> 1) + 1000. + * (This method of converting 13 bits at a time is used to avoid requiring 32-bit clients to do big-number arithmetic, + * and adding 1000 to the number avoids having clients to worry about properly zero-padding the number when displaying to the user.) + * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers, + * or with the three numbers on separate lines. + */ + fun getDecimalCodeRepresentation(byteArray: ByteArray): String { + val b0 = byteArray[0].toUnsignedInt() // need unsigned byte + val b1 = byteArray[1].toUnsignedInt() // need unsigned byte + val b2 = byteArray[2].toUnsignedInt() // need unsigned byte + val b3 = byteArray[3].toUnsignedInt() // need unsigned byte + val b4 = byteArray[4].toUnsignedInt() // need unsigned byte + // (B0 << 5 | B1 >> 3) + 1000 + val first = (b0.shl(5) or b1.shr(3)) + 1000 + // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 + val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 + // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 + val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 + return "$first $second $third" + } } override var state: VerificationTxState = VerificationTxState.None @@ -371,33 +398,6 @@ internal abstract class SASDefaultVerificationTransaction( return getDecimalCodeRepresentation(shortCodeBytes!!) } - /** - * decimal: generate five bytes by using HKDF. - * Take the first 13 bits and convert it to a decimal number (which will be a number between 0 and 8191 inclusive), - * and add 1000 (resulting in a number between 1000 and 9191 inclusive). - * Do the same with the second 13 bits, and the third 13 bits, giving three 4-digit numbers. - * In other words, if the five bytes are B0, B1, B2, B3, and B4, then the first number is (B0 << 5 | B1 >> 3) + 1000, - * the second number is ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000, and the third number is ((B3 & 0x3f) << 7 | B4 >> 1) + 1000. - * (This method of converting 13 bits at a time is used to avoid requiring 32-bit clients to do big-number arithmetic, - * and adding 1000 to the number avoids having clients to worry about properly zero-padding the number when displaying to the user.) - * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers, - * or with the three numbers on separate lines. - */ - fun getDecimalCodeRepresentation(byteArray: ByteArray): String { - val b0 = byteArray[0].toUnsignedInt() // need unsigned byte - val b1 = byteArray[1].toUnsignedInt() // need unsigned byte - val b2 = byteArray[2].toUnsignedInt() // need unsigned byte - val b3 = byteArray[3].toUnsignedInt() // need unsigned byte - val b4 = byteArray[4].toUnsignedInt() // need unsigned byte - // (B0 << 5 | B1 >> 3) + 1000 - val first = (b0.shl(5) or b1.shr(3)) + 1000 - // ((B1 & 0x7) << 10 | B2 << 2 | B3 >> 6) + 1000 - val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 - // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 - val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 - return "$first $second $third" - } - override fun getEmojiCodeRepresentation(): List { 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 6c45aacfaa..4bbf34280c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -56,10 +56,10 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu @JsonClass(generateAdapter = true) internal data class ECDHPayload( - @Json val algorithm: SecureRendezvousChannelAlgorithm? = null, - @Json val key: String? = null, - @Json val ciphertext: String? = null, - @Json val iv: String? = null + val algorithm: SecureRendezvousChannelAlgorithm? = null, + val key: String? = null, + val ciphertext: String? = null, + val iv: String? = null ) private val olmSASMutex = Mutex() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt index 0840e1ca2e..55bac6397e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt @@ -16,12 +16,11 @@ package org.matrix.android.sdk.api.rendezvous.model -import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class ECDHRendezvous( - @Json val transport: SimpleHttpRendezvousTransportDetails, - @Json val algorithm: SecureRendezvousChannelAlgorithm, - @Json val key: String + val transport: SimpleHttpRendezvousTransportDetails, + val algorithm: SecureRendezvousChannelAlgorithm, + val key: String ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt index 410c5c1036..575b5d4bfd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt @@ -16,11 +16,10 @@ package org.matrix.android.sdk.api.rendezvous.model -import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class ECDHRendezvousCode( - @Json val intent: RendezvousIntent, - @Json val rendezvous: ECDHRendezvous + val intent: RendezvousIntent, + val rendezvous: ECDHRendezvous ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt index 593177e625..04631ce959 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt @@ -21,12 +21,12 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) internal data class Payload( - @Json val type: PayloadType, - @Json val intent: RendezvousIntent? = null, - @Json val outcome: Outcome? = null, - @Json val protocols: List? = 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 55b3bbb5d9..1bde43ab7e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt @@ -16,10 +16,9 @@ package org.matrix.android.sdk.api.rendezvous.model -import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) open class RendezvousTransportDetails( - @Json val type: RendezvousTransportType + val type: RendezvousTransportType ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt index 70a441d760..049aa8b756 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt @@ -16,10 +16,9 @@ package org.matrix.android.sdk.api.rendezvous.model -import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class SimpleHttpRendezvousTransportDetails( - @Json val uri: String + val uri: String ) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1) From 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 4bbf34280c..e844143889 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.api.rendezvous.channels import android.util.Base64 -import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock From 0c52a7ed040c5637d81c408e2ec41197de4b2563 Mon Sep 17 00:00:00 2001 From: Onuray Sahin 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 c9b8ae0080..a0c113224d 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt @@ -24,6 +24,7 @@ import com.airbnb.mvrx.Mavericks import com.airbnb.mvrx.viewModel import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.addFragment +import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.features.home.HomeActivity import im.vector.lib.core.utils.compat.getParcelableCompat @@ -66,7 +67,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { } private fun showInstructionsFragment(qrCodeLoginArgs: QrCodeLoginArgs) { - addFragment( + replaceFragment( views.container, QrCodeLoginInstructionsFragment::class.java, qrCodeLoginArgs, diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt index 9bf3e955d3..97cca9d791 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt @@ -59,6 +59,11 @@ class QrCodeLoginViewModel @AssistedInject constructor( } private fun handleTryAgain() { + setState { + copy( + connectionStatus = null + ) + } _viewEvents.post(QrCodeLoginViewEvents.NavigateToInitialScreen) } From 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 e844143889..c1d6b1b70e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt @@ -46,11 +46,6 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu private const val ALGORITHM_SPEC = "AES/GCM/NoPadding" private const val KEY_SPEC = "AES" private val TAG = LoggerTag(ECDHRendezvousChannel::class.java.simpleName, LoggerTag.RENDEZVOUS).value - - // this is the same representation as for SAS but we delimit by dashes instead of spaces for readability - private fun getDecimalCodeRepresentation(byteArray: ByteArray): String { - return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(byteArray).replace(" ", "-") - } } @JsonClass(generateAdapter = true) @@ -114,7 +109,7 @@ class ECDHRendezvousChannel(override var transport: RendezvousTransport, theirPu aesKey = sas.generateShortCode(aesInfo, 32) val rawChecksum = sas.generateShortCode(aesInfo, 5) - return getDecimalCodeRepresentation(rawChecksum) + return SASDefaultVerificationTransaction.getDecimalCodeRepresentation(rawChecksum, separator = "-") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt index 7a5cbe5424..620b599e3d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt @@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.rendezvous.model.SimpleHttpRendezvousTransport import timber.log.Timber import java.text.SimpleDateFormat import java.util.Date +import java.util.Locale /** * Implementation of the Simple HTTP transport MSC3886: https://github.com/matrix-org/matrix-spec-proposals/pull/3886 @@ -87,7 +88,7 @@ class SimpleHttpRendezvousTransport(rendezvousUri: String?) : RendezvousTranspor val location = response.header("location") ?: throw RuntimeException("No rendezvous URI found in response") response.header("expires")?.let { - val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz") + val format = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US) expiresAt = format.parse(it) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt index b306288c5e..29b416bb82 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt @@ -95,7 +95,7 @@ internal abstract class SASDefaultVerificationTransaction( * The three 4-digit numbers are displayed to the user either with dashes (or another appropriate separator) separating the three numbers, * or with the three numbers on separate lines. */ - fun getDecimalCodeRepresentation(byteArray: ByteArray): String { + fun getDecimalCodeRepresentation(byteArray: ByteArray, separator: String = " "): String { val b0 = byteArray[0].toUnsignedInt() // need unsigned byte val b1 = byteArray[1].toUnsignedInt() // need unsigned byte val b2 = byteArray[2].toUnsignedInt() // need unsigned byte @@ -107,7 +107,7 @@ internal abstract class SASDefaultVerificationTransaction( val second = ((b1 and 0x7).shl(10) or b2.shl(2) or b3.shr(6)) + 1000 // ((B3 & 0x3f) << 7 | B4 >> 1) + 1000 val third = ((b3 and 0x3f).shl(7) or b4.shr(1)) + 1000 - return "$first $second $third" + return "$first$separator$second$separator$third" } } From 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 3410858988..398b7f7b35 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 fe849a1ddf..57adb82d3a 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 7ead21394c..3ad6feec53 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 cea69030bc..be542cab88 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 0349acedd1..e3edf65720 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 4a6c0edf10..968d8515ac 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -62,7 +62,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.5.4\"" + buildConfigField "String", "SDK_VERSION", "\"1.5.6\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\"" diff --git a/vector-app/build.gradle b/vector-app/build.gradle index ca77e4b86f..1e8a8b3d3f 100644 --- a/vector-app/build.gradle +++ b/vector-app/build.gradle @@ -37,7 +37,7 @@ ext.versionMinor = 5 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -ext.versionPatch = 4 +ext.versionPatch = 6 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From 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 57adb82d3a..10c0e262e3 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 398b7f7b35..717da1624c 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 10c0e262e3..9e37fb513d 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 3ad6feec53..05b01c2bde 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 e3edf65720..77a555ca4e 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 43a8301d58..289ab52a49 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 2a04e58f41..38e500e1fe 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 9e37fb513d..2d67b127f9 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 2d67b127f9..e8f720d1f5 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 472121269f..0651b8f3d8 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 9a2c32f97c..5b2874bdef 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 0000000000..b58d80e50a --- /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 30010f90fd..1b4b359916 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 141144acd8..77c5649709 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 81d1c04261..082acb63ab 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 ea296d379d..bc1a69769d 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 f081e0a874..0d7ada0587 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -82,7 +82,7 @@ ext.libs = [ 'transition' : "androidx.transition:transition:1.2.0", ], google : [ - 'material' : "com.google.android.material:material:1.6.1", + 'material' : "com.google.android.material:material:1.7.0", 'appdistributionApi' : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution", 'appdistribution' : "com.google.firebase:firebase-appdistribution:$appDistribution", // Phone number https://github.com/google/libphonenumber From 26c550921af72d553575a98f7fbe095b2b4d6f88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 08:27:20 +0200 Subject: [PATCH 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 d0f093a451..f162685d7d 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ buildscript { classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5' classpath "com.likethesalad.android:stem-plugin:2.2.3" - classpath 'org.owasp:dependency-check-gradle:7.2.1' + classpath 'org.owasp:dependency-check-gradle:7.3.0' classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20" classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0" classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3' From 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 e8f720d1f5..cac0a47ad7 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 b77173519d..67396a390f 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 ae29132c91..f1fe3a496f 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 c9057cd289..d1f81567c2 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 cd3ec93387..254c0fe0d8 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 ac08e662db..af7a8d7cce 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 0000000000..017e23cd9e --- /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 0000000000..83bf3c747b --- /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 0000000000..c923750bc4 --- /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 0000000000..b1c84cad47 --- /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 0000000000..b35b1185b9 --- /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 0000000000..c1bf4fd59a --- /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 dfd50ea5cb..e93e128686 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -18,6 +18,7 @@ package im.vector.app.features.voicebroadcast import android.media.AudioAttributes import android.media.MediaPlayer +import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State import im.vector.app.features.voice.VoiceFailure @@ -25,7 +26,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.getRelationContent import org.matrix.android.sdk.api.session.getRoom @@ -39,10 +39,12 @@ import javax.inject.Singleton @Singleton class VoiceBroadcastPlayer @Inject constructor( - private val session: Session, + private val sessionHolder: ActiveSessionHolder, private val playbackTracker: AudioMessagePlaybackTracker, ) { + private val session get() = sessionHolder.getActiveSession() + private val mediaPlayerScope = CoroutineScope(Dispatchers.IO) private var currentMediaPlayer: MediaPlayer? = null From b89ab6c2fd54cedfb76228d3189deafbecee705a Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 e93e128686..c7259b12f8 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -76,10 +76,8 @@ class VoiceBroadcastPlayer @Inject constructor( fun stop() { currentMediaPlayer?.stop() - currentMediaPlayer?.release() - currentMediaPlayer?.setOnInfoListener(null) - currentMediaPlayer = null currentVoiceBroadcastEventId?.let { playbackTracker.stopPlayback(it) } + release(currentMediaPlayer) playlist = emptyList() currentPlayingIndex = -1 } @@ -147,11 +145,21 @@ class VoiceBroadcastPlayer @Inject constructor( } } + private fun release(mp: MediaPlayer?) { + mp?.apply { + release() + setOnInfoListener(null) + setOnCompletionListener(null) + setOnErrorListener(null) + } + } + inner class MediaPlayerListener : MediaPlayer.OnInfoListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener { override fun onInfo(mp: MediaPlayer, what: Int, extra: Int): Boolean { when (what) { MediaPlayer.MEDIA_INFO_STARTED_AS_NEXT -> { + release(currentMediaPlayer) currentMediaPlayer = mp currentPlayingIndex++ mediaPlayerScope.launch { prepareNextFile() } From 0c847cffc131f446cc94c3757883aee2ea870f21 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 c7259b12f8..db07503927 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -24,6 +24,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlayb import im.vector.app.features.voice.VoiceFailure import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.RelationType @@ -42,13 +43,13 @@ class VoiceBroadcastPlayer @Inject constructor( private val sessionHolder: ActiveSessionHolder, private val playbackTracker: AudioMessagePlaybackTracker, ) { - - private val session get() = sessionHolder.getActiveSession() - - private val mediaPlayerScope = CoroutineScope(Dispatchers.IO) + private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) + private val session + get() = sessionHolder.getActiveSession() private var currentMediaPlayer: MediaPlayer? = null private var currentPlayingIndex: Int = -1 + private var playlist = emptyList() 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 db07503927..72ec181966 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -51,8 +51,8 @@ class VoiceBroadcastPlayer @Inject constructor( private var currentPlayingIndex: Int = -1 private var playlist = emptyList() - 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 71daf4cc4f..1f16041b54 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -401,7 +401,7 @@ fun Event.getRelationContent(): RelationDefaultContent? { when (getClearType()) { EventType.STICKER -> getClearContent().toModel()?.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 20094e4be8..2d6082f9b5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TimelineEventDataSource.kt @@ -22,6 +22,8 @@ import io.realm.Sort import org.matrix.android.sdk.api.session.events.model.getRelationContent import org.matrix.android.sdk.api.session.events.model.isImageMessage import org.matrix.android.sdk.api.session.events.model.isVideoMessage +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.database.RealmSessionProvider @@ -74,7 +76,13 @@ internal class TimelineEventDataSource @Inject constructor( .distinct(TimelineEventEntityFields.EVENT_ID) .findAll() .mapNotNull { - timelineEventMapper.map(it).takeIf { it.root.getRelationContent()?.takeIf { it.type == eventType && it.eventId == eventId } != null } + timelineEventMapper.map(it) + .takeIf { + val isEventRelatedTo = it.root.getRelationContent()?.takeIf { it.type == eventType && it.eventId == eventId } != null + val isContentRelatedTo = it.root.getClearContent()?.toModel() + ?.relatesTo?.takeIf { it.type == eventType && it.eventId == eventId } != null + isEventRelatedTo || isContentRelatedTo + } } } } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt index b967afa9cb..58e7de7f32 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastHelper.kt @@ -40,7 +40,7 @@ class VoiceBroadcastHelper @Inject constructor( suspend fun stopVoiceBroadcast(roomId: String) = stopVoiceBroadcastUseCase.execute(roomId) - fun playOrResumePlayback(roomId: String, eventId: String) = voiceBroadcastPlayer.play(roomId, eventId) + fun playOrResumePlayback(roomId: String, eventId: String) = voiceBroadcastPlayer.playOrResume(roomId, eventId) fun pausePlayback() = voiceBroadcastPlayer.pause() diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt index 72ec181966..7f5e13504e 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -20,13 +20,19 @@ import android.media.AudioAttributes import android.media.MediaPlayer import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker -import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State import im.vector.app.features.voice.VoiceFailure +import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState +import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent +import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastStateUseCase import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.getRelationContent import org.matrix.android.sdk.api.session.getRoom @@ -34,6 +40,11 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent +import org.matrix.android.sdk.api.session.room.timeline.Timeline +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings +import org.matrix.android.sdk.flow.flow +import org.matrix.android.sdk.flow.unwrap import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -42,62 +53,117 @@ import javax.inject.Singleton class VoiceBroadcastPlayer @Inject constructor( private val sessionHolder: ActiveSessionHolder, private val playbackTracker: AudioMessagePlaybackTracker, + private val getVoiceBroadcastStateUseCase: GetVoiceBroadcastStateUseCase, ) { - private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) private val session get() = sessionHolder.getActiveSession() + private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) + private var voiceBroadcastStateJob: Job? = null + private var currentTimeline: Timeline? = null + set(value) { + field?.removeAllListeners() + field?.dispose() + field = value + } + + private val mediaPlayerListener = MediaPlayerListener() + private var timelineListener: TimelineListener? = null + private var currentMediaPlayer: MediaPlayer? = null - private var currentPlayingIndex: Int = -1 + private var nextMediaPlayer: MediaPlayer? = null + set(value) { + field = value + currentMediaPlayer?.setNextMediaPlayer(value) + } + private var currentSequence: Int? = null private var playlist = emptyList() 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 0000000000..5b3153ea40 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.voicebroadcast.usecase + +import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState +import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.RelationType +import org.matrix.android.sdk.api.session.getRoom +import timber.log.Timber +import javax.inject.Inject + +class GetVoiceBroadcastStateUseCase @Inject constructor( + private val session: Session, +) { + + fun execute(roomId: String, eventId: String): VoiceBroadcastState? { + val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId") + + Timber.d("## GetVoiceBroadcastStateUseCase: get voice broadcast state requested for $eventId") + + val initialEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() // Fallback to initial event + val relatedEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId).sortedBy { it.root.originServerTs } + val lastVoiceBroadcastEvent = relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent + return lastVoiceBroadcastEvent?.content?.voiceBroadcastState + } +} From f05f0a85b01ade9525b9f8abbc2e4616983e17fa Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 404b112574..21d12ee986 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt @@ -21,6 +21,7 @@ import android.media.MediaRecorder import android.os.Build import androidx.annotation.RequiresApi import im.vector.app.features.voice.AbstractVoiceRecorderQ +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.content.ContentAttachmentData @RequiresApi(Build.VERSION_CODES.Q) @@ -30,6 +31,7 @@ class VoiceBroadcastRecorderQ( private var maxFileSize = 0L // zero or negative for no limit private var currentSequence = 0 + private var currentRoomId: String? = null override var listener: VoiceBroadcastRecorder.Listener? = null @@ -51,11 +53,23 @@ class VoiceBroadcastRecorderQ( } override fun startRecord(roomId: String, chunkLength: Int) { + currentRoomId = roomId maxFileSize = (chunkLength * audioEncodingBitRate / 8).toLong() currentSequence = 1 startRecord(roomId) } + override fun pauseRecord() { + tryOrNull { mediaRecorder?.stop() } + mediaRecorder?.reset() + notifyOutputFileCreated() + } + + override fun resumeRecord() { + currentSequence++ + currentRoomId?.let { startRecord(it) } + } + override fun stopRecord() { super.stopRecord() notifyOutputFileCreated() From 6d6b4e52087c4c28c4beeedba00be437d6e82da6 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 7cb66cd9e5..d5d58f822e 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt @@ -55,7 +55,7 @@ class StartVoiceBroadcastUseCase @Inject constructor( QueryStringValue.IsNotEmpty ) .mapNotNull { it.asVoiceBroadcastEvent() } - .filter { it.content?.voiceBroadcastState != VoiceBroadcastState.STOPPED } + .filter { it.content?.voiceBroadcastState != null && it.content?.voiceBroadcastState != VoiceBroadcastState.STOPPED } if (onGoingVoiceBroadcastEvents.isEmpty()) { startVoiceBroadcast(room) From ed0d255495c7c6a04bb4e8d2b7f26bc28f415268 Mon Sep 17 00:00:00 2001 From: Benoit Marty 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 1824d5dc6c..9ac33c0545 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt @@ -106,6 +106,8 @@ interface Timeline { /** * Called when new events come through the sync. + * Note that the corresponding events may not be available yet in the database. + * [onTimelineUpdated] will be called with the event content. */ fun onNewTimelineEvents(eventIds: List) = 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 7f5e13504e..c55cb8a1d0 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -179,7 +179,11 @@ class VoiceBroadcastPlayer @Inject constructor( room.flow() .liveStateEvent(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO, QueryStringValue.Equals(voiceBroadcastEvent.root.stateKey!!)) .unwrap() - .mapNotNull { it.asVoiceBroadcastEvent()?.content?.voiceBroadcastState } + .mapNotNull { event -> + event.asVoiceBroadcastEvent() + ?.takeIf { it.reference?.eventId == eventId } + ?.content?.voiceBroadcastState + } .onEach { state -> when (state) { VoiceBroadcastState.STARTED, From 99a2afa5ee1378050f9b2d334d72166a71a33e54 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 0000000000..06f69dfa7f --- /dev/null +++ b/changelog.d/7419.wip @@ -0,0 +1 @@ +[Voice Broadcast] Live listening support From bafa2f8bde785d85a82df37c4715e7a06f2b261f Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 37ff920c57..c9bb0c5f54 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt @@ -23,6 +23,7 @@ import java.io.File interface VoiceBroadcastRecorder : VoiceRecorder { var listener: Listener? + var currentSequence: Int fun startRecord(roomId: String, chunkLength: Int) diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt index 21d12ee986..a65aae6f8a 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt @@ -30,8 +30,8 @@ class VoiceBroadcastRecorderQ( ) : AbstractVoiceRecorderQ(context), VoiceBroadcastRecorder { private var maxFileSize = 0L // zero or negative for no limit - private var currentSequence = 0 private var currentRoomId: String? = null + override var currentSequence = 0 override var listener: VoiceBroadcastRecorder.Listener? = null diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt index a9db63c538..d882d4049e 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/model/MessageVoiceBroadcastInfoContent.kt @@ -44,6 +44,8 @@ data class MessageVoiceBroadcastInfoContent( @Json(name = "state") val voiceBroadcastStateStr: String = "", /** The length of the voice chunks in seconds. **/ @Json(name = "chunk_length") val chunkLength: Int? = null, + /** The sequence of the last sent chunk. **/ + @Json(name = "last_chunk_sequence") val lastChunkSequence: Int? = null, ) : MessageContent { val voiceBroadcastState: VoiceBroadcastState? = VoiceBroadcastState.values() diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt index 835a57c102..1430dd8c86 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/PauseVoiceBroadcastUseCase.kt @@ -59,6 +59,7 @@ class PauseVoiceBroadcastUseCase @Inject constructor( body = MessageVoiceBroadcastInfoContent( relatesTo = reference, voiceBroadcastStateStr = VoiceBroadcastState.PAUSED.value, + lastChunkSequence = voiceBroadcastRecorder?.currentSequence, ).toContent(), ) diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt index 6eefa06979..bc6a3e7be6 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StopVoiceBroadcastUseCase.kt @@ -60,6 +60,7 @@ class StopVoiceBroadcastUseCase @Inject constructor( body = MessageVoiceBroadcastInfoContent( relatesTo = reference, voiceBroadcastStateStr = VoiceBroadcastState.STOPPED.value, + lastChunkSequence = voiceBroadcastRecorder?.currentSequence, ).toContent(), ) From 05eeef9dfec7b89218f4abb430435561b092b607 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 c55cb8a1d0..403675a9ee 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -23,16 +23,12 @@ import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlayb import im.vector.app.features.voice.VoiceFailure import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent -import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastStateUseCase +import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastUseCase import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.getRelationContent import org.matrix.android.sdk.api.session.getRoom @@ -43,8 +39,6 @@ import org.matrix.android.sdk.api.session.room.model.message.asMessageAudioEvent import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings -import org.matrix.android.sdk.flow.flow -import org.matrix.android.sdk.flow.unwrap import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -53,7 +47,7 @@ import javax.inject.Singleton class VoiceBroadcastPlayer @Inject constructor( private val sessionHolder: ActiveSessionHolder, private val playbackTracker: AudioMessagePlaybackTracker, - private val getVoiceBroadcastStateUseCase: GetVoiceBroadcastStateUseCase, + private val getVoiceBroadcastUseCase: GetVoiceBroadcastUseCase, ) { private val session get() = sessionHolder.getActiveSession() @@ -87,6 +81,7 @@ class VoiceBroadcastPlayer @Inject constructor( Timber.w("## VoiceBroadcastPlayer state: $field -> $value") field = value } + private var currentRoomId: String? = null fun playOrResume(roomId: String, eventId: String) { val hasChanged = currentVoiceBroadcastId != eventId @@ -132,17 +127,19 @@ class VoiceBroadcastPlayer @Inject constructor( // Clear playlist playlist = emptyList() currentSequence = null + currentRoomId = null } private fun startPlayback(roomId: String, eventId: String) { val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId") + currentRoomId = roomId // Stop listening previous voice broadcast if any if (state != State.IDLE) stop() state = State.BUFFERING - val voiceBroadcastState = getVoiceBroadcastStateUseCase.execute(roomId, eventId) + val voiceBroadcastState = getVoiceBroadcastUseCase.execute(roomId, eventId)?.content?.voiceBroadcastState if (voiceBroadcastState == VoiceBroadcastState.STOPPED) { // Get static playlist updatePlaylist(getExistingChunks(room, eventId)) @@ -172,33 +169,10 @@ class VoiceBroadcastPlayer @Inject constructor( } private fun playLiveVoiceBroadcast(room: Room, eventId: String) { - val voiceBroadcastEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() - ?: error("Cannot retrieve voice broadcast $eventId") + room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() ?: error("Cannot retrieve voice broadcast $eventId") updatePlaylist(getExistingChunks(room, eventId)) startPlayback(true) - room.flow() - .liveStateEvent(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO, QueryStringValue.Equals(voiceBroadcastEvent.root.stateKey!!)) - .unwrap() - .mapNotNull { event -> - event.asVoiceBroadcastEvent() - ?.takeIf { it.reference?.eventId == eventId } - ?.content?.voiceBroadcastState - } - .onEach { state -> - when (state) { - VoiceBroadcastState.STARTED, - VoiceBroadcastState.PAUSED, - VoiceBroadcastState.RESUMED -> { - observeIncomingChunks(room, eventId) - } - VoiceBroadcastState.STOPPED -> { - currentTimeline?.dispose() - currentTimeline?.removeAllListeners() - currentTimeline = null - } - } - } - .launchIn(coroutineScope) + observeIncomingEvents(room, eventId) } private fun getExistingChunks(room: Room, eventId: String): List { @@ -207,10 +181,7 @@ class VoiceBroadcastPlayer @Inject constructor( .filter { it.isVoiceBroadcast() } } - private fun observeIncomingChunks(room: Room, eventId: String) { - // Fixme this is probably not necessary here - currentTimeline?.dispose() - currentTimeline?.removeAllListeners() + private fun observeIncomingEvents(room: Room, eventId: String) { currentTimeline = room.timelineService().createTimeline(null, TimelineSettings(5)).also { timeline -> timelineListener = TimelineListener(eventId).also { timeline.addListener(it) } timeline.start() @@ -321,13 +292,17 @@ class VoiceBroadcastPlayer @Inject constructor( } override fun onCompletion(mp: MediaPlayer) { - when { - timelineListener == null && nextMediaPlayer == null -> { - stop() - } - nextMediaPlayer == null -> { - state = State.BUFFERING - } + if (nextMediaPlayer != null) return + val roomId = currentRoomId ?: return + val voiceBroadcastId = currentVoiceBroadcastId ?: return + val voiceBroadcastEventContent = getVoiceBroadcastUseCase.execute(roomId, voiceBroadcastId)?.content ?: return + val isLive = voiceBroadcastEventContent.voiceBroadcastState != null && voiceBroadcastEventContent.voiceBroadcastState != VoiceBroadcastState.STOPPED + + if (!isLive && voiceBroadcastEventContent.lastChunkSequence == currentSequence) { + // We'll not receive new chunks anymore so we can stop the live listening + stop() + } else { + state = State.BUFFERING } } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastUseCase.kt similarity index 80% rename from vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt rename to vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastUseCase.kt index 5b3153ea40..d08fa14a95 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastStateUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/GetVoiceBroadcastUseCase.kt @@ -16,7 +16,7 @@ package im.vector.app.features.voicebroadcast.usecase -import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState +import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.RelationType @@ -24,18 +24,17 @@ import org.matrix.android.sdk.api.session.getRoom import timber.log.Timber import javax.inject.Inject -class GetVoiceBroadcastStateUseCase @Inject constructor( +class GetVoiceBroadcastUseCase @Inject constructor( private val session: Session, ) { - fun execute(roomId: String, eventId: String): VoiceBroadcastState? { + fun execute(roomId: String, eventId: String): VoiceBroadcastEvent? { val room = session.getRoom(roomId) ?: error("Unknown roomId: $roomId") - Timber.d("## GetVoiceBroadcastStateUseCase: get voice broadcast state requested for $eventId") + Timber.d("## GetVoiceBroadcastUseCase: get voice broadcast $eventId") val initialEvent = room.timelineService().getTimelineEvent(eventId)?.root?.asVoiceBroadcastEvent() // Fallback to initial event val relatedEvents = room.timelineService().getTimelineEventsRelatedTo(RelationType.REFERENCE, eventId).sortedBy { it.root.originServerTs } - val lastVoiceBroadcastEvent = relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent - return lastVoiceBroadcastEvent?.content?.voiceBroadcastState + return relatedEvents.mapNotNull { it.root.asVoiceBroadcastEvent() }.lastOrNull() ?: initialEvent } } From 0a9f2bfa0ad050ff99ea546059d868134068dfac Mon Sep 17 00:00:00 2001 From: yostyle 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 403675a9ee..62252570c6 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -116,8 +116,6 @@ class VoiceBroadcastPlayer @Inject constructor( voiceBroadcastStateJob = null // In case of live broadcast, stop observing new chunks - currentTimeline?.dispose() - currentTimeline?.removeAllListeners() currentTimeline = null timelineListener = null From 4c712095735fc5844ad93b5916fbe110bbf13ae2 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 e6714005a1..69b4d57e28 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3078,6 +3078,11 @@ %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 52d16eae7d..50d5aaf014 100644 --- a/library/ui-styles/src/main/res/values/dimens.xml +++ b/library/ui-styles/src/main/res/values/dimens.xml @@ -73,6 +73,9 @@ 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 cb947a67ce..245d92f95b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -201,7 +201,7 @@ class MessageItemFactory @Inject constructor( is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes) is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, attributes) is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes) - is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(messageContent, params.eventsGroup, highlight, callback, attributes) + is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(params, messageContent, highlight, callback, attributes) else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes) } return messageItem?.apply { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt index f2dfb020a1..1064d2bbc5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt @@ -15,46 +15,66 @@ */ package im.vector.app.features.home.room.detail.timeline.factory +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.DrawableProvider import im.vector.app.features.home.room.detail.timeline.TimelineEventController -import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider -import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventsGroup import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem -import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem -import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastItem_ +import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem +import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_ +import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class VoiceBroadcastItemFactory @Inject constructor( private val session: Session, private val avatarSizeProvider: AvatarSizeProvider, - private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker, + private val colorProvider: ColorProvider, + private val drawableProvider: DrawableProvider, + private val voiceBroadcastRecorder: VoiceBroadcastRecorder?, ) { fun create( + params: TimelineItemFactoryParams, messageContent: MessageVoiceBroadcastInfoContent, - eventsGroup: TimelineEventsGroup?, highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, - ): MessageVoiceBroadcastItem? { + ): MessageVoiceBroadcastRecordingItem? { // Only display item of the initial event with updated data if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null - val voiceBroadcastEventsGroup = eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null + val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent() val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent() val mostRecentMessageContent = mostRecentEvent?.content ?: return null val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId - return MessageVoiceBroadcastItem_() + return if (isRecording) { + createRecordingItem(params.event.roomId, highlight, callback, attributes) + } else { + createRecordingItem(params.event.roomId, highlight, callback, attributes) + } + } + + private fun createRecordingItem( + roomId: String, + highlight: Boolean, + callback: TimelineEventController.Callback?, + attributes: AbsMessageItem.Attributes, + ): MessageVoiceBroadcastRecordingItem? { + val roomSummary = session.getRoom(roomId)?.roomSummary() + return MessageVoiceBroadcastRecordingItem_() .attributes(attributes) .highlighted(highlight) - .voiceBroadcastState(mostRecentMessageContent.voiceBroadcastState) - .recording(isRecording) - .audioMessagePlaybackTracker(audioMessagePlaybackTracker) + .roomItem(roomSummary?.toMatrixItem()) + .colorProvider(colorProvider) + .drawableProvider(drawableProvider) + .voiceBroadcastRecorder(voiceBroadcastRecorder) .leftGuideline(avatarSizeProvider.leftGuideline) .callback(callback) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt deleted file mode 100644 index 1927024a36..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastItem.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.home.room.detail.timeline.item - -import android.annotation.SuppressLint -import android.widget.ImageButton -import android.widget.TextView -import com.airbnb.epoxy.EpoxyAttribute -import com.airbnb.epoxy.EpoxyModelClass -import im.vector.app.R -import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction -import im.vector.app.features.home.room.detail.timeline.TimelineEventController -import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker -import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker.Listener.State -import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState - -@EpoxyModelClass -abstract class MessageVoiceBroadcastItem : AbsMessageItem() { - - @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 0000000000..d271c55ebb --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.timeline.item + +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.extensions.tintBackground +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.DrawableProvider +import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction +import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder +import org.matrix.android.sdk.api.util.MatrixItem + +@EpoxyModelClass +abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem() { + + @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 c9bb0c5f54..8b69051823 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorder.kt @@ -22,12 +22,21 @@ import java.io.File interface VoiceBroadcastRecorder : VoiceRecorder { - var listener: Listener? - var currentSequence: Int + val currentSequence: Int + val state: State fun startRecord(roomId: String, chunkLength: Int) + fun addListener(listener: Listener) + fun removeListener(listener: Listener) - fun interface Listener { - fun onVoiceMessageCreated(file: File, @IntRange(from = 1) sequence: Int) + interface Listener { + fun onVoiceMessageCreated(file: File, @IntRange(from = 1) sequence: Int) = Unit + fun onStateUpdated(state: State) = Unit + } + + enum class State { + Recording, + Paused, + Idle, } } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt index a65aae6f8a..cd1a61b986 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt @@ -32,8 +32,13 @@ class VoiceBroadcastRecorderQ( private var maxFileSize = 0L // zero or negative for no limit private var currentRoomId: String? = null override var currentSequence = 0 + override var state = VoiceBroadcastRecorder.State.Idle + set(value) { + field = value + listeners.forEach { it.onStateUpdated(value) } + } - override var listener: VoiceBroadcastRecorder.Listener? = null + private val listeners = mutableListOf() override val outputFormat = MediaRecorder.OutputFormat.MPEG_4 override val audioEncoder = MediaRecorder.AudioEncoder.HE_AAC @@ -57,24 +62,28 @@ class VoiceBroadcastRecorderQ( maxFileSize = (chunkLength * audioEncodingBitRate / 8).toLong() currentSequence = 1 startRecord(roomId) + state = VoiceBroadcastRecorder.State.Recording } override fun pauseRecord() { tryOrNull { mediaRecorder?.stop() } mediaRecorder?.reset() notifyOutputFileCreated() + state = VoiceBroadcastRecorder.State.Paused } override fun resumeRecord() { currentSequence++ currentRoomId?.let { startRecord(it) } + state = VoiceBroadcastRecorder.State.Recording } override fun stopRecord() { super.stopRecord() notifyOutputFileCreated() - listener = null + listeners.clear() currentSequence = 0 + state = VoiceBroadcastRecorder.State.Idle } override fun release() { @@ -82,6 +91,15 @@ class VoiceBroadcastRecorderQ( super.release() } + override fun addListener(listener: VoiceBroadcastRecorder.Listener) { + listeners.add(listener) + listener.onStateUpdated(state) + } + + override fun removeListener(listener: VoiceBroadcastRecorder.Listener) { + listeners.remove(listener) + } + private fun onMaxFileSizeApproaching(roomId: String) { setNextOutputFile(roomId) } @@ -92,8 +110,8 @@ class VoiceBroadcastRecorderQ( } private fun notifyOutputFileCreated() { - outputFile?.let { - listener?.onVoiceMessageCreated(it, currentSequence) + outputFile?.let { file -> + listeners.forEach { it.onVoiceMessageCreated(file, currentSequence) } outputFile = nextOutputFile nextOutputFile = null } diff --git a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt index d5d58f822e..7934d18e36 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/usecase/StartVoiceBroadcastUseCase.kt @@ -81,9 +81,11 @@ class StartVoiceBroadcastUseCase @Inject constructor( } private fun startRecording(room: Room, eventId: String, chunkLength: Int) { - voiceBroadcastRecorder?.listener = VoiceBroadcastRecorder.Listener { file, sequence -> - sendVoiceFile(room, file, eventId, sequence) - } + voiceBroadcastRecorder?.addListener(object : VoiceBroadcastRecorder.Listener { + override fun onVoiceMessageCreated(file: File, sequence: Int) { + sendVoiceFile(room, file, eventId, sequence) + } + }) voiceBroadcastRecorder?.startRecord(room.roomId, chunkLength) } diff --git a/vector/src/main/res/drawable/ic_live_broadcast_16.xml b/vector/src/main/res/drawable/ic_live_broadcast_16.xml new file mode 100644 index 0000000000..7d427a56d0 --- /dev/null +++ b/vector/src/main/res/drawable/ic_live_broadcast_16.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/vector/src/main/res/drawable/ic_recording_dot.xml b/vector/src/main/res/drawable/ic_recording_dot.xml new file mode 100644 index 0000000000..f5d92f9718 --- /dev/null +++ b/vector/src/main/res/drawable/ic_recording_dot.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/drawable/ic_stop.xml b/vector/src/main/res/drawable/ic_stop.xml new file mode 100644 index 0000000000..459a7cfce2 --- /dev/null +++ b/vector/src/main/res/drawable/ic_stop.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/drawable/rounded_rect_shape_2.xml b/vector/src/main/res/drawable/rounded_rect_shape_2.xml new file mode 100644 index 0000000000..977de2fd09 --- /dev/null +++ b/vector/src/main/res/drawable/rounded_rect_shape_2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml index e35060f72a..6773280ba5 100644 --- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_stub.xml @@ -5,58 +5,89 @@ android:id="@+id/messageRootLayout" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@drawable/rounded_rect_shape_8" + android:backgroundTint="?vctr_content_quinary" android:padding="@dimen/layout_vertical_margin" tools:viewBindingIgnore="true"> + + + 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 1064d2bbc5..13a38ac4be 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt @@ -15,12 +15,16 @@ */ package im.vector.app.features.home.room.detail.timeline.factory +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem +import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem +import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_ import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder @@ -46,7 +50,7 @@ class VoiceBroadcastItemFactory @Inject constructor( highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, - ): MessageVoiceBroadcastRecordingItem? { + ): VectorEpoxyModel? { // Only display item of the initial event with updated data if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null @@ -57,7 +61,7 @@ class VoiceBroadcastItemFactory @Inject constructor( return if (isRecording) { createRecordingItem(params.event.roomId, highlight, callback, attributes) } else { - createRecordingItem(params.event.roomId, highlight, callback, attributes) + createListeningItem(params.event.roomId, highlight, callback, attributes) } } @@ -66,7 +70,7 @@ class VoiceBroadcastItemFactory @Inject constructor( highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, - ): MessageVoiceBroadcastRecordingItem? { + ): MessageVoiceBroadcastRecordingItem { val roomSummary = session.getRoom(roomId)?.roomSummary() return MessageVoiceBroadcastRecordingItem_() .attributes(attributes) @@ -78,4 +82,22 @@ class VoiceBroadcastItemFactory @Inject constructor( .leftGuideline(avatarSizeProvider.leftGuideline) .callback(callback) } + + private fun createListeningItem( + roomId: String, + highlight: Boolean, + callback: TimelineEventController.Callback?, + attributes: AbsMessageItem.Attributes, + ): MessageVoiceBroadcastListeningItem { + val roomSummary = session.getRoom(roomId)?.roomSummary() + return MessageVoiceBroadcastListeningItem_() + .attributes(attributes) + .highlighted(highlight) + .roomItem(roomSummary?.toMatrixItem()) + .colorProvider(colorProvider) + .drawableProvider(drawableProvider) + .voiceBroadcastRecorder(voiceBroadcastRecorder) + .leftGuideline(avatarSizeProvider.leftGuideline) + .callback(callback) + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt new file mode 100644 index 0000000000..e5d0fd6c30 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.timeline.item + +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.extensions.tintBackground +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.DrawableProvider +import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction +import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder +import org.matrix.android.sdk.api.util.MatrixItem + +@EpoxyModelClass +abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem() { + + @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 d271c55ebb..30475c1659 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt @@ -132,6 +132,6 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem + + + + + + + + + + + + + + + + + + + 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 | 32 +++--- 5 files changed, 98 insertions(+), 62 deletions(-) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 69b4d57e28..ea9b4b5999 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3082,6 +3082,9 @@ 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 13a38ac4be..af14d532c8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt @@ -27,6 +27,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadca import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastListeningItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceBroadcastRecordingItem_ +import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder import im.vector.app.features.voicebroadcast.model.MessageVoiceBroadcastInfoContent import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState @@ -42,6 +43,7 @@ class VoiceBroadcastItemFactory @Inject constructor( private val colorProvider: ColorProvider, private val drawableProvider: DrawableProvider, private val voiceBroadcastRecorder: VoiceBroadcastRecorder?, + private val voiceBroadcastPlayer: VoiceBroadcastPlayer, ) { fun create( @@ -53,7 +55,8 @@ class VoiceBroadcastItemFactory @Inject constructor( ): VectorEpoxyModel? { // Only display item of the initial event with updated data if (messageContent.voiceBroadcastState != VoiceBroadcastState.STARTED) return null - val voiceBroadcastEventsGroup = params.eventsGroup?.let { VoiceBroadcastEventsGroup(it) } ?: return null + val eventsGroup = params.eventsGroup ?: return null + val voiceBroadcastEventsGroup = VoiceBroadcastEventsGroup(eventsGroup) val mostRecentTimelineEvent = voiceBroadcastEventsGroup.getLastDisplayableEvent() val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent() val mostRecentMessageContent = mostRecentEvent?.content ?: return null @@ -61,7 +64,7 @@ class VoiceBroadcastItemFactory @Inject constructor( return if (isRecording) { createRecordingItem(params.event.roomId, highlight, callback, attributes) } else { - createListeningItem(params.event.roomId, highlight, callback, attributes) + createListeningItem(params.event.roomId, eventsGroup.groupId, highlight, callback, attributes) } } @@ -85,6 +88,7 @@ class VoiceBroadcastItemFactory @Inject constructor( private fun createListeningItem( roomId: String, + voiceBroadcastId: String, highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, @@ -96,7 +100,8 @@ class VoiceBroadcastItemFactory @Inject constructor( .roomItem(roomSummary?.toMatrixItem()) .colorProvider(colorProvider) .drawableProvider(drawableProvider) - .voiceBroadcastRecorder(voiceBroadcastRecorder) + .voiceBroadcastPlayer(voiceBroadcastPlayer) + .voiceBroadcastId(voiceBroadcastId) .leftGuideline(avatarSizeProvider.leftGuideline) .callback(callback) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt index e5d0fd6c30..3a090b0eb6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.item +import android.view.View import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView @@ -23,12 +24,11 @@ import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R -import im.vector.app.core.extensions.tintBackground import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider -import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction +import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.timeline.TimelineEventController -import im.vector.app.features.voicebroadcast.VoiceBroadcastRecorder +import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer import org.matrix.android.sdk.api.util.MatrixItem @EpoxyModelClass @@ -38,7 +38,10 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem + 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) } + VoiceBroadcastPlayer.State.PLAYING -> { + playPauseButton.setImageResource(R.drawable.ic_play_pause_pause) + playPauseButton.contentDescription = view.resources.getString(R.string.a11y_play_voice_broadcast) + playPauseButton.setOnClickListener { attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.Pause) } } - VoiceBroadcastRecorder.State.Paused -> { - stopRecordButton.isEnabled = true - - liveIndicator.isVisible = true - liveIndicator.tintBackground(colorProvider.getColorFromAttribute(R.attr.vctr_content_quaternary)) - - recordButton.setImageResource(R.drawable.ic_recording_dot) - recordButton.contentDescription = holder.view.resources.getString(R.string.a11y_resume_voice_broadcast_record) - recordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Resume) } - stopRecordButton.setOnClickListener { attributes.callback?.onTimelineItemAction(VoiceBroadcastAction.Recording.Stop) } - } - VoiceBroadcastRecorder.State.Idle -> { - recordButton.isEnabled = false - stopRecordButton.isEnabled = false - liveIndicator.isVisible = false + VoiceBroadcastPlayer.State.IDLE, + VoiceBroadcastPlayer.State.PAUSED -> { + playPauseButton.setImageResource(R.drawable.ic_play_pause_play) + playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast) + playPauseButton.setOnClickListener { + attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId)) + } } + VoiceBroadcastPlayer.State.BUFFERING -> Unit } } } + private fun renderInactiveMedia(holder: Holder) { + with(holder) { + liveIndicator.isVisible = false + bufferingView.isVisible = false + playPauseButton.isVisible = true + playPauseButton.setImageResource(R.drawable.ic_play_pause_play) + playPauseButton.contentDescription = view.resources.getString(R.string.a11y_pause_voice_broadcast) + playPauseButton.setOnClickListener { + attributes.callback?.onTimelineItemAction(RoomDetailAction.VoiceBroadcastAction.Listening.PlayOrResume(voiceBroadcastId)) + } + } + } + + private fun isCurrentMediaActive() = voiceBroadcastPlayer?.currentVoiceBroadcastId == voiceBroadcastId + override fun unbind(holder: Holder) { super.unbind(holder) - voiceBroadcastRecorder?.removeListener(recorderListener) + voiceBroadcastPlayer?.removeListener(playerListener) } override fun getViewStubId() = STUB_ID @@ -127,8 +142,8 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem(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 62252570c6..9c118f957d 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -73,15 +73,17 @@ class VoiceBroadcastPlayer @Inject constructor( private var currentSequence: Int? = null private var playlist = emptyList() - 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 6773280ba5..506ca4ff47 100644 --- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml @@ -65,29 +65,27 @@ app:constraint_referenced_ids="roomAvatarImageView,titleText" /> + + - - 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 af14d532c8..5a70f66d75 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt @@ -64,7 +64,7 @@ class VoiceBroadcastItemFactory @Inject constructor( return if (isRecording) { createRecordingItem(params.event.roomId, highlight, callback, attributes) } else { - createListeningItem(params.event.roomId, eventsGroup.groupId, highlight, callback, attributes) + createListeningItem(params.event.roomId, eventsGroup.groupId, mostRecentMessageContent.voiceBroadcastState, highlight, callback, attributes) } } @@ -89,6 +89,7 @@ class VoiceBroadcastItemFactory @Inject constructor( private fun createListeningItem( roomId: String, voiceBroadcastId: String, + voiceBroadcastState: VoiceBroadcastState?, highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, @@ -102,6 +103,7 @@ class VoiceBroadcastItemFactory @Inject constructor( .drawableProvider(drawableProvider) .voiceBroadcastPlayer(voiceBroadcastPlayer) .voiceBroadcastId(voiceBroadcastId) + .voiceBroadcastState(voiceBroadcastState) .leftGuideline(avatarSizeProvider.leftGuideline) .callback(callback) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt index 3a090b0eb6..bf0f202f0c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt @@ -24,11 +24,13 @@ import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R +import im.vector.app.core.extensions.tintBackground import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.voicebroadcast.VoiceBroadcastPlayer +import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState import org.matrix.android.sdk.api.util.MatrixItem @EpoxyModelClass @@ -43,6 +45,9 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem { + 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 9c118f957d..28ed7a58b8 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.events.model.RelationType -import org.matrix.android.sdk.api.session.events.model.getRelationContent import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent @@ -73,8 +72,7 @@ class VoiceBroadcastPlayer @Inject constructor( private var currentSequence: Int? = null private var playlist = emptyList() - 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 506ca4ff47..6a4fcef77a 100644 --- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml @@ -78,14 +78,18 @@ app:layout_constraintTop_toBottomOf="@id/headerBottomBarrier" app:tint="?vctr_content_secondary" /> - + 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 28ed7a58b8..bb5fe5c37d 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -18,6 +18,7 @@ package im.vector.app.features.voicebroadcast import android.media.AudioAttributes import android.media.MediaPlayer +import androidx.annotation.MainThread import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.voice.VoiceFailure @@ -29,6 +30,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.room.Room @@ -75,6 +77,7 @@ class VoiceBroadcastPlayer @Inject constructor( var currentVoiceBroadcastId: String? = null private var state: State = State.IDLE + @MainThread set(value) { Timber.w("## VoiceBroadcastPlayer state: $field -> $value") field = value @@ -168,7 +171,7 @@ class VoiceBroadcastPlayer @Inject constructor( currentMediaPlayer?.start() currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) } currentSequence = sequence - state = State.PLAYING + withContext(Dispatchers.Main) { state = State.PLAYING } nextMediaPlayer = prepareNextMediaPlayer() } catch (failure: Throwable) { Timber.e(failure, "Unable to start playback") From 930c85672866c5d83c47795656d40c8b740639e6 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 5a70f66d75..98f45cd5db 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt @@ -34,6 +34,7 @@ import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.getUser import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -61,10 +62,19 @@ class VoiceBroadcastItemFactory @Inject constructor( val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent() val mostRecentMessageContent = mostRecentEvent?.content ?: return null val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId + val recorderName = mostRecentTimelineEvent.root.stateKey?.let { session.getUser(it) }?.displayName ?: mostRecentTimelineEvent.root.stateKey return if (isRecording) { createRecordingItem(params.event.roomId, highlight, callback, attributes) } else { - createListeningItem(params.event.roomId, eventsGroup.groupId, mostRecentMessageContent.voiceBroadcastState, highlight, callback, attributes) + createListeningItem( + params.event.roomId, + eventsGroup.groupId, + mostRecentMessageContent.voiceBroadcastState, + recorderName, + highlight, + callback, + attributes + ) } } @@ -90,6 +100,7 @@ class VoiceBroadcastItemFactory @Inject constructor( roomId: String, voiceBroadcastId: String, voiceBroadcastState: VoiceBroadcastState?, + broadcasterName: String?, highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, @@ -104,6 +115,7 @@ class VoiceBroadcastItemFactory @Inject constructor( .voiceBroadcastPlayer(voiceBroadcastPlayer) .voiceBroadcastId(voiceBroadcastId) .voiceBroadcastState(voiceBroadcastState) + .broadcasterName(broadcasterName) .leftGuideline(avatarSizeProvider.leftGuideline) .callback(callback) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt index bf0f202f0c..130d44202f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt @@ -48,6 +48,9 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem(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 6a4fcef77a..73acde2942 100644 --- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml @@ -24,7 +24,7 @@ android:singleLine="true" android:text="@string/voice_broadcast_live" android:textColor="?colorOnError" - app:drawableStartCompat="@drawable/ic_live_broadcast_16" + app:drawableStartCompat="@drawable/ic_voice_broadcast_16" app:drawableTint="?colorOnError" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -36,7 +36,7 @@ android:contentDescription="@string/avatar" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:src="@sample/user_round_avatars" /> + tools:src="@sample/room_round_avatars" /> + 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 0000000000..4a399eee04 --- /dev/null +++ b/changelog.d/7421.wip @@ -0,0 +1 @@ +[Voice Broadcast] Improve rendering in the timeline From 9a96de4f06f60ed47261f9cb34b743db4e169918 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 98f45cd5db..5dc601a91a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/VoiceBroadcastItemFactory.kt @@ -62,9 +62,15 @@ class VoiceBroadcastItemFactory @Inject constructor( val mostRecentEvent = mostRecentTimelineEvent.root.asVoiceBroadcastEvent() val mostRecentMessageContent = mostRecentEvent?.content ?: return null val isRecording = mostRecentMessageContent.voiceBroadcastState != VoiceBroadcastState.STOPPED && mostRecentEvent.root.stateKey == session.myUserId - val recorderName = mostRecentTimelineEvent.root.stateKey?.let { session.getUser(it) }?.displayName ?: mostRecentTimelineEvent.root.stateKey + val recorderName = mostRecentTimelineEvent.root.stateKey?.let { session.getUser(it) }?.displayName ?: mostRecentTimelineEvent.root.stateKey return if (isRecording) { - createRecordingItem(params.event.roomId, highlight, callback, attributes) + createRecordingItem( + params.event.roomId, + eventsGroup.groupId, + highlight, + callback, + attributes + ) } else { createListeningItem( params.event.roomId, @@ -80,12 +86,14 @@ class VoiceBroadcastItemFactory @Inject constructor( private fun createRecordingItem( roomId: String, + voiceBroadcastId: String, highlight: Boolean, callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, ): MessageVoiceBroadcastRecordingItem { val roomSummary = session.getRoom(roomId)?.roomSummary() return MessageVoiceBroadcastRecordingItem_() + .id("voice_broadcast_$voiceBroadcastId") .attributes(attributes) .highlighted(highlight) .roomItem(roomSummary?.toMatrixItem()) @@ -107,6 +115,7 @@ class VoiceBroadcastItemFactory @Inject constructor( ): MessageVoiceBroadcastListeningItem { val roomSummary = session.getRoom(roomId)?.roomSummary() return MessageVoiceBroadcastListeningItem_() + .id("voice_broadcast_$voiceBroadcastId") .attributes(attributes) .highlighted(highlight) .roomItem(roomSummary?.toMatrixItem()) From f2cc08263fa59f3ca58b3ac41a7508b04aa65117 Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 130d44202f..5b58dda4e6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastListeningItem.kt @@ -24,6 +24,7 @@ import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R +import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.tintBackground import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider @@ -127,13 +128,13 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageItem { 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 bb5fe5c37d..2c892c8306 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastPlayer.kt @@ -41,6 +41,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import timber.log.Timber +import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject import javax.inject.Singleton @@ -84,7 +85,7 @@ class VoiceBroadcastPlayer @Inject constructor( listeners.forEach { it.onStateChanged(value) } } private var currentRoomId: String? = null - private var listeners = mutableListOf() + 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 cd1a61b986..5285dc5e3b 100644 --- a/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt +++ b/vector/src/main/java/im/vector/app/features/voicebroadcast/VoiceBroadcastRecorderQ.kt @@ -23,6 +23,7 @@ import androidx.annotation.RequiresApi import im.vector.app.features.voice.AbstractVoiceRecorderQ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.content.ContentAttachmentData +import java.util.concurrent.CopyOnWriteArrayList @RequiresApi(Build.VERSION_CODES.Q) class VoiceBroadcastRecorderQ( @@ -38,7 +39,7 @@ class VoiceBroadcastRecorderQ( listeners.forEach { it.onStateUpdated(value) } } - private val listeners = mutableListOf() + 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 73acde2942..248c04a2f6 100644 --- a/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_voice_broadcast_listening_stub.xml @@ -71,6 +71,7 @@ android:layout_width="16dp" android:layout_height="16dp" android:layout_marginEnd="5dp" + android:contentDescription="@null" android:src="@drawable/ic_microphone" app:tint="?vctr_content_secondary" /> @@ -97,6 +98,7 @@ android:layout_width="16dp" android:layout_height="16dp" android:layout_marginEnd="5dp" + android:contentDescription="@null" android:src="@drawable/ic_voice_broadcast_16" app:tint="?vctr_content_secondary" /> From 926f4d920180e40905f253d00df93aef714793ae Mon Sep 17 00:00:00 2001 From: Florian Renaud 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 41ef1fac9c..c417053b2a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceBroadcastRecordingItem.kt @@ -86,6 +86,7 @@ abstract class MessageVoiceBroadcastRecordingItem : AbsMessageItem { 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 | 10 ++-- .../home/room/detail/TimelineViewModel.kt | 51 +++++++++++++------ 2 files changed, 39 insertions(+), 22 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 c740e07257..7c83a4afa7 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,14 +114,12 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.notificationCount = unreadNotifications?.notificationCount ?: 0 roomSummaryEntity.threadHighlightCount = unreadThreadNotifications - ?.map { it.value.highlightCount.takeIf { count -> (count ?: 0) > 0 } } - ?.size - ?: 0 - roomSummaryEntity.threadNotificationCount = unreadThreadNotifications - ?.map { it.value.notificationCount.takeIf { count -> (count ?: 0) > 0 } } - ?.size + ?.count { (it.value.highlightCount ?: 0) > 0 } ?: 0 + roomSummaryEntity.threadNotificationCount = unreadThreadNotifications + ?.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 511fd597fe..c30d7a648d 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 0000000000..6c6673eaf0 --- /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 18ce26171c..34e36a55da 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -52,8 +52,8 @@ jobs: (contains(github.event.issue.labels.*.name, 'S-Critical') && (contains(github.event.issue.labels.*.name, 'O-Frequent') || contains(github.event.issue.labels.*.name, 'O-Occasional')) || - contains(github.event.issue.labels.*.name, 'S-Major') && - contains(github.event.issue.labels.*.name, 'O-Frequent') || + (contains(github.event.issue.labels.*.name, 'S-Major') && + contains(github.event.issue.labels.*.name, 'O-Frequent')) || contains(github.event.issue.labels.*.name, 'A11y')) steps: - uses: octokit/graphql-action@v2.x From 113d0ff5400860337deb5786c105cfeba655363e Mon Sep 17 00:00:00 2001 From: Nikita Fedrunov <66663241+fedrunov@users.noreply.github.com> Date: Fri, 21 Oct 2022 15:06:20 +0200 Subject: [PATCH 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 8c14ca892a..773e870ffd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt @@ -65,6 +65,11 @@ data class HomeServerCapabilities( * True if the home server supports login via qr code, false otherwise. */ val canLoginWithQrCode: Boolean = false, + + /** + * True if the home server supports threaded read receipts and unread notifications. + */ + val canUseThreadReadReceiptsAndNotifications: Boolean = false, ) { enum class RoomCapabilitySupport { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt index 75639c6a21..d443d6e3c8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/HomeServerVersion.kt @@ -60,5 +60,6 @@ internal data class HomeServerVersion( val r0_6_0 = HomeServerVersion(major = 0, minor = 6, patch = 0) val r0_6_1 = HomeServerVersion(major = 0, minor = 6, patch = 1) val v1_3_0 = HomeServerVersion(major = 1, minor = 3, patch = 0) + val v1_4_0 = HomeServerVersion(major = 1, minor = 4, patch = 0) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt index 5e133fab9c..1245d8df4b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt @@ -54,6 +54,8 @@ private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind" private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440" private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable" private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882" +private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771" +private const val FEATURE_THREADS_MSC3773 = "org.matrix.msc3773" /** * Return true if the SDK supports this homeserver version. @@ -79,6 +81,15 @@ internal fun Versions.doesServerSupportThreads(): Boolean { return unstableFeatures?.get(FEATURE_THREADS_MSC3440_STABLE) ?: false } +/** + * Indicate if the homeserver support MSC3771 and MSC3773 for threaded read receipts and unread notifications. + */ +internal fun Versions.doesServerSupportThreadUnreadNotifications(): Boolean { + val msc3771 = unstableFeatures?.get(FEATURE_THREADS_MSC3771) ?: false + val msc3773 = unstableFeatures?.get(FEATURE_THREADS_MSC3773) ?: false + return getMaxVersion() >= HomeServerVersion.v1_4_0 || (msc3771 && msc3773) +} + internal fun Versions.doesServerSupportQrCodeLogin(): Boolean { return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 9a2c32f97c..def0f6de7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -56,6 +56,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo039 +import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo040 import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import javax.inject.Inject @@ -64,7 +65,7 @@ internal class RealmSessionStoreMigration @Inject constructor( private val normalizer: Normalizer ) : MatrixRealmMigration( dbName = "Session", - schemaVersion = 39L, + schemaVersion = 40L, ) { /** * Forces all RealmSessionStoreMigration instances to be equal. @@ -113,5 +114,6 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldVersion < 37) MigrateSessionTo037(realm).perform() if (oldVersion < 38) MigrateSessionTo038(realm).perform() if (oldVersion < 39) MigrateSessionTo039(realm).perform() + if (oldVersion < 40) MigrateSessionTo040(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt index 63fa101c45..3528ca0051 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt @@ -45,6 +45,7 @@ internal object HomeServerCapabilitiesMapper { canUseThreading = entity.canUseThreading, canControlLogoutDevices = entity.canControlLogoutDevices, canLoginWithQrCode = entity.canLoginWithQrCode, + canUseThreadReadReceiptsAndNotifications = entity.canUseThreadReadReceiptsAndNotifications ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt new file mode 100644 index 0000000000..b3e02342dd --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo040.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.database.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields +import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +internal class MigrateSessionTo040(realm: DynamicRealm) : RealmMigrator(realm, 40) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("HomeServerCapabilitiesEntity") + ?.addField(HomeServerCapabilitiesEntityFields.CAN_USE_THREAD_READ_RECEIPTS_AND_NOTIFICATIONS, Boolean::class.java) + ?.transform { obj -> + obj.set(HomeServerCapabilitiesEntityFields.CAN_USE_THREAD_READ_RECEIPTS_AND_NOTIFICATIONS, false) + } + ?.forceRefreshOfHomeServerCapabilities() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt index cfa02b2c74..89f1e50b30 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt @@ -32,6 +32,7 @@ internal open class HomeServerCapabilitiesEntity( var canUseThreading: Boolean = false, var canControlLogoutDevices: Boolean = false, var canLoginWithQrCode: Boolean = false, + var canUseThreadReadReceiptsAndNotifications: Boolean = false, ) : RealmObject() { companion object diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt index 2c3cb440b6..a5953d870c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin +import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreadUnreadNotifications import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity @@ -144,6 +145,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( homeServerCapabilitiesEntity.canControlLogoutDevices = getVersionResult.doesServerSupportLogoutDevices() homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */ getVersionResult.doesServerSupportThreads() + homeServerCapabilitiesEntity.canUseThreadReadReceiptsAndNotifications = + getVersionResult.doesServerSupportThreadUnreadNotifications() homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult.doesServerSupportQrCodeLogin() } From 31811bb7e0eff83b1fcabc1cd27298ebb121bc5e Mon Sep 17 00:00:00 2001 From: jonnyandrew 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 0000000000..8f014af31b --- /dev/null +++ b/changelog.d/7428.bugfix @@ -0,0 +1 @@ +Fix crash by disabling Flipper on Android API 22 and below - only affects debug version of the application. diff --git a/tools/danger/dangerfile.js b/tools/danger/dangerfile.js index 1a36474470..2fc55bad0c 100644 --- a/tools/danger/dangerfile.js +++ b/tools/danger/dangerfile.js @@ -81,6 +81,7 @@ const allowList = [ "Florian14", "ganfra", "jmartinesp", + "jonnyandrew", "langleyd", "MadLittleMods", "manuroe", diff --git a/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt b/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt index 2e4336c942..cbf9e4f11f 100644 --- a/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt +++ b/vector-app/src/debug/java/im/vector/app/flipper/VectorFlipperProxy.kt @@ -17,6 +17,7 @@ package im.vector.app.flipper import android.content.Context +import android.os.Build import com.facebook.flipper.android.AndroidFlipperClient import com.facebook.flipper.android.utils.FlipperUtils import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin @@ -31,6 +32,7 @@ import com.kgurgul.flipper.RealmDatabaseDriver import com.kgurgul.flipper.RealmDatabaseProvider import im.vector.app.core.debug.FlipperProxy import io.realm.RealmConfiguration +import okhttp3.Interceptor import org.matrix.android.sdk.api.Matrix import javax.inject.Inject import javax.inject.Singleton @@ -41,29 +43,43 @@ class VectorFlipperProxy @Inject constructor( ) : FlipperProxy { private val networkFlipperPlugin = NetworkFlipperPlugin() + private val isEnabled: Boolean + get() { + // https://github.com/facebook/flipper/issues/3572 + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) { + return false + } + + return FlipperUtils.shouldEnableFlipper(context) + } + override fun init(matrix: Matrix) { + if (!isEnabled) return + SoLoader.init(context, false) - if (FlipperUtils.shouldEnableFlipper(context)) { - val client = AndroidFlipperClient.getInstance(context) - client.addPlugin(CrashReporterPlugin.getInstance()) - client.addPlugin(SharedPreferencesFlipperPlugin(context)) - client.addPlugin(InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())) - client.addPlugin(networkFlipperPlugin) - client.addPlugin( - DatabasesFlipperPlugin( - RealmDatabaseDriver( - context = context, - realmDatabaseProvider = object : RealmDatabaseProvider { - override fun getRealmConfigurations(): List { - 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 ea9b4b5999..9ce87199b0 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 97590028d8..2242abb7aa 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 a3e8b3780c..d16bf67a34 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 0000000000..f4b97b9f9c --- /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 0000000000..88cad2ab0b --- /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 0000000000..2b85b6882a --- /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 8536b765d4..55805a0728 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 0000000000..ec45e226be --- /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 1a8e10d102..9692777e15 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 55ec922a57..afa9c84353 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 0000000000..3aec5b5c20 --- /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 0000000000..478f631c06 --- /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 4e6b4fc3df..d989abc214 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 f6c30040d9..7b704a6331 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 + + + + - - - -