Link new device using QrCode.

This commit is contained in:
Benoit Marty 2025-12-04 10:52:26 +01:00 committed by Benoit Marty
parent 5ebb615751
commit a073117d62
94 changed files with 4431 additions and 36 deletions

View file

@ -19,6 +19,8 @@ import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopHandler
import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.media.MediaPreviewService
import io.element.android.libraries.matrix.api.notification.NotificationService
@ -195,6 +197,21 @@ interface MatrixClient {
*/
suspend fun markRoomAsFullyRead(roomId: RoomId, eventId: EventId): Result<Unit>
/**
* Check if linking a new device using QrCode is supported by the server.
*/
suspend fun canLinkNewDevice(): Result<Boolean>
/**
* Create a handler to link a new mobile device, i.e. a device capable of scanning QrCodes.
*/
fun createLinkMobileHandler(): Result<LinkMobileHandler>
/**
* Create a handler to link a new desktop device, i.e. a device not capable of scanning QrCodes.
*/
fun createLinkDesktopHandler(): Result<LinkDesktopHandler>
suspend fun performDatabaseVacuum(): Result<Unit>
}

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.linknewdevice
interface CheckCodeSender {
/**
* Validates the given [code]. Returns true if the code is valid, false otherwise.
* This method can be called multiple times to validate different codes.
*/
suspend fun validate(code: UByte): Boolean
/**
* Sends the given [code].
* This method can be called only once.
*/
suspend fun send(code: UByte): Result<Unit>
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.linknewdevice
sealed class ErrorType(message: String) : Exception(message) {
/**
* The requested device ID is already in use.
*/
class DeviceIdAlreadyInUse(message: String) : ErrorType(message)
/**
* The check code was incorrect.
*/
class InvalidCheckCode(message: String) : ErrorType(message)
/**
* The other client proposed an unsupported protocol.
*/
class UnsupportedProtocol(message: String) : ErrorType(message)
/**
* Secrets backup not set up properly.
*/
class MissingSecretsBackup(message: String) : ErrorType(message)
/**
* The rendezvous session was not found and might have expired.
*/
class NotFound(message: String) : ErrorType(message)
/**
* The device could not be created.
*/
class UnableToCreateDevice(message: String) : ErrorType(message)
/**
* An unknown error has happened.
*/
class Unknown(message: String) : ErrorType(message)
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.linknewdevice
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeDecodeException
import kotlinx.coroutines.flow.StateFlow
interface LinkDesktopHandler {
val linkDesktopStep: StateFlow<LinkDesktopStep>
suspend fun handleScannedQrCode(data: ByteArray)
}
sealed interface LinkDesktopStep {
data object Uninitialized : LinkDesktopStep
data object Starting : LinkDesktopStep
data class WaitingForAuth(
val verificationUri: String,
) : LinkDesktopStep
data class EstablishingSecureChannel(
val checkCode: UByte,
val checkCodeString: String,
) : LinkDesktopStep
data class InvalidQrCode(
val error: QrCodeDecodeException,
) : LinkDesktopStep
data class Error(
val errorType: ErrorType,
) : LinkDesktopStep
data object SyncingSecrets : LinkDesktopStep
data object Done : LinkDesktopStep
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.linknewdevice
import kotlinx.coroutines.flow.Flow
interface LinkMobileHandler {
val linkMobileStep: Flow<LinkMobileStep>
suspend fun start()
}
sealed interface LinkMobileStep {
data object Uninitialized : LinkMobileStep
data object Starting : LinkMobileStep
data class QrReady(val data: String) : LinkMobileStep
data class WaitingForAuth(val verificationUri: String) : LinkMobileStep
data class QrScanned(val checkCodeSender: CheckCodeSender) : LinkMobileStep
data class Error(val errorType: ErrorType) : LinkMobileStep
data object SyncingSecrets : LinkMobileStep
data object Done : LinkMobileStep
}

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.logs
import io.element.android.libraries.core.log.logger.LoggerTag
object LoggerTags {
val linkNewDevice = LoggerTag("LinkNewDevice")
}