Sign in with QR code (#2793)

* Add QR code login.
* Add FF to disable it in release mode.
* Force portrait orientation on the login flow.
* Create `NumberedList` UI components.
* Improve camera permission dialog.
* Make nodes in qrcode feature use `QrCodeLoginScope` instead of `AppScope`
* Bump SDK version.
* Fix maestro tests

---------

Co-authored-by: Benoit Marty <benoit@matrix.org>
Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
Jorge Martin Espinosa 2024-05-31 14:38:27 +02:00 committed by GitHub
parent e0c55ff4c8
commit 35702c04e9
253 changed files with 4421 additions and 326 deletions

View file

@ -20,9 +20,13 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@ -32,7 +36,9 @@ import kotlinx.coroutines.flow.flowOf
val A_OIDC_DATA = OidcDetails(url = "a-url")
class FakeMatrixAuthenticationService(
private val matrixClientResult: ((SessionId) -> Result<MatrixClient>)? = null
var matrixClientResult: ((SessionId) -> Result<MatrixClient>)? = null,
var loginWithQrCodeResult: (qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) -> Result<SessionId> =
lambdaRecorder<MatrixQrCodeLoginData, (QrCodeLoginStep) -> Unit, Result<SessionId>> { _, _ -> Result.success(A_SESSION_ID) },
) : MatrixAuthenticationService {
private val homeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
private var oidcError: Throwable? = null
@ -50,8 +56,8 @@ class FakeMatrixAuthenticationService(
override suspend fun getLatestSessionId(): SessionId? = getLatestSessionIdLambda()
override suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient> {
if (matrixClientResult != null) {
return matrixClientResult.invoke(sessionId)
matrixClientResult?.let {
return it.invoke(sessionId)
}
return if (matrixClient != null) {
Result.success(matrixClient!!)
@ -88,6 +94,10 @@ class FakeMatrixAuthenticationService(
loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID)
}
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit): Result<SessionId> = simulateLongTask {
loginWithQrCodeResult(qrCodeData, progress)
}
fun givenOidcError(throwable: Throwable?) {
oidcError = throwable
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 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 io.element.android.libraries.matrix.test.auth.qrlogin
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDataFactory
import io.element.android.tests.testutils.lambda.lambdaRecorder
class FakeMatrixQrCodeLoginDataFactory(
var parseQrCodeLoginDataResult: () -> Result<MatrixQrCodeLoginData> =
lambdaRecorder<Result<MatrixQrCodeLoginData>> { Result.success(FakeMatrixQrCodeLoginData()) },
) : MatrixQrCodeLoginDataFactory {
override fun parseQrCodeData(data: ByteArray): Result<MatrixQrCodeLoginData> {
return parseQrCodeLoginDataResult()
}
}
class FakeMatrixQrCodeLoginData : MatrixQrCodeLoginData