diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt index 5ae70e1978..d040f9c67b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt @@ -124,4 +124,10 @@ interface AuthenticationService { initialDeviceName: String, deviceId: String? = null ): Session + + /** + * @param homeServerConnectionConfig the information about the homeserver and other configuration + * Return true if qr code login is supported by the server, false otherwise. + */ + suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt index b5d6d891e4..8c14ca892a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt @@ -59,7 +59,12 @@ data class HomeServerCapabilities( /** * True if the home server supports controlling the logout of all devices when changing password. */ - val canControlLogoutDevices: Boolean = false + val canControlLogoutDevices: Boolean = false, + + /** + * True if the home server supports login via qr code, false otherwise. + */ + val canLoginWithQrCode: Boolean = false, ) { enum class RoomCapabilitySupport { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 446f931847..4f45f807df 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.login.LoginWizard import org.matrix.android.sdk.api.auth.registration.RegistrationWizard import org.matrix.android.sdk.api.auth.wellknown.WellknownResult +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixIdFailure import org.matrix.android.sdk.api.session.Session @@ -42,6 +43,7 @@ import org.matrix.android.sdk.internal.auth.login.DirectLoginTask import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices +import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk import org.matrix.android.sdk.internal.di.Unauthenticated @@ -404,6 +406,20 @@ internal class DefaultAuthenticationService @Inject constructor( ) } + override suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean { + val authAPI = buildAuthAPI(homeServerConnectionConfig) + val versions = runCatching { + executeRequest(null) { + authAPI.versions() + } + } + return if (versions.isSuccess) { + versions.getOrNull()?.doesServerSupportQrCodeLogin().orFalse() + } else { + false + } + } + private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI { val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString()) return retrofit.create(AuthAPI::class.java) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt index 915b25134b..5e133fab9c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt @@ -53,6 +53,7 @@ private const val FEATURE_ID_ACCESS_TOKEN = "m.id_access_token" private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind" private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440" private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable" +private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882" /** * Return true if the SDK supports this homeserver version. @@ -78,6 +79,10 @@ internal fun Versions.doesServerSupportThreads(): Boolean { return unstableFeatures?.get(FEATURE_THREADS_MSC3440_STABLE) ?: false } +internal fun Versions.doesServerSupportQrCodeLogin(): Boolean { + return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false +} + /** * Return true if the server support the lazy loading of room members. * diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 2693ca474c..aef482ae2e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -54,6 +54,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037 +import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038 import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import javax.inject.Inject @@ -62,7 +63,7 @@ internal class RealmSessionStoreMigration @Inject constructor( private val normalizer: Normalizer ) : MatrixRealmMigration( dbName = "Session", - schemaVersion = 37L, + schemaVersion = 38L, ) { /** * Forces all RealmSessionStoreMigration instances to be equal. @@ -109,5 +110,6 @@ internal class RealmSessionStoreMigration @Inject constructor( if (oldVersion < 35) MigrateSessionTo035(realm).perform() if (oldVersion < 36) MigrateSessionTo036(realm).perform() if (oldVersion < 37) MigrateSessionTo037(realm).perform() + if (oldVersion < 38) MigrateSessionTo038(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt index 184a0108b9..63fa101c45 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt @@ -43,7 +43,8 @@ internal object HomeServerCapabilitiesMapper { defaultIdentityServerUrl = entity.defaultIdentityServerUrl, roomVersions = mapRoomVersion(entity.roomVersionsJson), canUseThreading = entity.canUseThreading, - canControlLogoutDevices = entity.canControlLogoutDevices + canControlLogoutDevices = entity.canControlLogoutDevices, + canLoginWithQrCode = entity.canLoginWithQrCode, ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo038.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo038.kt new file mode 100644 index 0000000000..52a1ba1e4e --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/migration/MigrateSessionTo038.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.matrix.android.sdk.internal.database.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields +import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +internal class MigrateSessionTo038(realm: DynamicRealm) : RealmMigrator(realm, 38) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("HomeServerCapabilitiesEntity") + ?.addField(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, Boolean::class.java) + ?.transform { obj -> + obj.set(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, false) + } + ?.forceRefreshOfHomeServerCapabilities() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt index 9d90973f8a..cfa02b2c74 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt @@ -30,7 +30,8 @@ internal open class HomeServerCapabilitiesEntity( var defaultIdentityServerUrl: String? = null, var lastUpdatedTimestamp: Long = 0L, var canUseThreading: Boolean = false, - var canControlLogoutDevices: Boolean = false + var canControlLogoutDevices: Boolean = false, + var canLoginWithQrCode: Boolean = false, ) : RealmObject() { companion object diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt index add69dd8c7..c9a342777b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt @@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices +import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity @@ -134,6 +135,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( } homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */ getVersionResult?.doesServerSupportThreads().orFalse() + homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult?.doesServerSupportQrCodeLogin().orFalse() } if (getMediaConfigResult != null) { diff --git a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt index e0323fdc2d..042f885231 100644 --- a/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt @@ -24,7 +24,6 @@ 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.addFragmentToBackstack import im.vector.app.core.platform.SimpleFragmentActivity import org.matrix.android.sdk.api.extensions.orFalse import timber.log.Timber @@ -88,7 +87,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() { } private fun handleNavigateToStatusScreen() { - addFragmentToBackstack( + addFragment( views.container, QrCodeLoginStatusFragment::class.java, tag = FRAGMENT_QR_CODE_STATUS_TAG 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 index 9741b79f7a..1c68ebb2a5 100644 --- 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 @@ -44,7 +44,7 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment @@ -112,12 +113,14 @@ android:id="@+id/deviceListHeaderSignInWithQrCode" android:layout_width="0dp" android:layout_height="wrap_content" + android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/deviceListOtherSessions" app:sessionListHeaderShowLearnMore="false" app:sessionsListHeaderDescription="@string/device_manager_sessions_sign_in_with_qr_code_description" - app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title" /> + app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title" + tools:visibility="visible" />