Skip to content

Commit e1a8226

Browse files
author
Onuray Sahin
committed
Check if server supports qr code login.
1 parent 5763f5e commit e1a8226

File tree

15 files changed

+119
-13
lines changed

15 files changed

+119
-13
lines changed

matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/AuthenticationService.kt

+6
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,10 @@ interface AuthenticationService {
124124
initialDeviceName: String,
125125
deviceId: String? = null
126126
): Session
127+
128+
/**
129+
* @param homeServerConnectionConfig the information about the homeserver and other configuration
130+
* Return true if qr code login is supported by the server, false otherwise.
131+
*/
132+
suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean
127133
}

matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,12 @@ data class HomeServerCapabilities(
5959
/**
6060
* True if the home server supports controlling the logout of all devices when changing password.
6161
*/
62-
val canControlLogoutDevices: Boolean = false
62+
val canControlLogoutDevices: Boolean = false,
63+
64+
/**
65+
* True if the home server supports login via qr code, false otherwise.
66+
*/
67+
val canLoginWithQrCode: Boolean = false,
6368
) {
6469

6570
enum class RoomCapabilitySupport {

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt

+16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
3030
import org.matrix.android.sdk.api.auth.login.LoginWizard
3131
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
3232
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
33+
import org.matrix.android.sdk.api.extensions.orFalse
3334
import org.matrix.android.sdk.api.failure.Failure
3435
import org.matrix.android.sdk.api.failure.MatrixIdFailure
3536
import org.matrix.android.sdk.api.session.Session
@@ -42,6 +43,7 @@ import org.matrix.android.sdk.internal.auth.login.DirectLoginTask
4243
import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard
4344
import org.matrix.android.sdk.internal.auth.version.Versions
4445
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
46+
import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
4547
import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
4648
import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk
4749
import org.matrix.android.sdk.internal.di.Unauthenticated
@@ -404,6 +406,20 @@ internal class DefaultAuthenticationService @Inject constructor(
404406
)
405407
}
406408

409+
override suspend fun isQrLoginSupported(homeServerConnectionConfig: HomeServerConnectionConfig): Boolean {
410+
val authAPI = buildAuthAPI(homeServerConnectionConfig)
411+
val versions = runCatching {
412+
executeRequest(null) {
413+
authAPI.versions()
414+
}
415+
}
416+
return if (versions.isSuccess) {
417+
versions.getOrNull()?.doesServerSupportQrCodeLogin().orFalse()
418+
} else {
419+
false
420+
}
421+
}
422+
407423
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
408424
val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
409425
return retrofit.create(AuthAPI::class.java)

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt

+5
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ private const val FEATURE_ID_ACCESS_TOKEN = "m.id_access_token"
5353
private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
5454
private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
5555
private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
56+
private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
5657

5758
/**
5859
* Return true if the SDK supports this homeserver version.
@@ -78,6 +79,10 @@ internal fun Versions.doesServerSupportThreads(): Boolean {
7879
return unstableFeatures?.get(FEATURE_THREADS_MSC3440_STABLE) ?: false
7980
}
8081

82+
internal fun Versions.doesServerSupportQrCodeLogin(): Boolean {
83+
return unstableFeatures?.get(FEATURE_QR_CODE_LOGIN) ?: false
84+
}
85+
8186
/**
8287
* Return true if the server support the lazy loading of room members.
8388
*

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034
5454
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
5555
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
5656
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
57+
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038
5758
import org.matrix.android.sdk.internal.util.Normalizer
5859
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
5960
import javax.inject.Inject
@@ -62,7 +63,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
6263
private val normalizer: Normalizer
6364
) : MatrixRealmMigration(
6465
dbName = "Session",
65-
schemaVersion = 37L,
66+
schemaVersion = 38L,
6667
) {
6768
/**
6869
* Forces all RealmSessionStoreMigration instances to be equal.
@@ -109,5 +110,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
109110
if (oldVersion < 35) MigrateSessionTo035(realm).perform()
110111
if (oldVersion < 36) MigrateSessionTo036(realm).perform()
111112
if (oldVersion < 37) MigrateSessionTo037(realm).perform()
113+
if (oldVersion < 38) MigrateSessionTo038(realm).perform()
112114
}
113115
}

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/HomeServerCapabilitiesMapper.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ internal object HomeServerCapabilitiesMapper {
4343
defaultIdentityServerUrl = entity.defaultIdentityServerUrl,
4444
roomVersions = mapRoomVersion(entity.roomVersionsJson),
4545
canUseThreading = entity.canUseThreading,
46-
canControlLogoutDevices = entity.canControlLogoutDevices
46+
canControlLogoutDevices = entity.canControlLogoutDevices,
47+
canLoginWithQrCode = entity.canLoginWithQrCode,
4748
)
4849
}
4950

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.matrix.android.sdk.internal.database.migration
18+
19+
import io.realm.DynamicRealm
20+
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
21+
import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities
22+
import org.matrix.android.sdk.internal.util.database.RealmMigrator
23+
24+
internal class MigrateSessionTo038(realm: DynamicRealm) : RealmMigrator(realm, 38) {
25+
26+
override fun doMigrate(realm: DynamicRealm) {
27+
realm.schema.get("HomeServerCapabilitiesEntity")
28+
?.addField(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, Boolean::class.java)
29+
?.transform { obj ->
30+
obj.set(HomeServerCapabilitiesEntityFields.CAN_LOGIN_WITH_QR_CODE, false)
31+
}
32+
?.forceRefreshOfHomeServerCapabilities()
33+
}
34+
}

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/HomeServerCapabilitiesEntity.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ internal open class HomeServerCapabilitiesEntity(
3030
var defaultIdentityServerUrl: String? = null,
3131
var lastUpdatedTimestamp: Long = 0L,
3232
var canUseThreading: Boolean = false,
33-
var canControlLogoutDevices: Boolean = false
33+
var canControlLogoutDevices: Boolean = false,
34+
var canLoginWithQrCode: Boolean = false,
3435
) : RealmObject() {
3536

3637
companion object

matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/GetHomeServerCapabilitiesTask.kt

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.extensions.orTrue
2525
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
2626
import org.matrix.android.sdk.internal.auth.version.Versions
2727
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
28+
import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
2829
import org.matrix.android.sdk.internal.auth.version.doesServerSupportThreads
2930
import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
3031
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
@@ -134,6 +135,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
134135
}
135136
homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
136137
getVersionResult?.doesServerSupportThreads().orFalse()
138+
homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult?.doesServerSupportQrCodeLogin().orFalse()
137139
}
138140

139141
if (getMediaConfigResult != null) {

vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginActivity.kt

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import com.airbnb.mvrx.Mavericks
2424
import com.airbnb.mvrx.viewModel
2525
import dagger.hilt.android.AndroidEntryPoint
2626
import im.vector.app.core.extensions.addFragment
27-
import im.vector.app.core.extensions.addFragmentToBackstack
2827
import im.vector.app.core.platform.SimpleFragmentActivity
2928
import org.matrix.android.sdk.api.extensions.orFalse
3029
import timber.log.Timber

vector/src/main/java/im/vector/app/features/login/qr/QrCodeLoginViewModel.kt

-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
2525
import im.vector.app.core.platform.VectorViewModel
2626
import kotlinx.coroutines.Dispatchers
2727
import kotlinx.coroutines.launch
28-
import org.matrix.android.sdk.api.session.Session
2928
import org.matrix.android.sdk.internal.rendezvous.Rendezvous
3029
import org.matrix.android.sdk.internal.rendezvous.RendezvousFailureReason
3130
import timber.log.Timber
@@ -118,7 +117,6 @@ class QrCodeLoginViewModel @AssistedInject constructor(
118117
// }
119118
}
120119

121-
122120
private fun onFailed(reason: RendezvousFailureReason) {
123121
setState {
124122
copy(

vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt

+20
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,25 @@ class OnboardingViewModel @AssistedInject constructor(
117117
}
118118
}
119119

120+
private fun observeQrCodeLoginCapability() = viewModelScope.launch {
121+
if (!vectorFeatures.isQrCodeLoginEnabled()) {
122+
setState {
123+
copy(
124+
canLoginWithQrCode = false
125+
)
126+
}
127+
} else {
128+
homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
129+
val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
130+
setState {
131+
copy(
132+
canLoginWithQrCode = canLoginWithQrCode
133+
)
134+
}
135+
}
136+
}
137+
}
138+
120139
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
121140
private val defaultHomeserverUrl = matrixOrgUrl
122141

@@ -234,6 +253,7 @@ class OnboardingViewModel @AssistedInject constructor(
234253
private fun handleSplashAction(action: OnboardingAction.SplashAction) {
235254
setState { copy(onboardingFlow = action.onboardingFlow) }
236255
continueToPageAfterSplash(action.onboardingFlow)
256+
observeQrCodeLoginCapability()
237257
}
238258

239259
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {

vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ data class OnboardingViewState(
5858
val selectedAuthenticationState: SelectedAuthenticationState = SelectedAuthenticationState(),
5959

6060
@PersistState
61-
val personalizationState: PersonalizationState = PersonalizationState()
61+
val personalizationState: PersonalizationState = PersonalizationState(),
62+
63+
val canLoginWithQrCode: Boolean = false,
6264
) : MavericksState
6365

6466
enum class OnboardingFlow {

vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedLoginFragment.kt

+9-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,15 @@ class FtueAuthCombinedLoginFragment :
7474
viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content()))
7575
}
7676
views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) }
77-
if (vectorFeatures.isQrCodeLoginEnabled()) {
77+
78+
viewModel.onEach(OnboardingViewState::canLoginWithQrCode) {
79+
configureQrCodeLoginButtonVisibility(it)
80+
}
81+
}
82+
83+
private fun configureQrCodeLoginButtonVisibility(canLoginWithQrCode: Boolean) {
84+
if (canLoginWithQrCode) {
85+
views.loginWithQrCode.isVisible = true
7886
views.loginWithQrCode.debouncedClicks {
7987
navigator
8088
.openLoginWithQrCode(

vector/src/main/res/layout/fragment_settings_devices.xml

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
45
android:layout_width="match_parent"
56
android:layout_height="match_parent">
67

@@ -112,12 +113,14 @@
112113
android:id="@+id/deviceListHeaderSignInWithQrCode"
113114
android:layout_width="0dp"
114115
android:layout_height="wrap_content"
116+
android:visibility="gone"
115117
app:layout_constraintEnd_toEndOf="parent"
116118
app:layout_constraintStart_toStartOf="parent"
117119
app:layout_constraintTop_toBottomOf="@id/deviceListOtherSessions"
118-
app:sessionsListHeaderHasLearnMoreLink="false"
119120
app:sessionsListHeaderDescription="@string/device_manager_sessions_sign_in_with_qr_code_description"
120-
app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title" />
121+
app:sessionsListHeaderHasLearnMoreLink="false"
122+
app:sessionsListHeaderTitle="@string/device_manager_sessions_sign_in_with_qr_code_title"
123+
tools:visibility="visible" />
121124

122125
<Button
123126
android:id="@+id/deviceListHeaderScanQrCodeButton"
@@ -126,9 +129,11 @@
126129
android:layout_marginHorizontal="16dp"
127130
android:layout_marginTop="12dp"
128131
android:text="@string/qr_code_login_scan_qr_code_button"
132+
android:visibility="gone"
129133
app:layout_constraintEnd_toEndOf="parent"
130134
app:layout_constraintStart_toStartOf="parent"
131-
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode" />
135+
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode"
136+
tools:visibility="visible" />
132137

133138
<Button
134139
android:id="@+id/deviceListHeaderShowQrCodeButton"
@@ -139,9 +144,11 @@
139144
android:layout_marginTop="4dp"
140145
android:layout_marginBottom="12dp"
141146
android:text="@string/qr_code_login_show_qr_code_button"
147+
android:visibility="gone"
142148
app:layout_constraintEnd_toEndOf="parent"
143149
app:layout_constraintStart_toStartOf="parent"
144-
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton" />
150+
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton"
151+
tools:visibility="visible" />
145152

146153
<include
147154
android:id="@+id/waiting_view"

0 commit comments

Comments
 (0)