Cherry pick previous commits.

This commit is contained in:
Onuray Sahin 2022-10-13 20:33:52 +03:00
parent d8ea9c8215
commit fb2776dca4
16 changed files with 121 additions and 13 deletions

View file

@ -124,4 +124,10 @@ interface AuthenticationService {
initialDeviceName: String, initialDeviceName: String,
deviceId: String? = null deviceId: String? = null
): Session ): 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
} }

View file

@ -59,7 +59,12 @@ data class HomeServerCapabilities(
/** /**
* True if the home server supports controlling the logout of all devices when changing password. * 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 { enum class RoomCapabilitySupport {

View file

@ -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.login.LoginWizard
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult 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.Failure
import org.matrix.android.sdk.api.failure.MatrixIdFailure import org.matrix.android.sdk.api.failure.MatrixIdFailure
import org.matrix.android.sdk.api.session.Session 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.registration.DefaultRegistrationWizard
import org.matrix.android.sdk.internal.auth.version.Versions 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.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.isLoginAndRegistrationSupportedBySdk
import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk
import org.matrix.android.sdk.internal.di.Unauthenticated 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 { private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString()) val retrofit = retrofitFactory.create(buildClient(homeServerConnectionConfig), homeServerConnectionConfig.homeServerUriBase.toString())
return retrofit.create(AuthAPI::class.java) return retrofit.create(AuthAPI::class.java)

View file

@ -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_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440" private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable" 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. * 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 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. * Return true if the server support the lazy loading of room members.
* *

View file

@ -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.MigrateSessionTo035
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036 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.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.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject import javax.inject.Inject
@ -62,7 +63,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer private val normalizer: Normalizer
) : MatrixRealmMigration( ) : MatrixRealmMigration(
dbName = "Session", dbName = "Session",
schemaVersion = 37L, schemaVersion = 38L,
) { ) {
/** /**
* Forces all RealmSessionStoreMigration instances to be equal. * Forces all RealmSessionStoreMigration instances to be equal.
@ -109,5 +110,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 35) MigrateSessionTo035(realm).perform() if (oldVersion < 35) MigrateSessionTo035(realm).perform()
if (oldVersion < 36) MigrateSessionTo036(realm).perform() if (oldVersion < 36) MigrateSessionTo036(realm).perform()
if (oldVersion < 37) MigrateSessionTo037(realm).perform() if (oldVersion < 37) MigrateSessionTo037(realm).perform()
if (oldVersion < 38) MigrateSessionTo038(realm).perform()
} }
} }

View file

@ -43,7 +43,8 @@ internal object HomeServerCapabilitiesMapper {
defaultIdentityServerUrl = entity.defaultIdentityServerUrl, defaultIdentityServerUrl = entity.defaultIdentityServerUrl,
roomVersions = mapRoomVersion(entity.roomVersionsJson), roomVersions = mapRoomVersion(entity.roomVersionsJson),
canUseThreading = entity.canUseThreading, canUseThreading = entity.canUseThreading,
canControlLogoutDevices = entity.canControlLogoutDevices canControlLogoutDevices = entity.canControlLogoutDevices,
canLoginWithQrCode = entity.canLoginWithQrCode,
) )
} }

View file

@ -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()
}
}

View file

@ -30,7 +30,8 @@ internal open class HomeServerCapabilitiesEntity(
var defaultIdentityServerUrl: String? = null, var defaultIdentityServerUrl: String? = null,
var lastUpdatedTimestamp: Long = 0L, var lastUpdatedTimestamp: Long = 0L,
var canUseThreading: Boolean = false, var canUseThreading: Boolean = false,
var canControlLogoutDevices: Boolean = false var canControlLogoutDevices: Boolean = false,
var canLoginWithQrCode: Boolean = false,
) : RealmObject() { ) : RealmObject() {
companion object companion object

View file

@ -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.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.internal.auth.version.Versions 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.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.doesServerSupportThreads
import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
@ -134,6 +135,7 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
} }
homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */ homeServerCapabilitiesEntity.canUseThreading = /* capabilities?.threads?.enabled.orFalse() || */
getVersionResult?.doesServerSupportThreads().orFalse() getVersionResult?.doesServerSupportThreads().orFalse()
homeServerCapabilitiesEntity.canLoginWithQrCode = getVersionResult?.doesServerSupportQrCodeLogin().orFalse()
} }
if (getMediaConfigResult != null) { if (getMediaConfigResult != null) {

View file

@ -24,7 +24,6 @@ import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.extensions.addFragmentToBackstack
import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.SimpleFragmentActivity
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import timber.log.Timber import timber.log.Timber
@ -88,7 +87,7 @@ class QrCodeLoginActivity : SimpleFragmentActivity() {
} }
private fun handleNavigateToStatusScreen() { private fun handleNavigateToStatusScreen() {
addFragmentToBackstack( addFragment(
views.container, views.container,
QrCodeLoginStatusFragment::class.java, QrCodeLoginStatusFragment::class.java,
tag = FRAGMENT_QR_CODE_STATUS_TAG tag = FRAGMENT_QR_CODE_STATUS_TAG

View file

@ -44,7 +44,7 @@ class QrCodeLoginShowQrCodeFragment : VectorBaseFragment<FragmentQrCodeLoginShow
private fun initCancelButton() { private fun initCancelButton() {
views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks { views.qrCodeLoginShowQrCodeCancelButton.debouncedClicks {
parentFragmentManager.popBackStack() activity?.onBackPressed()
} }
} }

View file

@ -45,7 +45,7 @@ class QrCodeLoginStatusFragment : VectorBaseFragment<FragmentQrCodeLoginStatusBi
private fun initCancelButton() { private fun initCancelButton() {
views.qrCodeLoginStatusCancelButton.debouncedClicks { views.qrCodeLoginStatusCancelButton.debouncedClicks {
parentFragmentManager.popBackStack() activity?.onBackPressed()
} }
} }

View file

@ -117,6 +117,25 @@ class OnboardingViewModel @AssistedInject constructor(
} }
} }
private fun observeQrCodeLoginCapability() = viewModelScope.launch {
if (!vectorFeatures.isQrCodeLoginEnabled()) {
setState {
copy(
canLoginWithQrCode = false
)
}
} else {
homeServerConnectionConfigFactory.create(defaultHomeserverUrl)?.let {
val canLoginWithQrCode = authenticationService.isQrLoginSupported(it)
setState {
copy(
canLoginWithQrCode = canLoginWithQrCode
)
}
}
}
}
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash() private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
private val defaultHomeserverUrl = matrixOrgUrl private val defaultHomeserverUrl = matrixOrgUrl
@ -234,6 +253,7 @@ class OnboardingViewModel @AssistedInject constructor(
private fun handleSplashAction(action: OnboardingAction.SplashAction) { private fun handleSplashAction(action: OnboardingAction.SplashAction) {
setState { copy(onboardingFlow = action.onboardingFlow) } setState { copy(onboardingFlow = action.onboardingFlow) }
continueToPageAfterSplash(action.onboardingFlow) continueToPageAfterSplash(action.onboardingFlow)
observeQrCodeLoginCapability()
} }
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) { private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {

View file

@ -58,7 +58,9 @@ data class OnboardingViewState(
val selectedAuthenticationState: SelectedAuthenticationState = SelectedAuthenticationState(), val selectedAuthenticationState: SelectedAuthenticationState = SelectedAuthenticationState(),
@PersistState @PersistState
val personalizationState: PersonalizationState = PersonalizationState() val personalizationState: PersonalizationState = PersonalizationState(),
val canLoginWithQrCode: Boolean = false,
) : MavericksState ) : MavericksState
enum class OnboardingFlow { enum class OnboardingFlow {

View file

@ -74,7 +74,15 @@ class FtueAuthCombinedLoginFragment :
viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content())) viewModel.handle(OnboardingAction.UserNameEnteredAction.Login(views.loginInput.content()))
} }
views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) } views.loginForgotPassword.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnForgetPasswordClicked)) }
if (vectorFeatures.isQrCodeLoginEnabled()) {
viewModel.onEach(OnboardingViewState::canLoginWithQrCode) {
configureQrCodeLoginButtonVisibility(it)
}
}
private fun configureQrCodeLoginButtonVisibility(canLoginWithQrCode: Boolean) {
if (canLoginWithQrCode) {
views.loginWithQrCode.isVisible = true
views.loginWithQrCode.debouncedClicks { views.loginWithQrCode.debouncedClicks {
navigator navigator
.openLoginWithQrCode( .openLoginWithQrCode(

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -112,12 +113,14 @@
android:id="@+id/deviceListHeaderSignInWithQrCode" android:id="@+id/deviceListHeaderSignInWithQrCode"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListOtherSessions" app:layout_constraintTop_toBottomOf="@id/deviceListOtherSessions"
app:sessionListHeaderShowLearnMore="false" app:sessionListHeaderShowLearnMore="false"
app:sessionsListHeaderDescription="@string/device_manager_sessions_sign_in_with_qr_code_description" 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" />
<Button <Button
android:id="@+id/deviceListHeaderScanQrCodeButton" android:id="@+id/deviceListHeaderScanQrCodeButton"
@ -126,9 +129,11 @@
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/qr_code_login_scan_qr_code_button" android:text="@string/qr_code_login_scan_qr_code_button"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode" /> app:layout_constraintTop_toBottomOf="@id/deviceListHeaderSignInWithQrCode"
tools:visibility="visible" />
<Button <Button
android:id="@+id/deviceListHeaderShowQrCodeButton" android:id="@+id/deviceListHeaderShowQrCodeButton"
@ -139,9 +144,11 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:text="@string/qr_code_login_show_qr_code_button" android:text="@string/qr_code_login_show_qr_code_button"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton" /> app:layout_constraintTop_toBottomOf="@id/deviceListHeaderScanQrCodeButton"
tools:visibility="visible" />
<include <include
android:id="@+id/waiting_view" android:id="@+id/waiting_view"