From fb10bd530d46b1586356eb2c129cc4263c56a94e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 26 Aug 2024 14:23:07 +0200 Subject: [PATCH 1/4] Remove login with QR code feature. --- .../sdk/api/rendezvous/RendezvousTest.kt | 110 -------- .../android/sdk/api/rendezvous/Rendezvous.kt | 254 ------------------ .../sdk/api/rendezvous/RendezvousChannel.kt | 51 ---- .../api/rendezvous/RendezvousFailureReason.kt | 32 --- .../sdk/api/rendezvous/RendezvousTransport.kt | 36 --- .../channels/ECDHRendezvousChannel.kt | 199 -------------- .../api/rendezvous/model/ECDHRendezvous.kt | 26 -- .../rendezvous/model/ECDHRendezvousCode.kt | 25 -- .../sdk/api/rendezvous/model/Outcome.kt | 38 --- .../sdk/api/rendezvous/model/Payload.kt | 36 --- .../sdk/api/rendezvous/model/PayloadType.kt | 32 --- .../sdk/api/rendezvous/model/Protocol.kt | 26 -- .../sdk/api/rendezvous/model/Rendezvous.kt | 25 -- .../api/rendezvous/model/RendezvousCode.kt | 25 -- .../api/rendezvous/model/RendezvousError.kt | 21 -- .../api/rendezvous/model/RendezvousIntent.kt | 26 -- .../model/RendezvousTransportDetails.kt | 24 -- .../model/RendezvousTransportType.kt | 26 -- .../model/SecureRendezvousChannelAlgorithm.kt | 28 -- .../SimpleHttpRendezvousTransportDetails.kt | 24 -- .../SimpleHttpRendezvousTransport.kt | 173 ------------ .../features/DebugFeaturesStateFactory.kt | 15 -- .../debug/features/DebugVectorFeatures.kt | 12 - vector/src/main/AndroidManifest.xml | 1 - .../app/core/di/MavericksViewModelModule.kt | 6 - .../im/vector/app/features/VectorFeatures.kt | 6 - .../features/login/qr/QrCodeLoginAction.kt | 26 -- .../features/login/qr/QrCodeLoginActivity.kt | 130 --------- .../app/features/login/qr/QrCodeLoginArgs.kt | 26 -- .../login/qr/QrCodeLoginConnectionStatus.kt | 26 -- .../login/qr/QrCodeLoginHeaderView.kt | 81 ------ .../qr/QrCodeLoginInstructionsFragment.kt | 105 -------- .../login/qr/QrCodeLoginInstructionsView.kt | 79 ------ .../login/qr/QrCodeLoginShowQrCodeFragment.kt | 82 ------ .../login/qr/QrCodeLoginStatusFragment.kt | 148 ---------- .../app/features/login/qr/QrCodeLoginType.kt | 22 -- .../login/qr/QrCodeLoginViewEvents.kt | 26 -- .../features/login/qr/QrCodeLoginViewModel.kt | 166 ------------ .../features/login/qr/QrCodeLoginViewState.kt | 30 --- .../features/navigation/DefaultNavigator.kt | 10 - .../app/features/navigation/Navigator.kt | 6 - .../onboarding/OnboardingViewModel.kt | 24 -- .../onboarding/OnboardingViewState.kt | 2 - .../ftueauth/FtueAuthCombinedLoginFragment.kt | 22 -- .../v2/VectorSettingsDevicesFragment.kt | 35 --- .../layout/fragment_ftue_combined_login.xml | 16 +- .../fragment_qr_code_login_instructions.xml | 84 ------ .../fragment_qr_code_login_show_qr_code.xml | 53 ---- .../layout/fragment_qr_code_login_status.xml | 155 ----------- .../res/layout/fragment_settings_devices.xml | 42 --- .../onboarding/OnboardingViewModelTest.kt | 22 -- 51 files changed, 1 insertion(+), 2694 deletions(-) delete mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/rendezvous/RendezvousTest.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Rendezvous.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousCode.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt delete mode 100644 vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt delete mode 100644 vector/src/main/res/layout/fragment_qr_code_login_instructions.xml delete mode 100644 vector/src/main/res/layout/fragment_qr_code_login_show_qr_code.xml delete mode 100644 vector/src/main/res/layout/fragment_qr_code_login_status.xml diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/rendezvous/RendezvousTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/rendezvous/RendezvousTest.kt deleted file mode 100644 index 5b5aad4c511..00000000000 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/rendezvous/RendezvousTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2023 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 - -import org.amshove.kluent.invoking -import org.amshove.kluent.shouldBeEqualTo -import org.amshove.kluent.shouldBeInstanceOf -import org.amshove.kluent.shouldThrow -import org.amshove.kluent.with -import org.junit.Test -import org.matrix.android.sdk.InstrumentedTest -import org.matrix.android.sdk.api.rendezvous.channels.ECDHRendezvousChannel -import org.matrix.android.sdk.api.rendezvous.model.RendezvousError -import org.matrix.android.sdk.common.CommonTestHelper - -class RendezvousTest : InstrumentedTest { - - @Test - fun shouldSuccessfullyBuildChannels() = CommonTestHelper.runCryptoTest(context()) { _, _ -> - val cases = listOf( - // v1: - "{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256\"," + - "\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" + - "{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," + - "\"intent\":\"login.reciprocate\"}", - // v2: - "{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256\"," + - "\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" + - "{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," + - "\"intent\":\"login.reciprocate\"}", - ) - - cases.forEach { input -> - Rendezvous.buildChannelFromCode(input).channel shouldBeInstanceOf ECDHRendezvousChannel::class - } - } - - @Test - fun shouldFailToBuildChannelAsUnsupportedAlgorithm() { - invoking { - Rendezvous.buildChannelFromCode( - "{\"rendezvous\":{\"algorithm\":\"bad algo\"," + - "\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" + - "{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," + - "\"intent\":\"login.reciprocate\"}" - ) - } shouldThrow RendezvousError::class with { - this.reason shouldBeEqualTo RendezvousFailureReason.UnsupportedAlgorithm - } - } - - @Test - fun shouldFailToBuildChannelAsUnsupportedTransport() { - invoking { - Rendezvous.buildChannelFromCode( - "{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256\"," + - "\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" + - "{\"type\":\"bad transport\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," + - "\"intent\":\"login.reciprocate\"}" - ) - } shouldThrow RendezvousError::class with { - this.reason shouldBeEqualTo RendezvousFailureReason.UnsupportedTransport - } - } - - @Test - fun shouldFailToBuildChannelWithInvalidIntent() { - invoking { - Rendezvous.buildChannelFromCode( - "{\"rendezvous\":{\"algorithm\":\"org.matrix.msc3903.rendezvous.v1.curve25519-aes-sha256\"," + - "\"key\":\"aeSGwYTV1IUhikUyCapzC6p2xG5NpJ4Lwj2UgUMlcTk\",\"transport\":" + - "{\"type\":\"org.matrix.msc3886.http.v1\",\"uri\":\"https://rendezvous.lab.element.dev/bcab62cd-3e34-48b4-bc39-90895da8f6fe\"}}," + - "\"intent\":\"foo\"}" - ) - } shouldThrow RendezvousError::class with { - this.reason shouldBeEqualTo RendezvousFailureReason.InvalidCode - } - } - - @Test - fun shouldFailToBuildChannelAsInvalidCode() { - val cases = listOf( - "{}", - "rubbish", - "" - ) - - cases.forEach { input -> - invoking { - Rendezvous.buildChannelFromCode(input) - } shouldThrow RendezvousError::class with { - this.reason shouldBeEqualTo RendezvousFailureReason.InvalidCode - } - } - } -} 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 deleted file mode 100644 index 5bceecf643d..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/Rendezvous.kt +++ /dev/null @@ -1,254 +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 - -import android.net.Uri -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.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.RendezvousCode -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.RendezvousTransportType -import org.matrix.android.sdk.api.rendezvous.model.SecureRendezvousChannelAlgorithm -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.util.MatrixJsonParser -import timber.log.Timber - -// n.b MSC3886/MSC3903/MSC3906 that this is based on are now closed. -// However, we want to keep this implementation around for some time. -// TODO define an end-of-life date for this implementation. - -/** - * Implementation of MSC3906 to sign in + E2EE set up using a QR code. - */ -class Rendezvous( - val channel: RendezvousChannel, - val theirIntent: RendezvousIntent, -) { - companion object { - private val TAG = LoggerTag(Rendezvous::class.java.simpleName, LoggerTag.RENDEZVOUS).value - - @Throws(RendezvousError::class) - fun buildChannelFromCode(code: String): Rendezvous { - // we first check that the code is valid JSON and has right high-level structure - val genericParsed = try { - // we rely on moshi validating the code and throwing exception if invalid JSON or algorithm doesn't match - MatrixJsonParser.getMoshi().adapter(RendezvousCode::class.java).fromJson(code) - } catch (a: Throwable) { - throw RendezvousError("Malformed code", RendezvousFailureReason.InvalidCode) - } ?: throw RendezvousError("Code is null", RendezvousFailureReason.InvalidCode) - - // then we check that algorithm is supported - if (!SecureRendezvousChannelAlgorithm.values().map { it.value }.contains(genericParsed.rendezvous.algorithm)) { - throw RendezvousError("Unsupported algorithm", RendezvousFailureReason.UnsupportedAlgorithm) - } - - // and, that the transport is supported - if (!RendezvousTransportType.values().map { it.value }.contains(genericParsed.rendezvous.transport.type)) { - throw RendezvousError("Unsupported transport", RendezvousFailureReason.UnsupportedTransport) - } - - // now that we know the overall structure looks sensible, we rely on moshi validating the code and - // throwing exception if other parts are invalid - val supportedParsed = try { - MatrixJsonParser.getMoshi().adapter(ECDHRendezvousCode::class.java).fromJson(code) - } catch (a: Throwable) { - throw RendezvousError("Malformed ECDH rendezvous code", RendezvousFailureReason.InvalidCode) - } ?: throw RendezvousError("ECDH rendezvous code is null", RendezvousFailureReason.InvalidCode) - - val transport = SimpleHttpRendezvousTransport(supportedParsed.rendezvous.transport.uri) - - return Rendezvous( - ECDHRendezvousChannel(transport, supportedParsed.rendezvous.algorithm, supportedParsed.rendezvous.key), - supportedParsed.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 - - @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)) - if (ourIntent == RendezvousIntent.LOGIN_ON_NEW_DEVICE) { - throw RendezvousError("The other device isn't signed in", RendezvousFailureReason.OtherDeviceNotSignedIn) - } else { - throw RendezvousError("The other device is already signed in", RendezvousFailureReason.OtherDeviceAlreadySignedIn) - } - } - } - - @Throws(RendezvousError::class) - suspend fun startAfterScanningCode(): String { - val checksum = channel.connect() - - Timber.tag(TAG).i("Connected to secure channel with checksum: $checksum") - - checkCompatibility() - - // get protocols - Timber.tag(TAG).i("Waiting for protocols") - val protocolsResponse = receive() - - if (protocolsResponse?.protocols == null || !protocolsResponse.protocols.contains(Protocol.LOGIN_TOKEN)) { - send(Payload(PayloadType.FINISH, outcome = Outcome.UNSUPPORTED)) - throw RendezvousError("Unsupported protocols", RendezvousFailureReason.UnsupportedHomeserver) - } - - send(Payload(PayloadType.PROGRESS, protocol = Protocol.LOGIN_TOKEN)) - - return checksum - } - - @Throws(RendezvousError::class) - 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) { - Outcome.DECLINED -> { - throw RendezvousError("Login declined by other device", RendezvousFailureReason.UserDeclined) - } - Outcome.UNSUPPORTED -> { - throw RendezvousError("Homeserver lacks support", RendezvousFailureReason.UnsupportedHomeserver) - } - else -> { - throw RendezvousError("Unknown error", RendezvousFailureReason.Unknown) - } - } - } - - 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") - - val hsConfig = HomeServerConnectionConfig(homeServerUri = Uri.parse(homeserver)) - return authenticationService.loginUsingQrLoginToken(hsConfig, token) - } - - @Throws(RendezvousError::class) - suspend fun completeVerificationOnNewDevice(session: Session) { - val userId = session.myUserId - val crypto = session.cryptoService() - val deviceId = crypto.getMyCryptoDevice().deviceId - val deviceKey = crypto.getMyCryptoDevice().fingerprint() - send(Payload(PayloadType.PROGRESS, outcome = Outcome.SUCCESS, deviceId = deviceId, deviceKey = deviceKey)) - - try { - // explicitly download keys for ourself rather than racing with initial sync which might not complete in time - crypto.downloadKeysIfNeeded(listOf(userId), false) - } catch (e: Throwable) { - // log as warning and continue as initial sync might still complete - Timber.tag(TAG).w(e, "Failed to download keys for self") - } - - // await confirmation of verification - val verificationResponse = receive() - 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) - } - - verificationResponse.masterKey?.let { masterKeyFromVerifyingDevice -> - // verifying device provided us with a master key, so use it to check integrity - - // see what the homeserver told us - val localMasterKey = crypto.crossSigningService().getMyCrossSigningKeys()?.masterKey() - - // n.b. if no local master key this is a problem, as well as it not matching - if (localMasterKey?.unpaddedBase64PublicKey != masterKeyFromVerifyingDevice) { - Timber.tag(TAG).w("Master key from verifying device doesn't match: $masterKeyFromVerifyingDevice vs $localMasterKey") - // inform the other side - send(Payload(PayloadType.FINISH, outcome = Outcome.E2EE_SECURITY_ERROR)) - throw RendezvousError("Master key from verifying device doesn't match", RendezvousFailureReason.E2EESecurityIssue) - } - - // set other device as verified - Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) - - Timber.tag(TAG).i("Setting master key as trusted") - crypto.crossSigningService().markMyMasterKeyAsTrusted() - } ?: run { - // set other device as verified anyway - Timber.tag(TAG).i("Setting device $verifyingDeviceId as verified") - crypto.setDeviceVerification(DeviceTrustLevel(locallyVerified = true, crossSigningVerified = false), userId, verifyingDeviceId) - - Timber.tag(TAG).i("No master key given by verifying device") - } - - // request secrets from other sessions. - Timber.tag(TAG).i("Requesting secrets from other sessions") - - session.sharedSecretStorageService().requestMissingSecrets() - } else { - Timber.tag(TAG).i("Not doing verification") - } - } - - @Throws(RendezvousError::class) - private suspend fun receive(): Payload? { - val data = channel.receive() ?: return null - 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 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 deleted file mode 100644 index 0956a5b0a0a..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousChannel.kt +++ /dev/null @@ -1,51 +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 - -import org.matrix.android.sdk.api.rendezvous.model.RendezvousError - -/** - * Representation of a rendezvous channel such as that described by MSC3903. - */ -interface RendezvousChannel { - val transport: RendezvousTransport - - /** - * @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? - - /** - * Closes the channel and cleans up. - */ - suspend fun close() -} 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 deleted file mode 100644 index 18e625d8259..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousFailureReason.kt +++ /dev/null @@ -1,32 +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 - -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 deleted file mode 100644 index 81632e951a7..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/RendezvousTransport.kt +++ /dev/null @@ -1,36 +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 - -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 - - @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() -} 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 deleted file mode 100644 index bcde4a2a7f4..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/channels/ECDHRendezvousChannel.kt +++ /dev/null @@ -1,199 +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.channels - -import android.util.Base64 -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 -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.SecureRendezvousChannelAlgorithm -import org.matrix.android.sdk.api.util.MatrixJsonParser -import org.matrix.android.sdk.internal.crypto.verification.getDecimalCodeRepresentation -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 - -/** - * 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, - private val algorithm: SecureRendezvousChannelAlgorithm, - 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 - } - - @JsonClass(generateAdapter = true) - internal data class ECDHPayload( - val algorithm: SecureRendezvousChannelAlgorithm? = null, - val key: String? = null, - val ciphertext: String? = null, - val iv: String? = null, - ) - - private val olmSASMutex = Mutex() - 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 = decodeBase64(it) - } - olmSAS = OlmSAS() - ourPublicKey = decodeBase64(olmSAS!!.publicKey) - } - - fun encodeBase64(input: ByteArray?): String? { - if (algorithm == SecureRendezvousChannelAlgorithm.ECDH_V2) { - return Base64.encodeToString(input, Base64.NO_WRAP or Base64.NO_PADDING) - } - return Base64.encodeToString(input, Base64.NO_WRAP) - } - - fun decodeBase64(input: String?): ByteArray { - // for decoding we aren't concerned about padding - return Base64.decode(input, Base64.NO_WRAP) - } - - @Throws(RendezvousError::class) - override suspend fun connect(): String { - 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 (res.key == null) { - throw RendezvousError( - "Unsupported algorithm: ${res.algorithm}", - RendezvousFailureReason.UnsupportedAlgorithm, - ) - } - theirPublicKey = decodeBase64(res.key) - } else { - // send our public key unencrypted - Timber.tag(TAG).i("Sending public key") - send( - ECDHPayload( - algorithm = algorithm, - key = encodeBase64(ourPublicKey) - ) - ) - } - - olmSASMutex.withLock { - sas.setTheirPublicKey(encodeBase64(theirPublicKey)) - sas.setTheirPublicKey(encodeBase64(theirPublicKey)) - - val initiatorKey = encodeBase64(if (isInitiator) ourPublicKey else theirPublicKey) - val recipientKey = encodeBase64(if (isInitiator) theirPublicKey else ourPublicKey) - val aesInfo = "${algorithm.value}|$initiatorKey|$recipientKey" - - aesKey = sas.generateShortCode(aesInfo, 32) - - val rawChecksum = sas.generateShortCode(aesInfo, 5) - return rawChecksum.getDecimalCodeRepresentation(separator = "-") - } - } - - 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 IllegalStateException("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 IllegalStateException("Shared secret not established") - } - val payload = receiveAsPayload() ?: return null - return decrypt(payload) - } - - override suspend fun close() { - 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() - } - - private fun encrypt(plainText: ByteArray): ECDHPayload { - 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 = encodeBase64(cipherText.toByteArray()), - iv = encodeBase64(iv) - ) - } - - private fun decrypt(payload: ECDHPayload): ByteArray { - val iv = decodeBase64(payload.iv) - 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(decodeBase64(payload.ciphertext)).toList()) - plainText.addAll(encryptCipher.doFinal().toList()) - - return plainText.toByteArray() - } -} 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 deleted file mode 100644 index 55bac6397e5..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvous.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.JsonClass - -@JsonClass(generateAdapter = true) -data class ECDHRendezvous( - 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 deleted file mode 100644 index 575b5d4bfd3..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/ECDHRendezvousCode.kt +++ /dev/null @@ -1,25 +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.JsonClass - -@JsonClass(generateAdapter = true) -data class ECDHRendezvousCode( - val intent: RendezvousIntent, - val rendezvous: ECDHRendezvous -) 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 deleted file mode 100644 index 0ebd1f88b34..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Outcome.kt +++ /dev/null @@ -1,38 +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 = false) -enum class Outcome(val value: String) { - @Json(name = "success") - SUCCESS("success"), - - @Json(name = "declined") - DECLINED("declined"), - - @Json(name = "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/Payload.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt deleted file mode 100644 index 04631ce9599..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Payload.kt +++ /dev/null @@ -1,36 +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) -internal data class Payload( - 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, - @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 deleted file mode 100644 index 33beb1f5250..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/PayloadType.kt +++ /dev/null @@ -1,32 +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 = false) -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/Protocol.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.kt deleted file mode 100644 index 6fce2fa11c4..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Protocol.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 = false) -enum class Protocol(val value: String) { - @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/Rendezvous.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Rendezvous.kt deleted file mode 100644 index f424f8cab0f..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/Rendezvous.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2023 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.JsonClass - -@JsonClass(generateAdapter = true) -open class Rendezvous( - val transport: RendezvousTransportDetails, - val algorithm: String, -) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousCode.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousCode.kt deleted file mode 100644 index ffa8bf66610..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousCode.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2023 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.JsonClass - -@JsonClass(generateAdapter = true) -open class RendezvousCode( - open val intent: RendezvousIntent, - open val rendezvous: Rendezvous -) 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 deleted file mode 100644 index c52b11a3226..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousError.kt +++ /dev/null @@ -1,21 +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 org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason - -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/model/RendezvousIntent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.kt deleted file mode 100644 index 65037e1252e..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousIntent.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 = 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/RendezvousTransportDetails.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt deleted file mode 100644 index 34d96ac64a5..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportDetails.kt +++ /dev/null @@ -1,24 +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.JsonClass - -@JsonClass(generateAdapter = true) -open class RendezvousTransportDetails( - val type: String -) 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 deleted file mode 100644 index 6fca7efa714..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/RendezvousTransportType.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 = false) -enum class RendezvousTransportType(val value: String) { - @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 deleted file mode 100644 index 123e41a5d73..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SecureRendezvousChannelAlgorithm.kt +++ /dev/null @@ -1,28 +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 = 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"), - @Json(name = "org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256") - ECDH_V2("org.matrix.msc3903.rendezvous.v2.curve25519-aes-sha256") -} 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 deleted file mode 100644 index d2342bb9d54..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/model/SimpleHttpRendezvousTransportDetails.kt +++ /dev/null @@ -1,24 +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.JsonClass - -@JsonClass(generateAdapter = true) -data class SimpleHttpRendezvousTransportDetails( - val uri: String -) : RendezvousTransportDetails(type = RendezvousTransportType.MSC3886_SIMPLE_HTTP_V1.name) 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 deleted file mode 100644 index 620b599e3df..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/rendezvous/transports/SimpleHttpRendezvousTransport.kt +++ /dev/null @@ -1,173 +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.transports - -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.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 -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 - */ -class SimpleHttpRendezvousTransport(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? - 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) - } - - @Throws(RendezvousError::class) - override suspend fun send(contentType: MediaType, data: ByteArray) { - if (cancelled) { - throw IllegalStateException("Rendezvous cancelled") - } - - val method = if (uri != null) "PUT" else "POST" - val uri = this.uri ?: throw RuntimeException("No rendezvous 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) { - throw get404Error() - } - 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", Locale.US) - expiresAt = format.parse(it) - } - - // resolve location header which could be relative or absolute - this.uri = response.request.url.toUri().resolve(location).toString() - ready = true - } - } - - @Throws(RendezvousError::class) - 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) { - 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 { - // expired - if (response.code == 404) { - throw get404Error() - } - - // 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 - } - return response.body?.bytes() - } - - // 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() - } - } - } - - private fun get404Error(): RendezvousError { - if (expiresAt != null && Date() > expiresAt) { - return RendezvousError("Expired", RendezvousFailureReason.Expired) - } - - return RendezvousError("Received unexpected 404", RendezvousFailureReason.Unknown) - } - - override suspend fun close() { - cancelled = true - ready = false - - 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-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 16e26ff3b58..5f34a349d68 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 @@ -85,21 +85,6 @@ class DebugFeaturesStateFactory @Inject constructor( key = DebugFeatureKeys.newAppLayoutEnabled, factory = VectorFeatures::isNewAppLayoutFeatureEnabled ), - createBooleanFeature( - label = "Enable QR Code Login", - key = DebugFeatureKeys.qrCodeLoginEnabled, - factory = VectorFeatures::isQrCodeLoginEnabled - ), - createBooleanFeature( - label = "Allow QR Code Login for all servers", - key = DebugFeatureKeys.qrCodeLoginForAllServers, - factory = VectorFeatures::isQrCodeLoginForAllServers - ), - createBooleanFeature( - label = "Show QR Code Login in Device Manager", - key = DebugFeatureKeys.reciprocateQrCodeLogin, - factory = VectorFeatures::isReciprocateQrCodeLogin - ), 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 2134c8cf2c5..ffaf462244d 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 @@ -76,15 +76,6 @@ class DebugVectorFeatures( override fun isNewAppLayoutFeatureEnabled(): Boolean = read(DebugFeatureKeys.newAppLayoutEnabled) ?: vectorFeatures.isNewAppLayoutFeatureEnabled() - override fun isQrCodeLoginEnabled() = read(DebugFeatureKeys.qrCodeLoginEnabled) - ?: vectorFeatures.isQrCodeLoginEnabled() - - override fun isQrCodeLoginForAllServers() = read(DebugFeatureKeys.qrCodeLoginForAllServers) - ?: vectorFeatures.isQrCodeLoginForAllServers() - - override fun isReciprocateQrCodeLogin() = read(DebugFeatureKeys.reciprocateQrCodeLogin) - ?: vectorFeatures.isReciprocateQrCodeLogin() - override fun isVoiceBroadcastEnabled(): Boolean = read(DebugFeatureKeys.voiceBroadcastEnabled) ?: vectorFeatures.isVoiceBroadcastEnabled() @@ -150,9 +141,6 @@ object DebugFeatureKeys { val screenSharing = booleanPreferencesKey("screen-sharing") val forceUsageOfOpusEncoder = booleanPreferencesKey("force-usage-of-opus-encoder") val newAppLayoutEnabled = booleanPreferencesKey("new-app-layout-enabled") - val qrCodeLoginEnabled = booleanPreferencesKey("qr-code-login-enabled") - val qrCodeLoginForAllServers = booleanPreferencesKey("qr-code-login-for-all-servers") - val reciprocateQrCodeLogin = booleanPreferencesKey("reciprocate-qr-code-login") val voiceBroadcastEnabled = booleanPreferencesKey("voice-broadcast-enabled") val unverifiedSessionsAlertEnabled = booleanPreferencesKey("unverified-sessions-alert-enabled") } diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 6fed67e7da1..d843f03216b 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -349,7 +349,6 @@ - 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 8751907f2ab..748e672c52b 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 @@ -61,7 +61,6 @@ import im.vector.app.features.location.LocationSharingViewModel import im.vector.app.features.location.live.map.LiveLocationMapViewModel import im.vector.app.features.location.preview.LocationPreviewViewModel import im.vector.app.features.login.LoginViewModel -import im.vector.app.features.login.qr.QrCodeLoginViewModel import im.vector.app.features.matrixto.MatrixToBottomSheetViewModel import im.vector.app.features.media.VectorAttachmentViewerViewModel import im.vector.app.features.onboarding.OnboardingViewModel @@ -668,11 +667,6 @@ interface MavericksViewModelModule { @MavericksViewModelKey(RenameSessionViewModel::class) fun renameSessionViewModelFactory(factory: RenameSessionViewModel.Factory): MavericksAssistedViewModelFactory<*, *> - @Binds - @IntoMap - @MavericksViewModelKey(QrCodeLoginViewModel::class) - fun qrCodeLoginViewModelFactory(factory: QrCodeLoginViewModel.Factory): MavericksAssistedViewModelFactory<*, *> - @Binds @IntoMap @MavericksViewModelKey(SessionLearnMoreViewModel::class) 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 99abc15f81f..65c1d036554 100644 --- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -40,9 +40,6 @@ interface VectorFeatures { * use [VectorPreferences.isNewAppLayoutEnabled] instead. */ fun isNewAppLayoutFeatureEnabled(): Boolean - fun isQrCodeLoginEnabled(): Boolean - fun isQrCodeLoginForAllServers(): Boolean - fun isReciprocateQrCodeLogin(): Boolean fun isVoiceBroadcastEnabled(): Boolean fun isUnverifiedSessionsAlertEnabled(): Boolean } @@ -60,9 +57,6 @@ class DefaultVectorFeatures : VectorFeatures { override fun isLocationSharingEnabled() = Config.ENABLE_LOCATION_SHARING override fun forceUsageOfOpusEncoder(): Boolean = false override fun isNewAppLayoutFeatureEnabled(): Boolean = true - override fun isQrCodeLoginEnabled(): Boolean = true - override fun isQrCodeLoginForAllServers(): Boolean = false - override fun isReciprocateQrCodeLogin(): Boolean = false override fun isVoiceBroadcastEnabled(): Boolean = true override fun isUnverifiedSessionsAlertEnabled(): Boolean = true } 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 deleted file mode 100644 index 5ea46d3dcd2..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginAction.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.login.qr - -import im.vector.app.core.platform.VectorViewModelAction - -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 deleted file mode 100644 index 8eb8fd1ddc1..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt +++ /dev/null @@ -1,130 +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 - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.View -import com.airbnb.mvrx.Mavericks -import com.airbnb.mvrx.viewModel -import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.core.extensions.addFragment -import im.vector.app.core.extensions.replaceFragment -import im.vector.app.core.platform.SimpleFragmentActivity -import im.vector.app.features.home.HomeActivity -import im.vector.lib.core.utils.compat.getParcelableCompat -import timber.log.Timber - -// n.b MSC3886/MSC3903/MSC3906 that this is based on are now closed. -// However, we want to keep this implementation around for some time. -// TODO define an end-of-life date for this implementation. - -@AndroidEntryPoint -class QrCodeLoginActivity : SimpleFragmentActivity() { - - private val viewModel: QrCodeLoginViewModel by viewModel() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - views.toolbar.visibility = View.GONE - - if (isFirstCreation()) { - 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) { - replaceFragment( - views.container, - QrCodeLoginInstructionsFragment::class.java, - qrCodeLoginArgs, - tag = FRAGMENT_QR_CODE_INSTRUCTIONS_TAG - ) - } - - private fun observeViewEvents() { - viewModel.observeViewEvents { - when (it) { - QrCodeLoginViewEvents.NavigateToStatusScreen -> handleNavigateToStatusScreen() - QrCodeLoginViewEvents.NavigateToShowQrCodeScreen -> handleNavigateToShowQrCodeScreen() - QrCodeLoginViewEvents.NavigateToHomeScreen -> handleNavigateToHomeScreen() - QrCodeLoginViewEvents.NavigateToInitialScreen -> handleNavigateToInitialScreen() - } - } - } - - private fun handleNavigateToInitialScreen() { - navigateToInitialFragment() - } - - private fun handleNavigateToShowQrCodeScreen() { - addFragment( - views.container, - QrCodeLoginShowQrCodeFragment::class.java, - tag = FRAGMENT_SHOW_QR_CODE_TAG - ) - } - - private fun handleNavigateToStatusScreen() { - addFragment( - views.container, - QrCodeLoginStatusFragment::class.java, - tag = FRAGMENT_QR_CODE_STATUS_TAG - ) - } - - 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" - private const val FRAGMENT_SHOW_QR_CODE_TAG = "FRAGMENT_SHOW_QR_CODE_TAG" - private const val FRAGMENT_QR_CODE_STATUS_TAG = "FRAGMENT_QR_CODE_STATUS_TAG" - - fun getIntent(context: Context, qrCodeLoginArgs: QrCodeLoginArgs): Intent { - return Intent(context, QrCodeLoginActivity::class.java).apply { - putExtra(Mavericks.KEY_ARG, qrCodeLoginArgs) - } - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt deleted file mode 100644 index 6c23d07c0f6..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginArgs.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.login.qr - -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize -data class QrCodeLoginArgs( - val loginType: QrCodeLoginType, - val showQrCodeImmediately: Boolean, -) : Parcelable diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt deleted file mode 100644 index 4bef41b6c1a..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginConnectionStatus.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.login.qr - -import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason - -sealed class QrCodeLoginConnectionStatus { - object ConnectingToDevice : QrCodeLoginConnectionStatus() - data class Connected(val securityCode: String, val canConfirmSecurityCode: Boolean) : QrCodeLoginConnectionStatus() - object SigningIn : QrCodeLoginConnectionStatus() - data class Failed(val errorType: RendezvousFailureReason, val canTryAgain: Boolean) : QrCodeLoginConnectionStatus() -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt deleted file mode 100644 index 05c7bdffad3..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginHeaderView.kt +++ /dev/null @@ -1,81 +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 - -import android.content.Context -import android.content.res.ColorStateList -import android.content.res.TypedArray -import android.util.AttributeSet -import android.view.LayoutInflater -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.content.res.use -import im.vector.app.core.extensions.setTextOrHide -import im.vector.app.databinding.ViewQrCodeLoginHeaderBinding - -class QrCodeLoginHeaderView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr) { - - private val binding = ViewQrCodeLoginHeaderBinding.inflate( - LayoutInflater.from(context), - this - ) - - init { - context.obtainStyledAttributes( - attrs, - im.vector.lib.ui.styles.R.styleable.QrCodeLoginHeaderView, - 0, - 0 - ).use { - setTitle(it) - setDescription(it) - setImage(it) - } - } - - private fun setTitle(typedArray: TypedArray) { - val title = typedArray.getString(im.vector.lib.ui.styles.R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderTitle) - setTitle(title) - } - - private fun setDescription(typedArray: TypedArray) { - val description = typedArray.getString(im.vector.lib.ui.styles.R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderDescription) - setDescription(description) - } - - private fun setImage(typedArray: TypedArray) { - val imageResource = typedArray.getResourceId(im.vector.lib.ui.styles.R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderImageResource, 0) - val backgroundTint = typedArray.getColor(im.vector.lib.ui.styles.R.styleable.QrCodeLoginHeaderView_qrCodeLoginHeaderImageBackgroundTint, 0) - setImage(imageResource, backgroundTint) - } - - fun setTitle(title: String?) { - binding.qrCodeLoginHeaderTitleTextView.setTextOrHide(title) - } - - fun setDescription(description: String?) { - binding.qrCodeLoginHeaderDescriptionTextView.setTextOrHide(description) - } - - fun setImage(imageResource: Int, backgroundTintColor: Int) { - binding.qrCodeLoginHeaderImageView.setImageResource(imageResource) - binding.qrCodeLoginHeaderImageView.backgroundTintList = ColorStateList.valueOf(backgroundTintColor) - } -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt deleted file mode 100644 index f78cae5d22e..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsFragment.kt +++ /dev/null @@ -1,105 +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 - -import android.app.Activity -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.airbnb.mvrx.activityViewModel -import com.airbnb.mvrx.withState -import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.core.extensions.registerStartForActivityResult -import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.databinding.FragmentQrCodeLoginInstructionsBinding -import im.vector.app.features.qrcode.QrCodeScannerActivity -import im.vector.lib.strings.CommonStrings -import timber.log.Timber - -@AndroidEntryPoint -class QrCodeLoginInstructionsFragment : VectorBaseFragment() { - - private val viewModel: QrCodeLoginViewModel by activityViewModel() - - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginInstructionsBinding { - return FragmentQrCodeLoginInstructionsBinding.inflate(inflater, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - initScanQrCodeButton() - initShowQrCodeButton() - } - - private fun initShowQrCodeButton() { - views.qrCodeLoginInstructionsShowQrCodeButton.debouncedClicks { - viewModel.handle(QrCodeLoginAction.ShowQrCode) - } - } - - private fun initScanQrCodeButton() { - views.qrCodeLoginInstructionsScanQrCodeButton.debouncedClicks { - QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher) - } - } - - private val scanActivityResultLauncher = registerStartForActivityResult { activityResult -> - if (activityResult.resultCode == Activity.RESULT_OK) { - val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data) - val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data) - - Timber.d("Scanned QR code: $scannedQrCode, was QR code: $wasQrCode") - if (wasQrCode && !scannedQrCode.isNullOrBlank()) { - onQrCodeScanned(scannedQrCode) - } else { - onQrCodeScannerFailed() - } - } - } - - private fun onQrCodeScanned(scannedQrCode: String) { - viewModel.handle(QrCodeLoginAction.OnQrCodeScanned(scannedQrCode)) - } - - private fun onQrCodeScannerFailed() { - // The user scanned something unexpected, so we try scanning again. - // This seems to happen particularly with the large QRs needed for rendezvous - // especially when the QR is partially off the screen - Timber.d("QrCodeLoginInstructionsFragment.onQrCodeScannerFailed - showing scanner again") - QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher) - } - - override fun invalidate() = withState(viewModel) { state -> - if (state.loginType == QrCodeLoginType.LOGIN) { - views.qrCodeLoginInstructionsView.setInstructions( - listOf( - getString(CommonStrings.qr_code_login_new_device_instruction_1), - getString(CommonStrings.qr_code_login_new_device_instruction_2), - getString(CommonStrings.qr_code_login_new_device_instruction_3), - ) - ) - } else { - views.qrCodeLoginInstructionsView.setInstructions( - listOf( - getString(CommonStrings.qr_code_login_link_a_device_scan_qr_code_instruction_1), - getString(CommonStrings.qr_code_login_link_a_device_scan_qr_code_instruction_2), - ) - ) - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt deleted file mode 100644 index bed4efc9528..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginInstructionsView.kt +++ /dev/null @@ -1,79 +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 - -import android.content.Context -import android.content.res.TypedArray -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.LinearLayout -import android.widget.TextView -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.content.res.use -import androidx.core.view.isVisible -import im.vector.app.databinding.ViewQrCodeLoginInstructionsBinding - -class QrCodeLoginInstructionsView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr) { - - private val binding = ViewQrCodeLoginInstructionsBinding.inflate( - LayoutInflater.from(context), - this - ) - - init { - context.obtainStyledAttributes( - attrs, - im.vector.lib.ui.styles.R.styleable.QrCodeLoginInstructionsView, - 0, - 0 - ).use { - setInstructions(it) - } - } - - private fun setInstructions(typedArray: TypedArray) { - val instruction1 = typedArray.getString(im.vector.lib.ui.styles.R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction1) - val instruction2 = typedArray.getString(im.vector.lib.ui.styles.R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction2) - val instruction3 = typedArray.getString(im.vector.lib.ui.styles.R.styleable.QrCodeLoginInstructionsView_qrCodeLoginInstruction3) - setInstructions( - listOf( - instruction1, - instruction2, - instruction3, - ) - ) - } - - fun setInstructions(instructions: List?) { - setInstruction(binding.instructions1Layout, binding.instruction1TextView, instructions?.getOrNull(0)) - setInstruction(binding.instructions2Layout, binding.instruction2TextView, instructions?.getOrNull(1)) - setInstruction(binding.instructions3Layout, binding.instruction3TextView, instructions?.getOrNull(2)) - } - - private fun setInstruction(instructionLayout: LinearLayout, instructionTextView: TextView, instruction: String?) { - instruction?.let { - instructionLayout.isVisible = true - instructionTextView.text = instruction - } ?: run { - instructionLayout.isVisible = false - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt deleted file mode 100644 index e84007bd43f..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginShowQrCodeFragment.kt +++ /dev/null @@ -1,82 +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 - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.airbnb.mvrx.activityViewModel -import com.airbnb.mvrx.withState -import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.databinding.FragmentQrCodeLoginShowQrCodeBinding -import im.vector.lib.strings.CommonStrings - -@AndroidEntryPoint -class QrCodeLoginShowQrCodeFragment : VectorBaseFragment() { - - private val viewModel: QrCodeLoginViewModel by activityViewModel() - - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginShowQrCodeBinding { - return FragmentQrCodeLoginShowQrCodeBinding.inflate(inflater, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - initCancelButton() - viewModel.handle(QrCodeLoginAction.GenerateQrCode) - } - - private fun initCancelButton() { - views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks { - activity?.onBackPressedDispatcher?.onBackPressed() - } - } - - private fun setInstructions(loginType: QrCodeLoginType) { - if (loginType == QrCodeLoginType.LOGIN) { - views.qrCodeLoginShowQrCodeHeaderView.setDescription(getString(CommonStrings.qr_code_login_header_show_qr_code_new_device_description)) - views.qrCodeLoginShowQrCodeInstructionsView.setInstructions( - listOf( - getString(CommonStrings.qr_code_login_new_device_instruction_1), - getString(CommonStrings.qr_code_login_new_device_instruction_2), - getString(CommonStrings.qr_code_login_new_device_instruction_3), - ) - ) - } else { - views.qrCodeLoginShowQrCodeHeaderView.setDescription(getString(CommonStrings.qr_code_login_header_show_qr_code_link_a_device_description)) - views.qrCodeLoginShowQrCodeInstructionsView.setInstructions( - listOf( - getString(CommonStrings.qr_code_login_link_a_device_show_qr_code_instruction_1), - getString(CommonStrings.qr_code_login_link_a_device_show_qr_code_instruction_2), - ) - ) - } - } - - private fun showQrCode(qrCodeData: String) { - views.qrCodeLoginSHowQrCodeImageView.setData(qrCodeData) - } - - override fun invalidate() = withState(viewModel) { state -> - state.generatedQrCodeData?.let { qrCodeData -> - showQrCode(qrCodeData) - } - setInstructions(state.loginType) - } -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt deleted file mode 100644 index dcfcd8b06f9..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginStatusFragment.kt +++ /dev/null @@ -1,148 +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 - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import com.airbnb.mvrx.activityViewModel -import com.airbnb.mvrx.withState -import dagger.hilt.android.AndroidEntryPoint -import im.vector.app.R -import im.vector.app.core.platform.VectorBaseFragment -import im.vector.app.databinding.FragmentQrCodeLoginStatusBinding -import im.vector.app.features.themes.ThemeUtils -import im.vector.lib.strings.CommonStrings -import org.matrix.android.sdk.api.rendezvous.RendezvousFailureReason - -@AndroidEntryPoint -class QrCodeLoginStatusFragment : VectorBaseFragment() { - - private val viewModel: QrCodeLoginViewModel by activityViewModel() - - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeLoginStatusBinding { - return FragmentQrCodeLoginStatusBinding.inflate(inflater, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - initCancelButton() - initTryAgainButton() - } - - private fun initTryAgainButton() { - views.qrCodeLoginStatusTryAgainButton.debouncedClicks { - viewModel.handle(QrCodeLoginAction.TryAgain) - } - } - - private fun initCancelButton() { - views.qrCodeLoginStatusCancelButton.debouncedClicks { - activity?.onBackPressedDispatcher?.onBackPressed() - } - } - - private fun handleFailed(connectionStatus: QrCodeLoginConnectionStatus.Failed) { - views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = false - views.qrCodeLoginStatusLoadingLayout.isVisible = false - views.qrCodeLoginStatusHeaderView.isVisible = true - views.qrCodeLoginStatusSecurityCode.isVisible = false - views.qrCodeLoginStatusNoMatchLayout.isVisible = false - views.qrCodeLoginStatusCancelButton.isVisible = true - views.qrCodeLoginStatusTryAgainButton.isVisible = connectionStatus.canTryAgain - views.qrCodeLoginStatusHeaderView.setTitle(getString(CommonStrings.qr_code_login_header_failed_title)) - views.qrCodeLoginStatusHeaderView.setDescription(getErrorDescription(connectionStatus.errorType)) - views.qrCodeLoginStatusHeaderView.setImage( - imageResource = R.drawable.ic_qr_code_login_failed, - backgroundTintColor = ThemeUtils.getColor(requireContext(), com.google.android.material.R.attr.colorError) - ) - } - - private fun getErrorDescription(reason: RendezvousFailureReason): String { - return when (reason) { - RendezvousFailureReason.UnsupportedAlgorithm, - RendezvousFailureReason.UnsupportedTransport -> getString(CommonStrings.qr_code_login_header_failed_device_is_not_supported_description) - RendezvousFailureReason.UnsupportedHomeserver -> getString(CommonStrings.qr_code_login_header_failed_homeserver_is_not_supported_description) - RendezvousFailureReason.Expired -> getString(CommonStrings.qr_code_login_header_failed_timeout_description) - RendezvousFailureReason.UserDeclined -> getString(CommonStrings.qr_code_login_header_failed_denied_description) - RendezvousFailureReason.E2EESecurityIssue -> getString(CommonStrings.qr_code_login_header_failed_e2ee_security_issue_description) - RendezvousFailureReason.OtherDeviceAlreadySignedIn -> - getString(CommonStrings.qr_code_login_header_failed_other_device_already_signed_in_description) - RendezvousFailureReason.OtherDeviceNotSignedIn -> getString(CommonStrings.qr_code_login_header_failed_other_device_not_signed_in_description) - RendezvousFailureReason.InvalidCode -> getString(CommonStrings.qr_code_login_header_failed_invalid_qr_code_description) - RendezvousFailureReason.UserCancelled -> getString(CommonStrings.qr_code_login_header_failed_user_cancelled_description) - else -> getString(CommonStrings.qr_code_login_header_failed_other_description) - } - } - - private fun handleConnectingToDevice() { - views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = false - views.qrCodeLoginStatusLoadingLayout.isVisible = true - views.qrCodeLoginStatusHeaderView.isVisible = false - views.qrCodeLoginStatusSecurityCode.isVisible = false - views.qrCodeLoginStatusNoMatchLayout.isVisible = false - views.qrCodeLoginStatusCancelButton.isVisible = true - views.qrCodeLoginStatusTryAgainButton.isVisible = false - views.qrCodeLoginStatusLoadingTextView.setText(CommonStrings.qr_code_login_connecting_to_device) - } - - private fun handleSigningIn() { - views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = false - views.qrCodeLoginStatusLoadingLayout.isVisible = true - views.qrCodeLoginStatusHeaderView.apply { - isVisible = true - setTitle(getString(CommonStrings.dialog_title_success)) - setDescription("") - setImage(R.drawable.ic_tick, ThemeUtils.getColor(requireContext(), com.google.android.material.R.attr.colorPrimary)) - } - views.qrCodeLoginStatusSecurityCode.isVisible = false - views.qrCodeLoginStatusNoMatchLayout.isVisible = false - views.qrCodeLoginStatusCancelButton.isVisible = false - views.qrCodeLoginStatusTryAgainButton.isVisible = false - views.qrCodeLoginStatusLoadingTextView.setText(CommonStrings.qr_code_login_signing_in) - } - - private fun handleConnectionEstablished(connectionStatus: QrCodeLoginConnectionStatus.Connected, loginType: QrCodeLoginType) { - views.qrCodeLoginConfirmSecurityCodeLayout.isVisible = loginType == QrCodeLoginType.LINK_A_DEVICE - views.qrCodeLoginStatusLoadingLayout.isVisible = false - views.qrCodeLoginStatusHeaderView.isVisible = true - views.qrCodeLoginStatusSecurityCode.isVisible = true - views.qrCodeLoginStatusNoMatchLayout.isVisible = loginType == QrCodeLoginType.LOGIN - views.qrCodeLoginStatusCancelButton.isVisible = true - views.qrCodeLoginStatusTryAgainButton.isVisible = false - views.qrCodeLoginStatusSecurityCode.text = connectionStatus.securityCode - views.qrCodeLoginStatusHeaderView.setTitle(getString(CommonStrings.qr_code_login_header_connected_title)) - views.qrCodeLoginStatusHeaderView.setDescription(getString(CommonStrings.qr_code_login_header_connected_description)) - views.qrCodeLoginStatusHeaderView.setImage( - imageResource = R.drawable.ic_qr_code_login_connected, - backgroundTintColor = ThemeUtils.getColor(requireContext(), com.google.android.material.R.attr.colorPrimary) - ) - } - - override fun invalidate() = withState(viewModel) { state -> - when (state.connectionStatus) { - is QrCodeLoginConnectionStatus.Connected -> handleConnectionEstablished(state.connectionStatus, state.loginType) - QrCodeLoginConnectionStatus.ConnectingToDevice -> handleConnectingToDevice() - QrCodeLoginConnectionStatus.SigningIn -> handleSigningIn() - is QrCodeLoginConnectionStatus.Failed -> handleFailed(state.connectionStatus) - null -> { /* NOOP */ - } - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt deleted file mode 100644 index b4bb5b667fc..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginType.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.login.qr - -enum class QrCodeLoginType { - LOGIN, - LINK_A_DEVICE, -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt deleted file mode 100644 index e20ea6b2e8e..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewEvents.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2022 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.login.qr - -import im.vector.app.core.platform.VectorViewEvents - -sealed class QrCodeLoginViewEvents : VectorViewEvents { - object NavigateToStatusScreen : QrCodeLoginViewEvents() - object NavigateToShowQrCodeScreen : QrCodeLoginViewEvents() - object NavigateToHomeScreen : QrCodeLoginViewEvents() - object NavigateToInitialScreen : QrCodeLoginViewEvents() -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt deleted file mode 100644 index 97cca9d7918..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt +++ /dev/null @@ -1,166 +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 - -import com.airbnb.mvrx.MavericksViewModelFactory -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.MavericksAssistedViewModelFactory -import im.vector.app.core.di.hiltMavericksViewModelFactory -import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.session.ConfigureAndStartSessionUseCase -import kotlinx.coroutines.Dispatchers -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( - @Assisted private val initialState: QrCodeLoginViewState, - private val authenticationService: AuthenticationService, - private val activeSessionHolder: ActiveSessionHolder, - private val configureAndStartSessionUseCase: ConfigureAndStartSessionUseCase, -) : VectorViewModel(initialState) { - - @AssistedFactory - interface Factory : MavericksAssistedViewModelFactory { - override fun create(initialState: QrCodeLoginViewState): QrCodeLoginViewModel - } - - companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { - val TAG: String = QrCodeLoginViewModel::class.java.simpleName - } - - override fun handle(action: QrCodeLoginAction) { - when (action) { - is QrCodeLoginAction.OnQrCodeScanned -> handleOnQrCodeScanned(action) - QrCodeLoginAction.GenerateQrCode -> handleQrCodeViewStarted() - QrCodeLoginAction.ShowQrCode -> handleShowQrCode() - QrCodeLoginAction.TryAgain -> handleTryAgain() - } - } - - private fun handleTryAgain() { - setState { - copy( - connectionStatus = null - ) - } - _viewEvents.post(QrCodeLoginViewEvents.NavigateToInitialScreen) - } - - private fun handleShowQrCode() { - _viewEvents.post(QrCodeLoginViewEvents.NavigateToShowQrCodeScreen) - } - - private fun handleQrCodeViewStarted() { - val qrCodeData = generateQrCodeData() - setState { - copy( - generatedQrCodeData = qrCodeData - ) - } - } - - private fun handleOnQrCodeScanned(action: QrCodeLoginAction.OnQrCodeScanned) { - 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") - if (t is RendezvousError) { - onFailed(t.reason) - } else { - onFailed(RendezvousFailureReason.Unknown) - } - return - } - - setState { - copy( - connectionStatus = QrCodeLoginConnectionStatus.ConnectingToDevice - ) - } - - _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen) - - viewModelScope.launch(Dispatchers.IO) { - try { - val confirmationCode = rendezvous.startAfterScanningCode() - Timber.tag(TAG).i("Established secure channel with checksum: $confirmationCode") - - onConnectionEstablished(confirmationCode) - - val session = rendezvous.waitForLoginOnNewDevice(authenticationService) - onSigningIn() - - 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) { - onFailed(t.reason) - } else { - onFailed(RendezvousFailureReason.Unknown) - } - } - } - } - - private fun onFailed(reason: RendezvousFailureReason) { - _viewEvents.post(QrCodeLoginViewEvents.NavigateToStatusScreen) - - setState { - copy( - connectionStatus = QrCodeLoginConnectionStatus.Failed(reason, reason.canRetry) - ) - } - } - - private fun onConnectionEstablished(securityCode: String) { - val canConfirmSecurityCode = initialState.loginType == QrCodeLoginType.LINK_A_DEVICE - setState { - copy( - connectionStatus = QrCodeLoginConnectionStatus.Connected(securityCode, canConfirmSecurityCode) - ) - } - } - - private fun onSigningIn() { - setState { - copy( - connectionStatus = QrCodeLoginConnectionStatus.SigningIn - ) - } - } - - /** - * QR code generation is not currently supported and this is a placeholder for future - * functionality. - */ - private fun generateQrCodeData(): String { - return "NOT SUPPORTED" - } -} diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt deleted file mode 100644 index 0c4457c12fb..00000000000 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewState.kt +++ /dev/null @@ -1,30 +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 - -import com.airbnb.mvrx.MavericksState - -data class QrCodeLoginViewState( - val loginType: QrCodeLoginType, - val connectionStatus: QrCodeLoginConnectionStatus? = null, - val generatedQrCodeData: String? = null, -) : MavericksState { - - constructor(args: QrCodeLoginArgs) : this( - loginType = args.loginType, - ) -} diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 755b9b3676d..dde658ce6d9 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -71,8 +71,6 @@ import im.vector.app.features.location.live.map.LiveLocationMapViewActivity import im.vector.app.features.location.live.map.LiveLocationMapViewArgs import im.vector.app.features.login.LoginActivity import im.vector.app.features.login.LoginConfig -import im.vector.app.features.login.qr.QrCodeLoginActivity -import im.vector.app.features.login.qr.QrCodeLoginArgs import im.vector.app.features.matrixto.MatrixToBottomSheet import im.vector.app.features.matrixto.OriginOfMatrixTo import im.vector.app.features.media.AttachmentData @@ -614,14 +612,6 @@ class DefaultNavigator @Inject constructor( activityResultLauncher.launch(screenCaptureIntent) } - override fun openLoginWithQrCode(context: Context, qrCodeLoginArgs: QrCodeLoginArgs) { - QrCodeLoginActivity - .getIntent(context, qrCodeLoginArgs) - .also { - context.startActivity(it) - } - } - private fun Intent.start(context: Context) { context.startActivity(this) } diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index 5c6a36f797a..23fbedf573b 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -31,7 +31,6 @@ import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs import im.vector.app.features.location.LocationData import im.vector.app.features.location.LocationSharingMode import im.vector.app.features.login.LoginConfig -import im.vector.app.features.login.qr.QrCodeLoginArgs import im.vector.app.features.matrixto.OriginOfMatrixTo import im.vector.app.features.media.AttachmentData import im.vector.app.features.pin.PinMode @@ -202,9 +201,4 @@ interface Navigator { screenCaptureIntent: Intent, activityResultLauncher: ActivityResultLauncher ) - - fun openLoginWithQrCode( - context: Context, - qrCodeLoginArgs: QrCodeLoginArgs, - ) } 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 949c728aa2c..21b806fda17 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 @@ -121,29 +121,6 @@ class OnboardingViewModel @AssistedInject constructor( } } - private fun checkQrCodeLoginCapability() { - if (!vectorFeatures.isQrCodeLoginEnabled()) { - setState { - copy( - canLoginWithQrCode = false - ) - } - } else if (vectorFeatures.isQrCodeLoginForAllServers()) { - // allow for all servers - setState { - copy( - canLoginWithQrCode = true - ) - } - } else { - setState { - copy( - canLoginWithQrCode = selectedHomeserver.isLoginWithQrSupported - ) - } - } - } - private val matrixOrgUrl = stringProvider.getString(im.vector.app.config.R.string.matrix_org_server_url).ensureTrailingSlash() private val defaultHomeserverUrl = mdmService.getData(MdmData.DefaultHomeserverUrl, matrixOrgUrl) @@ -710,7 +687,6 @@ class OnboardingViewModel @AssistedInject constructor( _viewEvents.post(OnboardingViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig"))) } else { startAuthenticationFlow(action, homeServerConnectionConfig, serverTypeOverride, suspend { - checkQrCodeLoginCapability() postAction() }) } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt index 58b28ac4e41..defb08aeaea 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt @@ -59,8 +59,6 @@ data class OnboardingViewState( @PersistState val personalizationState: PersonalizationState = PersonalizationState(), - - val canLoginWithQrCode: Boolean = false, ) : MavericksState enum class OnboardingFlow { diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt index abcfcafc64c..cb687f6b00b 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt @@ -40,8 +40,6 @@ import im.vector.app.features.VectorFeatures import im.vector.app.features.login.LoginMode import im.vector.app.features.login.SSORedirectRouterActivity import im.vector.app.features.login.SocialLoginButtonsView -import im.vector.app.features.login.qr.QrCodeLoginArgs -import im.vector.app.features.login.qr.QrCodeLoginType import im.vector.app.features.login.render import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingViewEvents @@ -75,26 +73,6 @@ class FtueAuthCombinedLoginFragment : viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content())) } views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) } - - viewModel.onEach(OnboardingViewState::canLoginWithQrCode) { - configureQrCodeLoginButtonVisibility(it) - } - } - - private fun configureQrCodeLoginButtonVisibility(canLoginWithQrCode: Boolean) { - views.loginWithQrCode.isVisible = canLoginWithQrCode - if (canLoginWithQrCode) { - views.loginWithQrCode.debouncedClicks { - navigator - .openLoginWithQrCode( - requireActivity(), - QrCodeLoginArgs( - loginType = QrCodeLoginType.LOGIN, - showQrCodeImmediately = false, - ) - ) - } - } } private fun setupSubmitButton() { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index d680de8aa33..0c49fd07bad 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 @@ -41,8 +41,6 @@ import im.vector.app.databinding.FragmentSettingsDevicesBinding import im.vector.app.features.VectorFeatures import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.crypto.recover.SetupMode -import im.vector.app.features.login.qr.QrCodeLoginArgs -import im.vector.app.features.login.qr.QrCodeLoginType import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER import im.vector.app.features.settings.devices.v2.list.OtherSessionsView @@ -106,7 +104,6 @@ class VectorSettingsDevicesFragment : initOtherSessionsHeaderView() initOtherSessionsView() initSecurityRecommendationsView() - initQrLoginView() observeViewEvents() } @@ -240,38 +237,6 @@ class VectorSettingsDevicesFragment : } } - private fun initQrLoginView() { - if (!vectorFeatures.isReciprocateQrCodeLogin()) { - views.deviceListHeaderSignInWithQrCode.isVisible = false - views.deviceListHeaderScanQrCodeButton.isVisible = false - views.deviceListHeaderShowQrCodeButton.isVisible = false - return - } - - views.deviceListHeaderSignInWithQrCode.isVisible = true - views.deviceListHeaderScanQrCodeButton.isVisible = true - views.deviceListHeaderShowQrCodeButton.isVisible = true - - views.deviceListHeaderScanQrCodeButton.debouncedClicks { - navigateToQrCodeScreen(showQrCodeImmediately = false) - } - - views.deviceListHeaderShowQrCodeButton.debouncedClicks { - navigateToQrCodeScreen(showQrCodeImmediately = true) - } - } - - private fun navigateToQrCodeScreen(showQrCodeImmediately: Boolean) { - navigator - .openLoginWithQrCode( - requireActivity(), - QrCodeLoginArgs( - loginType = QrCodeLoginType.LINK_A_DEVICE, - showQrCodeImmediately = showQrCodeImmediately, - ) - ) - } - override fun onDestroyView() { cleanUpLearnMoreButtonsListeners() super.onDestroyView() diff --git a/vector/src/main/res/layout/fragment_ftue_combined_login.xml b/vector/src/main/res/layout/fragment_ftue_combined_login.xml index 7eff92f4f97..d3ffcb93d90 100644 --- a/vector/src/main/res/layout/fragment_ftue_combined_login.xml +++ b/vector/src/main/res/layout/fragment_ftue_combined_login.xml @@ -244,20 +244,6 @@ app:layout_constraintStart_toStartOf="@id/loginGutterStart" app:layout_constraintTop_toBottomOf="@id/loginSubmit" /> -