renaming the ftue domain to onboarding and ftue to ftue auth
This commit is contained in:
parent
3e1801a5c4
commit
8de892bc60
|
@ -37,7 +37,6 @@ import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeVie
|
||||||
import im.vector.app.features.devtools.RoomDevToolViewModel
|
import im.vector.app.features.devtools.RoomDevToolViewModel
|
||||||
import im.vector.app.features.discovery.DiscoverySettingsViewModel
|
import im.vector.app.features.discovery.DiscoverySettingsViewModel
|
||||||
import im.vector.app.features.discovery.change.SetIdentityServerViewModel
|
import im.vector.app.features.discovery.change.SetIdentityServerViewModel
|
||||||
import im.vector.app.features.ftue.FTUEViewModel
|
|
||||||
import im.vector.app.features.home.HomeActivityViewModel
|
import im.vector.app.features.home.HomeActivityViewModel
|
||||||
import im.vector.app.features.home.HomeDetailViewModel
|
import im.vector.app.features.home.HomeDetailViewModel
|
||||||
import im.vector.app.features.home.PromoteRestrictedViewModel
|
import im.vector.app.features.home.PromoteRestrictedViewModel
|
||||||
|
|
|
@ -1,363 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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 im.vector.app.features.ftue
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import androidx.core.view.children
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.fragment.app.FragmentTransaction
|
|
||||||
import com.airbnb.mvrx.withState
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import im.vector.app.R
|
|
||||||
import im.vector.app.core.extensions.POP_BACK_STACK_EXCLUSIVE
|
|
||||||
import im.vector.app.core.extensions.addFragment
|
|
||||||
import im.vector.app.core.extensions.addFragmentToBackstack
|
|
||||||
import im.vector.app.core.extensions.exhaustive
|
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
|
||||||
import im.vector.app.databinding.ActivityLoginBinding
|
|
||||||
import im.vector.app.features.home.HomeActivity
|
|
||||||
import im.vector.app.features.login.LoginCaptchaFragment
|
|
||||||
import im.vector.app.features.login.LoginCaptchaFragmentArgument
|
|
||||||
import im.vector.app.features.login.LoginConfig
|
|
||||||
import im.vector.app.features.login.LoginFragment
|
|
||||||
import im.vector.app.features.login.LoginGenericTextInputFormFragment
|
|
||||||
import im.vector.app.features.login.LoginGenericTextInputFormFragmentArgument
|
|
||||||
import im.vector.app.features.login.LoginMode
|
|
||||||
import im.vector.app.features.login.LoginResetPasswordFragment
|
|
||||||
import im.vector.app.features.login.LoginResetPasswordMailConfirmationFragment
|
|
||||||
import im.vector.app.features.login.LoginResetPasswordSuccessFragment
|
|
||||||
import im.vector.app.features.login.LoginServerSelectionFragment
|
|
||||||
import im.vector.app.features.login.LoginServerUrlFormFragment
|
|
||||||
import im.vector.app.features.login.LoginSignUpSignInSelectionFragment
|
|
||||||
import im.vector.app.features.login.LoginSplashFragment
|
|
||||||
import im.vector.app.features.login.LoginWaitForEmailFragment
|
|
||||||
import im.vector.app.features.login.LoginWaitForEmailFragmentArgument
|
|
||||||
import im.vector.app.features.login.LoginWebFragment
|
|
||||||
import im.vector.app.features.login.ServerType
|
|
||||||
import im.vector.app.features.login.SignMode
|
|
||||||
import im.vector.app.features.login.TextInputFormFragmentMode
|
|
||||||
import im.vector.app.features.login.isSupported
|
|
||||||
import im.vector.app.features.login.terms.LoginTermsFragment
|
|
||||||
import im.vector.app.features.login.terms.LoginTermsFragmentArgument
|
|
||||||
import im.vector.app.features.login.terms.toLocalizedLoginTerms
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
|
|
||||||
private const val FRAGMENT_REGISTRATION_STAGE_TAG = "FRAGMENT_REGISTRATION_STAGE_TAG"
|
|
||||||
private const val FRAGMENT_LOGIN_TAG = "FRAGMENT_LOGIN_TAG"
|
|
||||||
|
|
||||||
class DefaultFTUEVariant(
|
|
||||||
private val views: ActivityLoginBinding,
|
|
||||||
private val ftueViewModel: FTUEViewModel,
|
|
||||||
private val activity: VectorBaseActivity<ActivityLoginBinding>,
|
|
||||||
private val supportFragmentManager: FragmentManager
|
|
||||||
) : FTUEVariant {
|
|
||||||
|
|
||||||
private val enterAnim = R.anim.enter_fade_in
|
|
||||||
private val exitAnim = R.anim.exit_fade_out
|
|
||||||
|
|
||||||
private val popEnterAnim = R.anim.no_anim
|
|
||||||
private val popExitAnim = R.anim.exit_fade_out
|
|
||||||
|
|
||||||
private val topFragment: Fragment?
|
|
||||||
get() = supportFragmentManager.findFragmentById(R.id.loginFragmentContainer)
|
|
||||||
|
|
||||||
private val commonOption: (FragmentTransaction) -> Unit = { ft ->
|
|
||||||
// Find the loginLogo on the current Fragment, this should not return null
|
|
||||||
(topFragment?.view as? ViewGroup)
|
|
||||||
// Find findViewById does not work, I do not know why
|
|
||||||
// findViewById<View?>(R.id.loginLogo)
|
|
||||||
?.children
|
|
||||||
?.firstOrNull { it.id == R.id.loginLogo }
|
|
||||||
?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
|
|
||||||
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun initUiAndData(isFirstCreation: Boolean) {
|
|
||||||
if (isFirstCreation) {
|
|
||||||
addFirstFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
with(activity) {
|
|
||||||
ftueViewModel.onEach {
|
|
||||||
updateWithState(it)
|
|
||||||
}
|
|
||||||
ftueViewModel.observeViewEvents { handleLoginViewEvents(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get config extra
|
|
||||||
val loginConfig = activity.intent.getParcelableExtra<LoginConfig?>(FTUEActivity.EXTRA_CONFIG)
|
|
||||||
if (isFirstCreation) {
|
|
||||||
ftueViewModel.handle(FTUEAction.InitWith(loginConfig))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setIsLoading(isLoading: Boolean) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addFirstFragment() {
|
|
||||||
activity.addFragment(R.id.loginFragmentContainer, LoginSplashFragment::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleLoginViewEvents(ftueViewEvents: FTUEViewEvents) {
|
|
||||||
when (ftueViewEvents) {
|
|
||||||
is FTUEViewEvents.RegistrationFlowResult -> {
|
|
||||||
// Check that all flows are supported by the application
|
|
||||||
if (ftueViewEvents.flowResult.missingStages.any { !it.isSupported() }) {
|
|
||||||
// Display a popup to propose use web fallback
|
|
||||||
onRegistrationStageNotSupported()
|
|
||||||
} else {
|
|
||||||
if (ftueViewEvents.isRegistrationStarted) {
|
|
||||||
// Go on with registration flow
|
|
||||||
handleRegistrationNavigation(ftueViewEvents.flowResult)
|
|
||||||
} else {
|
|
||||||
// First ask for login and password
|
|
||||||
// I add a tag to indicate that this fragment is a registration stage.
|
|
||||||
// This way it will be automatically popped in when starting the next registration stage
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginFragment::class.java,
|
|
||||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
|
||||||
option = commonOption
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is FTUEViewEvents.OutdatedHomeserver -> {
|
|
||||||
MaterialAlertDialogBuilder(activity)
|
|
||||||
.setTitle(R.string.login_error_outdated_homeserver_title)
|
|
||||||
.setMessage(R.string.login_error_outdated_homeserver_warning_content)
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show()
|
|
||||||
Unit
|
|
||||||
}
|
|
||||||
is FTUEViewEvents.OpenServerSelection ->
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginServerSelectionFragment::class.java,
|
|
||||||
option = { ft ->
|
|
||||||
activity.findViewById<View?>(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
|
|
||||||
// Disable transition of text
|
|
||||||
// findViewById<View?>(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
|
|
||||||
// No transition here now actually
|
|
||||||
// findViewById<View?>(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
|
|
||||||
// TODO Disabled because it provokes a flickering
|
|
||||||
// ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
|
|
||||||
})
|
|
||||||
is FTUEViewEvents.OnServerSelectionDone -> onServerSelectionDone(ftueViewEvents)
|
|
||||||
is FTUEViewEvents.OnSignModeSelected -> onSignModeSelected(ftueViewEvents)
|
|
||||||
is FTUEViewEvents.OnLoginFlowRetrieved ->
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginSignUpSignInSelectionFragment::class.java,
|
|
||||||
option = commonOption)
|
|
||||||
is FTUEViewEvents.OnWebLoginError -> onWebLoginError(ftueViewEvents)
|
|
||||||
is FTUEViewEvents.OnForgetPasswordClicked ->
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginResetPasswordFragment::class.java,
|
|
||||||
option = commonOption)
|
|
||||||
is FTUEViewEvents.OnResetPasswordSendThreePidDone -> {
|
|
||||||
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginResetPasswordMailConfirmationFragment::class.java,
|
|
||||||
option = commonOption)
|
|
||||||
}
|
|
||||||
is FTUEViewEvents.OnResetPasswordMailConfirmationSuccess -> {
|
|
||||||
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginResetPasswordSuccessFragment::class.java,
|
|
||||||
option = commonOption)
|
|
||||||
}
|
|
||||||
is FTUEViewEvents.OnResetPasswordMailConfirmationSuccessDone -> {
|
|
||||||
// Go back to the login fragment
|
|
||||||
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
|
|
||||||
}
|
|
||||||
is FTUEViewEvents.OnSendEmailSuccess -> {
|
|
||||||
// Pop the enter email Fragment
|
|
||||||
supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginWaitForEmailFragment::class.java,
|
|
||||||
LoginWaitForEmailFragmentArgument(ftueViewEvents.email),
|
|
||||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
}
|
|
||||||
is FTUEViewEvents.OnSendMsisdnSuccess -> {
|
|
||||||
// Pop the enter Msisdn Fragment
|
|
||||||
supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginGenericTextInputFormFragment::class.java,
|
|
||||||
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, ftueViewEvents.msisdn),
|
|
||||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
}
|
|
||||||
is FTUEViewEvents.Failure,
|
|
||||||
is FTUEViewEvents.Loading ->
|
|
||||||
// This is handled by the Fragments
|
|
||||||
Unit
|
|
||||||
}.exhaustive
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateWithState(ftueViewState: FTUEViewState) {
|
|
||||||
if (ftueViewState.isUserLogged()) {
|
|
||||||
val intent = HomeActivity.newIntent(
|
|
||||||
activity,
|
|
||||||
accountCreation = ftueViewState.signMode == SignMode.SignUp
|
|
||||||
)
|
|
||||||
activity.startActivity(intent)
|
|
||||||
activity.finish()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loading
|
|
||||||
views.loginLoading.isVisible = ftueViewState.isLoading()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onWebLoginError(onWebLoginError: FTUEViewEvents.OnWebLoginError) {
|
|
||||||
// Pop the backstack
|
|
||||||
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
|
||||||
|
|
||||||
// And inform the user
|
|
||||||
MaterialAlertDialogBuilder(activity)
|
|
||||||
.setTitle(R.string.dialog_title_error)
|
|
||||||
.setMessage(activity.getString(R.string.login_sso_error_message, onWebLoginError.description, onWebLoginError.errorCode))
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onServerSelectionDone(loginViewEvents: FTUEViewEvents.OnServerSelectionDone) {
|
|
||||||
when (loginViewEvents.serverType) {
|
|
||||||
ServerType.MatrixOrg -> Unit // In this case, we wait for the login flow
|
|
||||||
ServerType.EMS,
|
|
||||||
ServerType.Other -> activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginServerUrlFormFragment::class.java,
|
|
||||||
option = commonOption)
|
|
||||||
ServerType.Unknown -> Unit /* Should not happen */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onSignModeSelected(loginViewEvents: FTUEViewEvents.OnSignModeSelected) = withState(ftueViewModel) { state ->
|
|
||||||
// state.signMode could not be ready yet. So use value from the ViewEvent
|
|
||||||
when (loginViewEvents.signMode) {
|
|
||||||
SignMode.Unknown -> error("Sign mode has to be set before calling this method")
|
|
||||||
SignMode.SignUp -> {
|
|
||||||
// This is managed by the LoginViewEvents
|
|
||||||
}
|
|
||||||
SignMode.SignIn -> {
|
|
||||||
// It depends on the LoginMode
|
|
||||||
when (state.loginMode) {
|
|
||||||
LoginMode.Unknown,
|
|
||||||
is LoginMode.Sso -> error("Developer error")
|
|
||||||
is LoginMode.SsoAndPassword,
|
|
||||||
LoginMode.Password -> activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginFragment::class.java,
|
|
||||||
tag = FRAGMENT_LOGIN_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
LoginMode.Unsupported -> onLoginModeNotSupported(state.loginModeSupportedTypes)
|
|
||||||
}.exhaustive
|
|
||||||
}
|
|
||||||
SignMode.SignInWithMatrixId -> activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginFragment::class.java,
|
|
||||||
tag = FRAGMENT_LOGIN_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
}.exhaustive
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the SSO redirection here
|
|
||||||
*/
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
|
||||||
intent?.data
|
|
||||||
?.let { tryOrNull { it.getQueryParameter("loginToken") } }
|
|
||||||
?.let { ftueViewModel.handle(FTUEAction.LoginWithToken(it)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRegistrationStageNotSupported() {
|
|
||||||
MaterialAlertDialogBuilder(activity)
|
|
||||||
.setTitle(R.string.app_name)
|
|
||||||
.setMessage(activity.getString(R.string.login_registration_not_supported))
|
|
||||||
.setPositiveButton(R.string.yes) { _, _ ->
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginWebFragment::class.java,
|
|
||||||
option = commonOption)
|
|
||||||
}
|
|
||||||
.setNegativeButton(R.string.no, null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onLoginModeNotSupported(supportedTypes: List<String>) {
|
|
||||||
MaterialAlertDialogBuilder(activity)
|
|
||||||
.setTitle(R.string.app_name)
|
|
||||||
.setMessage(activity.getString(R.string.login_mode_not_supported, supportedTypes.joinToString { "'$it'" }))
|
|
||||||
.setPositiveButton(R.string.yes) { _, _ ->
|
|
||||||
activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginWebFragment::class.java,
|
|
||||||
option = commonOption)
|
|
||||||
}
|
|
||||||
.setNegativeButton(R.string.no, null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleRegistrationNavigation(flowResult: FlowResult) {
|
|
||||||
// Complete all mandatory stages first
|
|
||||||
val mandatoryStage = flowResult.missingStages.firstOrNull { it.mandatory }
|
|
||||||
|
|
||||||
if (mandatoryStage != null) {
|
|
||||||
doStage(mandatoryStage)
|
|
||||||
} else {
|
|
||||||
// Consider optional stages
|
|
||||||
val optionalStage = flowResult.missingStages.firstOrNull { !it.mandatory && it !is Stage.Dummy }
|
|
||||||
if (optionalStage == null) {
|
|
||||||
// Should not happen...
|
|
||||||
} else {
|
|
||||||
doStage(optionalStage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doStage(stage: Stage) {
|
|
||||||
// Ensure there is no fragment for registration stage in the backstack
|
|
||||||
supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
|
||||||
|
|
||||||
when (stage) {
|
|
||||||
is Stage.ReCaptcha -> activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginCaptchaFragment::class.java,
|
|
||||||
LoginCaptchaFragmentArgument(stage.publicKey),
|
|
||||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
is Stage.Email -> activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginGenericTextInputFormFragment::class.java,
|
|
||||||
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetEmail, stage.mandatory),
|
|
||||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
is Stage.Msisdn -> activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginGenericTextInputFormFragment::class.java,
|
|
||||||
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory),
|
|
||||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
is Stage.Terms -> activity.addFragmentToBackstack(R.id.loginFragmentContainer,
|
|
||||||
LoginTermsFragment::class.java,
|
|
||||||
LoginTermsFragmentArgument(stage.policies.toLocalizedLoginTerms(activity.getString(R.string.resources_language))),
|
|
||||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
|
||||||
option = commonOption)
|
|
||||||
else -> Unit // Should not happen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 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 im.vector.app.features.ftue
|
|
||||||
|
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
|
||||||
import im.vector.app.features.login.LoginConfig
|
|
||||||
import im.vector.app.features.login.ServerType
|
|
||||||
import im.vector.app.features.login.SignMode
|
|
||||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
|
||||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
|
||||||
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
|
|
||||||
|
|
||||||
sealed class FTUEAction : VectorViewModelAction {
|
|
||||||
data class OnGetStarted(val resetLoginConfig: Boolean) : FTUEAction()
|
|
||||||
|
|
||||||
data class UpdateServerType(val serverType: ServerType) : FTUEAction()
|
|
||||||
data class UpdateHomeServer(val homeServerUrl: String) : FTUEAction()
|
|
||||||
data class UpdateSignMode(val signMode: SignMode) : FTUEAction()
|
|
||||||
data class LoginWithToken(val loginToken: String) : FTUEAction()
|
|
||||||
data class WebLoginSuccess(val credentials: Credentials) : FTUEAction()
|
|
||||||
data class InitWith(val loginConfig: LoginConfig?) : FTUEAction()
|
|
||||||
data class ResetPassword(val email: String, val newPassword: String) : FTUEAction()
|
|
||||||
object ResetPasswordMailConfirmed : FTUEAction()
|
|
||||||
|
|
||||||
// Login or Register, depending on the signMode
|
|
||||||
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : FTUEAction()
|
|
||||||
|
|
||||||
// Register actions
|
|
||||||
open class RegisterAction : FTUEAction()
|
|
||||||
|
|
||||||
data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction()
|
|
||||||
object SendAgainThreePid : RegisterAction()
|
|
||||||
|
|
||||||
// TODO Confirm Email (from link in the email, open in the phone, intercepted by the app)
|
|
||||||
data class ValidateThreePid(val code: String) : RegisterAction()
|
|
||||||
|
|
||||||
data class CheckIfEmailHasBeenValidated(val delayMillis: Long) : RegisterAction()
|
|
||||||
object StopEmailValidationCheck : RegisterAction()
|
|
||||||
|
|
||||||
data class CaptchaDone(val captchaResponse: String) : RegisterAction()
|
|
||||||
object AcceptTerms : RegisterAction()
|
|
||||||
object RegisterDummy : RegisterAction()
|
|
||||||
|
|
||||||
// Reset actions
|
|
||||||
open class ResetAction : FTUEAction()
|
|
||||||
|
|
||||||
object ResetHomeServerType : ResetAction()
|
|
||||||
object ResetHomeServerUrl : ResetAction()
|
|
||||||
object ResetSignMode : ResetAction()
|
|
||||||
object ResetLogin : ResetAction()
|
|
||||||
object ResetResetPassword : ResetAction()
|
|
||||||
|
|
||||||
// Homeserver history
|
|
||||||
object ClearHomeServerHistory : FTUEAction()
|
|
||||||
|
|
||||||
// For the soft logout case
|
|
||||||
data class SetupSsoForSessionRecovery(val homeServerUrl: String,
|
|
||||||
val deviceId: String,
|
|
||||||
val ssoIdentityProviders: List<SsoIdentityProvider>?) : FTUEAction()
|
|
||||||
|
|
||||||
data class PostViewEvent(val viewEvent: FTUEViewEvents) : FTUEAction()
|
|
||||||
|
|
||||||
data class UserAcceptCertificate(val fingerprint: Fingerprint) : FTUEAction()
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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 im.vector.app.features.ftue
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import im.vector.app.core.extensions.lazyViewModel
|
|
||||||
import im.vector.app.core.platform.ToolbarConfigurable
|
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
|
||||||
import im.vector.app.core.platform.lifecycleAwareLazy
|
|
||||||
import im.vector.app.databinding.ActivityLoginBinding
|
|
||||||
import im.vector.app.features.login.LoginConfig
|
|
||||||
import im.vector.app.features.pin.UnlockedActivity
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class FTUEActivity : VectorBaseActivity<ActivityLoginBinding>(), ToolbarConfigurable, UnlockedActivity {
|
|
||||||
|
|
||||||
private val ftueVariant by lifecycleAwareLazy {
|
|
||||||
ftueVariantFactory.create(this, ftueViewModel = lazyViewModel(), loginViewModel2 = lazyViewModel())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject lateinit var ftueVariantFactory: FTUEVariantFactory
|
|
||||||
|
|
||||||
override fun getBinding() = ActivityLoginBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
override fun getCoordinatorLayout() = views.coordinatorLayout
|
|
||||||
|
|
||||||
override fun configure(toolbar: MaterialToolbar) {
|
|
||||||
configureToolbar(toolbar)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
ftueVariant.onNewIntent(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun initUiAndData() {
|
|
||||||
ftueVariant.initUiAndData(isFirstCreation())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hack for AccountCreatedFragment
|
|
||||||
fun setIsLoading(isLoading: Boolean) {
|
|
||||||
ftueVariant.setIsLoading(isLoading)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val EXTRA_CONFIG = "EXTRA_CONFIG"
|
|
||||||
|
|
||||||
fun newIntent(context: Context, loginConfig: LoginConfig?): Intent {
|
|
||||||
return Intent(context, FTUEActivity::class.java).apply {
|
|
||||||
putExtra(EXTRA_CONFIG, loginConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun redirectIntent(context: Context, data: Uri?): Intent {
|
|
||||||
return Intent(context, FTUEActivity::class.java).apply {
|
|
||||||
setData(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FTUEVariant {
|
|
||||||
fun onNewIntent(intent: Intent?)
|
|
||||||
fun initUiAndData(isFirstCreation: Boolean)
|
|
||||||
fun setIsLoading(isLoading: Boolean)
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2021 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 im.vector.app.features.ftue
|
|
||||||
|
|
||||||
import im.vector.app.features.VectorFeatures
|
|
||||||
import im.vector.app.features.login2.LoginViewModel2
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class FTUEVariantFactory @Inject constructor(
|
|
||||||
private val vectorFeatures: VectorFeatures,
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun create(activity: FTUEActivity, ftueViewModel: Lazy<FTUEViewModel>, loginViewModel2: Lazy<LoginViewModel2>) = when (vectorFeatures.loginVariant()) {
|
|
||||||
VectorFeatures.LoginVariant.LEGACY -> error("Legacy is not supported by the FTUE")
|
|
||||||
VectorFeatures.LoginVariant.FTUE -> DefaultFTUEVariant(
|
|
||||||
views = activity.getBinding(),
|
|
||||||
ftueViewModel = ftueViewModel.value,
|
|
||||||
activity = activity,
|
|
||||||
supportFragmentManager = activity.supportFragmentManager
|
|
||||||
)
|
|
||||||
VectorFeatures.LoginVariant.FTUE_WIP -> FTUEWipVariant(
|
|
||||||
views = activity.getBinding(),
|
|
||||||
loginViewModel = loginViewModel2.value,
|
|
||||||
activity = activity,
|
|
||||||
supportFragmentManager = activity.supportFragmentManager
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 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 im.vector.app.features.ftue
|
|
||||||
|
|
||||||
import im.vector.app.core.platform.VectorViewEvents
|
|
||||||
import im.vector.app.features.login.ServerType
|
|
||||||
import im.vector.app.features.login.SignMode
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transient events for Login
|
|
||||||
*/
|
|
||||||
sealed class FTUEViewEvents : VectorViewEvents {
|
|
||||||
data class Loading(val message: CharSequence? = null) : FTUEViewEvents()
|
|
||||||
data class Failure(val throwable: Throwable) : FTUEViewEvents()
|
|
||||||
|
|
||||||
data class RegistrationFlowResult(val flowResult: FlowResult, val isRegistrationStarted: Boolean) : FTUEViewEvents()
|
|
||||||
object OutdatedHomeserver : FTUEViewEvents()
|
|
||||||
|
|
||||||
// Navigation event
|
|
||||||
|
|
||||||
object OpenServerSelection : FTUEViewEvents()
|
|
||||||
data class OnServerSelectionDone(val serverType: ServerType) : FTUEViewEvents()
|
|
||||||
object OnLoginFlowRetrieved : FTUEViewEvents()
|
|
||||||
data class OnSignModeSelected(val signMode: SignMode) : FTUEViewEvents()
|
|
||||||
object OnForgetPasswordClicked : FTUEViewEvents()
|
|
||||||
object OnResetPasswordSendThreePidDone : FTUEViewEvents()
|
|
||||||
object OnResetPasswordMailConfirmationSuccess : FTUEViewEvents()
|
|
||||||
object OnResetPasswordMailConfirmationSuccessDone : FTUEViewEvents()
|
|
||||||
|
|
||||||
data class OnSendEmailSuccess(val email: String) : FTUEViewEvents()
|
|
||||||
data class OnSendMsisdnSuccess(val msisdn: String) : FTUEViewEvents()
|
|
||||||
|
|
||||||
data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : FTUEViewEvents()
|
|
||||||
}
|
|
|
@ -1,840 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 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 im.vector.app.features.ftue
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
|
||||||
import com.airbnb.mvrx.Fail
|
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
|
||||||
import com.airbnb.mvrx.Success
|
|
||||||
import com.airbnb.mvrx.Uninitialized
|
|
||||||
import dagger.assisted.Assisted
|
|
||||||
import dagger.assisted.AssistedFactory
|
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import im.vector.app.R
|
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
|
||||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
|
||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
|
||||||
import im.vector.app.core.extensions.configureAndStart
|
|
||||||
import im.vector.app.core.extensions.exhaustive
|
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
|
||||||
import im.vector.app.core.resources.StringProvider
|
|
||||||
import im.vector.app.core.utils.ensureTrailingSlash
|
|
||||||
import im.vector.app.features.login.HomeServerConnectionConfigFactory
|
|
||||||
import im.vector.app.features.login.LoginConfig
|
|
||||||
import im.vector.app.features.login.LoginMode
|
|
||||||
import im.vector.app.features.login.ReAuthHelper
|
|
||||||
import im.vector.app.features.login.ServerType
|
|
||||||
import im.vector.app.features.login.SignMode
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.matrix.android.sdk.api.MatrixPatterns.getDomain
|
|
||||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
|
||||||
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
|
||||||
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.FlowResult
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
|
||||||
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
|
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
|
||||||
import org.matrix.android.sdk.api.failure.MatrixIdFailure
|
|
||||||
import org.matrix.android.sdk.api.session.Session
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.concurrent.CancellationException
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class FTUEViewModel @AssistedInject constructor(
|
|
||||||
@Assisted initialState: FTUEViewState,
|
|
||||||
private val applicationContext: Context,
|
|
||||||
private val authenticationService: AuthenticationService,
|
|
||||||
private val activeSessionHolder: ActiveSessionHolder,
|
|
||||||
private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory,
|
|
||||||
private val reAuthHelper: ReAuthHelper,
|
|
||||||
private val stringProvider: StringProvider,
|
|
||||||
private val homeServerHistoryService: HomeServerHistoryService
|
|
||||||
) : VectorViewModel<FTUEViewState, FTUEAction, FTUEViewEvents>(initialState) {
|
|
||||||
|
|
||||||
@AssistedFactory
|
|
||||||
interface Factory : MavericksAssistedViewModelFactory<FTUEViewModel, FTUEViewState> {
|
|
||||||
override fun create(initialState: FTUEViewState): FTUEViewModel
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : MavericksViewModelFactory<FTUEViewModel, FTUEViewState> by hiltMavericksViewModelFactory()
|
|
||||||
|
|
||||||
init {
|
|
||||||
getKnownCustomHomeServersUrls()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getKnownCustomHomeServersUrls() {
|
|
||||||
setState {
|
|
||||||
copy(knownCustomHomeServersUrls = homeServerHistoryService.getKnownServersUrls())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the last action, to redo it after user has trusted the untrusted certificate
|
|
||||||
private var lastAction: FTUEAction? = null
|
|
||||||
private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null
|
|
||||||
|
|
||||||
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
|
|
||||||
|
|
||||||
val currentThreePid: String?
|
|
||||||
get() = registrationWizard?.currentThreePid
|
|
||||||
|
|
||||||
// True when login and password has been sent with success to the homeserver
|
|
||||||
val isRegistrationStarted: Boolean
|
|
||||||
get() = authenticationService.isRegistrationStarted
|
|
||||||
|
|
||||||
private val registrationWizard: RegistrationWizard?
|
|
||||||
get() = authenticationService.getRegistrationWizard()
|
|
||||||
|
|
||||||
private val loginWizard: LoginWizard?
|
|
||||||
get() = authenticationService.getLoginWizard()
|
|
||||||
|
|
||||||
private var loginConfig: LoginConfig? = null
|
|
||||||
|
|
||||||
private var currentJob: Job? = null
|
|
||||||
set(value) {
|
|
||||||
// Cancel any previous Job
|
|
||||||
field?.cancel()
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handle(action: FTUEAction) {
|
|
||||||
when (action) {
|
|
||||||
is FTUEAction.OnGetStarted -> handleOnGetStarted(action)
|
|
||||||
is FTUEAction.UpdateServerType -> handleUpdateServerType(action)
|
|
||||||
is FTUEAction.UpdateSignMode -> handleUpdateSignMode(action)
|
|
||||||
is FTUEAction.InitWith -> handleInitWith(action)
|
|
||||||
is FTUEAction.UpdateHomeServer -> handleUpdateHomeserver(action).also { lastAction = action }
|
|
||||||
is FTUEAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
|
|
||||||
is FTUEAction.LoginWithToken -> handleLoginWithToken(action)
|
|
||||||
is FTUEAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
|
||||||
is FTUEAction.ResetPassword -> handleResetPassword(action)
|
|
||||||
is FTUEAction.ResetPasswordMailConfirmed -> handleResetPasswordMailConfirmed()
|
|
||||||
is FTUEAction.RegisterAction -> handleRegisterAction(action)
|
|
||||||
is FTUEAction.ResetAction -> handleResetAction(action)
|
|
||||||
is FTUEAction.SetupSsoForSessionRecovery -> handleSetupSsoForSessionRecovery(action)
|
|
||||||
is FTUEAction.UserAcceptCertificate -> handleUserAcceptCertificate(action)
|
|
||||||
FTUEAction.ClearHomeServerHistory -> handleClearHomeServerHistory()
|
|
||||||
is FTUEAction.PostViewEvent -> _viewEvents.post(action.viewEvent)
|
|
||||||
}.exhaustive
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleOnGetStarted(action: FTUEAction.OnGetStarted) {
|
|
||||||
if (action.resetLoginConfig) {
|
|
||||||
loginConfig = null
|
|
||||||
}
|
|
||||||
|
|
||||||
val configUrl = loginConfig?.homeServerUrl?.takeIf { it.isNotEmpty() }
|
|
||||||
if (configUrl != null) {
|
|
||||||
// Use config from uri
|
|
||||||
val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(configUrl)
|
|
||||||
if (homeServerConnectionConfig == null) {
|
|
||||||
// Url is invalid, in this case, just use the regular flow
|
|
||||||
Timber.w("Url from config url was invalid: $configUrl")
|
|
||||||
_viewEvents.post(FTUEViewEvents.OpenServerSelection)
|
|
||||||
} else {
|
|
||||||
getLoginFlow(homeServerConnectionConfig, ServerType.Other)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_viewEvents.post(FTUEViewEvents.OpenServerSelection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleUserAcceptCertificate(action: FTUEAction.UserAcceptCertificate) {
|
|
||||||
// It happens when we get the login flow, or during direct authentication.
|
|
||||||
// So alter the homeserver config and retrieve again the login flow
|
|
||||||
when (val finalLastAction = lastAction) {
|
|
||||||
is FTUEAction.UpdateHomeServer -> {
|
|
||||||
currentHomeServerConnectionConfig
|
|
||||||
?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
|
|
||||||
?.let { getLoginFlow(it) }
|
|
||||||
}
|
|
||||||
is FTUEAction.LoginOrRegister ->
|
|
||||||
handleDirectLogin(
|
|
||||||
finalLastAction,
|
|
||||||
HomeServerConnectionConfig.Builder()
|
|
||||||
// Will be replaced by the task
|
|
||||||
.withHomeServerUri("https://dummy.org")
|
|
||||||
.withAllowedFingerPrints(listOf(action.fingerprint))
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun rememberHomeServer(homeServerUrl: String) {
|
|
||||||
homeServerHistoryService.addHomeServerToHistory(homeServerUrl)
|
|
||||||
getKnownCustomHomeServersUrls()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleClearHomeServerHistory() {
|
|
||||||
homeServerHistoryService.clearHistory()
|
|
||||||
getKnownCustomHomeServersUrls()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleLoginWithToken(action: FTUEAction.LoginWithToken) {
|
|
||||||
val safeLoginWizard = loginWizard
|
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(Throwable("Bad configuration"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Loading()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
safeLoginWizard.loginWithToken(action.loginToken)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(failure))
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
?.let { onSessionCreated(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleSetupSsoForSessionRecovery(action: FTUEAction.SetupSsoForSessionRecovery) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
signMode = SignMode.SignIn,
|
|
||||||
loginMode = LoginMode.Sso(action.ssoIdentityProviders),
|
|
||||||
homeServerUrlFromUser = action.homeServerUrl,
|
|
||||||
homeServerUrl = action.homeServerUrl,
|
|
||||||
deviceId = action.deviceId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleRegisterAction(action: FTUEAction.RegisterAction) {
|
|
||||||
when (action) {
|
|
||||||
is FTUEAction.CaptchaDone -> handleCaptchaDone(action)
|
|
||||||
is FTUEAction.AcceptTerms -> handleAcceptTerms()
|
|
||||||
is FTUEAction.RegisterDummy -> handleRegisterDummy()
|
|
||||||
is FTUEAction.AddThreePid -> handleAddThreePid(action)
|
|
||||||
is FTUEAction.SendAgainThreePid -> handleSendAgainThreePid()
|
|
||||||
is FTUEAction.ValidateThreePid -> handleValidateThreePid(action)
|
|
||||||
is FTUEAction.CheckIfEmailHasBeenValidated -> handleCheckIfEmailHasBeenValidated(action)
|
|
||||||
is FTUEAction.StopEmailValidationCheck -> handleStopEmailValidationCheck()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleCheckIfEmailHasBeenValidated(action: FTUEAction.CheckIfEmailHasBeenValidated) {
|
|
||||||
// We do not want the common progress bar to be displayed, so we do not change asyncRegistration value in the state
|
|
||||||
currentJob = executeRegistrationStep(withLoading = false) {
|
|
||||||
it.checkIfEmailHasBeenValidated(action.delayMillis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleStopEmailValidationCheck() {
|
|
||||||
currentJob = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleValidateThreePid(action: FTUEAction.ValidateThreePid) {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.handleValidateThreePid(action.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun executeRegistrationStep(withLoading: Boolean = true,
|
|
||||||
block: suspend (RegistrationWizard) -> RegistrationResult): Job {
|
|
||||||
if (withLoading) {
|
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
|
||||||
}
|
|
||||||
return viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
registrationWizard?.let { block(it) }
|
|
||||||
/*
|
|
||||||
// Simulate registration disabled
|
|
||||||
throw Failure.ServerError(MatrixError(
|
|
||||||
code = MatrixError.FORBIDDEN,
|
|
||||||
message = "Registration is disabled"
|
|
||||||
), 403))
|
|
||||||
*/
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
if (failure !is CancellationException) {
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(failure))
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
?.let { data ->
|
|
||||||
when (data) {
|
|
||||||
is RegistrationResult.Success -> onSessionCreated(data.session)
|
|
||||||
is RegistrationResult.FlowResponse -> onFlowResponse(data.flowResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncRegistration = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAddThreePid(action: FTUEAction.AddThreePid) {
|
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
registrationWizard?.addThreePid(action.threePid)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(failure))
|
|
||||||
}
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncRegistration = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleSendAgainThreePid() {
|
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
registrationWizard?.sendAgainThreePid()
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(failure))
|
|
||||||
}
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncRegistration = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleAcceptTerms() {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.acceptTerms()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleRegisterDummy() {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.dummy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleRegisterWith(action: FTUEAction.LoginOrRegister) {
|
|
||||||
reAuthHelper.data = action.password
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.createAccount(
|
|
||||||
action.username,
|
|
||||||
action.password,
|
|
||||||
action.initialDeviceName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleCaptchaDone(action: FTUEAction.CaptchaDone) {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.performReCaptcha(action.captchaResponse)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleResetAction(action: FTUEAction.ResetAction) {
|
|
||||||
// Cancel any request
|
|
||||||
currentJob = null
|
|
||||||
|
|
||||||
when (action) {
|
|
||||||
FTUEAction.ResetHomeServerType -> {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
serverType = ServerType.Unknown
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FTUEAction.ResetHomeServerUrl -> {
|
|
||||||
viewModelScope.launch {
|
|
||||||
authenticationService.reset()
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
|
||||||
homeServerUrlFromUser = null,
|
|
||||||
homeServerUrl = null,
|
|
||||||
loginMode = LoginMode.Unknown,
|
|
||||||
serverType = ServerType.Unknown,
|
|
||||||
loginModeSupportedTypes = emptyList()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FTUEAction.ResetSignMode -> {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
|
||||||
signMode = SignMode.Unknown,
|
|
||||||
loginMode = LoginMode.Unknown,
|
|
||||||
loginModeSupportedTypes = emptyList()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FTUEAction.ResetLogin -> {
|
|
||||||
viewModelScope.launch {
|
|
||||||
authenticationService.cancelPendingLoginOrRegistration()
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Uninitialized,
|
|
||||||
asyncRegistration = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FTUEAction.ResetResetPassword -> {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetPassword = Uninitialized,
|
|
||||||
asyncResetMailConfirmed = Uninitialized,
|
|
||||||
resetPasswordEmail = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleUpdateSignMode(action: FTUEAction.UpdateSignMode) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
signMode = action.signMode
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
when (action.signMode) {
|
|
||||||
SignMode.SignUp -> startRegistrationFlow()
|
|
||||||
SignMode.SignIn -> startAuthenticationFlow()
|
|
||||||
SignMode.SignInWithMatrixId -> _viewEvents.post(FTUEViewEvents.OnSignModeSelected(SignMode.SignInWithMatrixId))
|
|
||||||
SignMode.Unknown -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleUpdateServerType(action: FTUEAction.UpdateServerType) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
serverType = action.serverType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
when (action.serverType) {
|
|
||||||
ServerType.Unknown -> Unit /* Should not happen */
|
|
||||||
ServerType.MatrixOrg ->
|
|
||||||
// Request login flow here
|
|
||||||
handle(FTUEAction.UpdateHomeServer(matrixOrgUrl))
|
|
||||||
ServerType.EMS,
|
|
||||||
ServerType.Other -> _viewEvents.post(FTUEViewEvents.OnServerSelectionDone(action.serverType))
|
|
||||||
}.exhaustive
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleInitWith(action: FTUEAction.InitWith) {
|
|
||||||
loginConfig = action.loginConfig
|
|
||||||
|
|
||||||
// If there is a pending email validation continue on this step
|
|
||||||
try {
|
|
||||||
if (registrationWizard?.isRegistrationStarted == true) {
|
|
||||||
currentThreePid?.let {
|
|
||||||
handle(FTUEAction.PostViewEvent(FTUEViewEvents.OnSendEmailSuccess(it)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
// NOOP. API is designed to use wizards in a login/registration flow,
|
|
||||||
// but we need to check the state anyway.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleResetPassword(action: FTUEAction.ResetPassword) {
|
|
||||||
val safeLoginWizard = loginWizard
|
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetPassword = Fail(Throwable("Bad configuration")),
|
|
||||||
asyncResetMailConfirmed = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetPassword = Loading(),
|
|
||||||
asyncResetMailConfirmed = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
safeLoginWizard.resetPassword(action.email, action.newPassword)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetPassword = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetPassword = Success(Unit),
|
|
||||||
resetPasswordEmail = action.email
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
_viewEvents.post(FTUEViewEvents.OnResetPasswordSendThreePidDone)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleResetPasswordMailConfirmed() {
|
|
||||||
val safeLoginWizard = loginWizard
|
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetPassword = Uninitialized,
|
|
||||||
asyncResetMailConfirmed = Fail(Throwable("Bad configuration"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetPassword = Uninitialized,
|
|
||||||
asyncResetMailConfirmed = Loading()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
safeLoginWizard.resetPasswordMailConfirmed()
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetMailConfirmed = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncResetMailConfirmed = Success(Unit),
|
|
||||||
resetPasswordEmail = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
_viewEvents.post(FTUEViewEvents.OnResetPasswordMailConfirmationSuccess)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleLoginOrRegister(action: FTUEAction.LoginOrRegister) = withState { state ->
|
|
||||||
when (state.signMode) {
|
|
||||||
SignMode.Unknown -> error("Developer error, invalid sign mode")
|
|
||||||
SignMode.SignIn -> handleLogin(action)
|
|
||||||
SignMode.SignUp -> handleRegisterWith(action)
|
|
||||||
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
|
|
||||||
}.exhaustive
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleDirectLogin(action: FTUEAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Loading()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
val data = try {
|
|
||||||
authenticationService.getWellKnownData(action.username, homeServerConnectionConfig)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
onDirectLoginError(failure)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
when (data) {
|
|
||||||
is WellknownResult.Prompt ->
|
|
||||||
onWellknownSuccess(action, data, homeServerConnectionConfig)
|
|
||||||
is WellknownResult.FailPrompt ->
|
|
||||||
// Relax on IS discovery if homeserver is valid
|
|
||||||
if (data.homeServerUrl != null && data.wellKnown != null) {
|
|
||||||
onWellknownSuccess(action, WellknownResult.Prompt(data.homeServerUrl!!, null, data.wellKnown!!), homeServerConnectionConfig)
|
|
||||||
} else {
|
|
||||||
onWellKnownError()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
onWellKnownError()
|
|
||||||
}
|
|
||||||
}.exhaustive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onWellKnownError() {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(Exception(stringProvider.getString(R.string.autodiscover_well_known_error))))
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun onWellknownSuccess(action: FTUEAction.LoginOrRegister,
|
|
||||||
wellKnownPrompt: WellknownResult.Prompt,
|
|
||||||
homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
|
||||||
val alteredHomeServerConnectionConfig = homeServerConnectionConfig
|
|
||||||
?.copy(
|
|
||||||
homeServerUriBase = Uri.parse(wellKnownPrompt.homeServerUrl),
|
|
||||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
|
|
||||||
)
|
|
||||||
?: HomeServerConnectionConfig(
|
|
||||||
homeServerUri = Uri.parse("https://${action.username.getDomain()}"),
|
|
||||||
homeServerUriBase = Uri.parse(wellKnownPrompt.homeServerUrl),
|
|
||||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
|
|
||||||
)
|
|
||||||
|
|
||||||
val data = try {
|
|
||||||
authenticationService.directAuthentication(
|
|
||||||
alteredHomeServerConnectionConfig,
|
|
||||||
action.username,
|
|
||||||
action.password,
|
|
||||||
action.initialDeviceName)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
onDirectLoginError(failure)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
onSessionCreated(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onDirectLoginError(failure: Throwable) {
|
|
||||||
when (failure) {
|
|
||||||
is MatrixIdFailure.InvalidMatrixId,
|
|
||||||
is Failure.UnrecognizedCertificateFailure -> {
|
|
||||||
// Display this error in a dialog
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(failure))
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleLogin(action: FTUEAction.LoginOrRegister) {
|
|
||||||
val safeLoginWizard = loginWizard
|
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(Throwable("Bad configuration"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Loading()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
safeLoginWizard.login(
|
|
||||||
action.username,
|
|
||||||
action.password,
|
|
||||||
action.initialDeviceName
|
|
||||||
)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
?.let {
|
|
||||||
reAuthHelper.data = action.password
|
|
||||||
onSessionCreated(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startRegistrationFlow() {
|
|
||||||
currentJob = executeRegistrationStep {
|
|
||||||
it.getRegistrationFlow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startAuthenticationFlow() {
|
|
||||||
// Ensure Wizard is ready
|
|
||||||
loginWizard
|
|
||||||
|
|
||||||
_viewEvents.post(FTUEViewEvents.OnSignModeSelected(SignMode.SignIn))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onFlowResponse(flowResult: FlowResult) {
|
|
||||||
// If dummy stage is mandatory, and password is already sent, do the dummy stage now
|
|
||||||
if (isRegistrationStarted &&
|
|
||||||
flowResult.missingStages.any { it is Stage.Dummy && it.mandatory }) {
|
|
||||||
handleRegisterDummy()
|
|
||||||
} else {
|
|
||||||
// Notify the user
|
|
||||||
_viewEvents.post(FTUEViewEvents.RegistrationFlowResult(flowResult, isRegistrationStarted))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun onSessionCreated(session: Session) {
|
|
||||||
activeSessionHolder.setActiveSession(session)
|
|
||||||
|
|
||||||
authenticationService.reset()
|
|
||||||
session.configureAndStart(applicationContext)
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncLoginAction = Success(Unit)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleWebLoginSuccess(action: FTUEAction.WebLoginSuccess) = withState { state ->
|
|
||||||
val homeServerConnectionConfigFinal = homeServerConnectionConfigFactory.create(state.homeServerUrl)
|
|
||||||
|
|
||||||
if (homeServerConnectionConfigFinal == null) {
|
|
||||||
// Should not happen
|
|
||||||
Timber.w("homeServerConnectionConfig is null")
|
|
||||||
} else {
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
authenticationService.createSessionFromSso(homeServerConnectionConfigFinal, action.credentials)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
setState {
|
|
||||||
copy(asyncLoginAction = Fail(failure))
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
?.let { onSessionCreated(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleUpdateHomeserver(action: FTUEAction.UpdateHomeServer) {
|
|
||||||
val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(action.homeServerUrl)
|
|
||||||
if (homeServerConnectionConfig == null) {
|
|
||||||
// This is invalid
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
|
|
||||||
} else {
|
|
||||||
getLoginFlow(homeServerConnectionConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig,
|
|
||||||
serverTypeOverride: ServerType? = null) {
|
|
||||||
currentHomeServerConnectionConfig = homeServerConnectionConfig
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
|
||||||
authenticationService.cancelPendingLoginOrRegistration()
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncHomeServerLoginFlowRequest = Loading(),
|
|
||||||
// If user has entered https://matrix.org, ensure that server type is ServerType.MatrixOrg
|
|
||||||
// It is also useful to set the value again in the case of a certificate error on matrix.org
|
|
||||||
serverType = if (homeServerConnectionConfig.homeServerUri.toString() == matrixOrgUrl) {
|
|
||||||
ServerType.MatrixOrg
|
|
||||||
} else {
|
|
||||||
serverTypeOverride ?: serverType
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val data = try {
|
|
||||||
authenticationService.getLoginFlow(homeServerConnectionConfig)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(FTUEViewEvents.Failure(failure))
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
|
||||||
// If we were trying to retrieve matrix.org login flow, also reset the serverType
|
|
||||||
serverType = if (serverType == ServerType.MatrixOrg) ServerType.Unknown else serverType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
data ?: return@launch
|
|
||||||
|
|
||||||
// Valid Homeserver, add it to the history.
|
|
||||||
// Note: we add what the user has input, data.homeServerUrlBase can be different
|
|
||||||
rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString())
|
|
||||||
|
|
||||||
val loginMode = when {
|
|
||||||
// SSO login is taken first
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) &&
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
|
|
||||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
|
||||||
else -> LoginMode.Unsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
|
||||||
homeServerUrlFromUser = homeServerConnectionConfig.homeServerUri.toString(),
|
|
||||||
homeServerUrl = data.homeServerUrl,
|
|
||||||
loginMode = loginMode,
|
|
||||||
loginModeSupportedTypes = data.supportedLoginTypes.toList()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) ||
|
|
||||||
data.isOutdatedHomeserver) {
|
|
||||||
// Notify the UI
|
|
||||||
_viewEvents.post(FTUEViewEvents.OutdatedHomeserver)
|
|
||||||
}
|
|
||||||
_viewEvents.post(FTUEViewEvents.OnLoginFlowRetrieved)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getInitialHomeServerUrl(): String? {
|
|
||||||
return loginConfig?.homeServerUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
|
|
||||||
return authenticationService.getSsoUrl(redirectUrl, deviceId, providerId)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
|
|
||||||
return authenticationService.getFallbackUrl(forSignIn, deviceId)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2019 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 im.vector.app.features.ftue
|
|
||||||
|
|
||||||
import com.airbnb.mvrx.Async
|
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import com.airbnb.mvrx.MavericksState
|
|
||||||
import com.airbnb.mvrx.PersistState
|
|
||||||
import com.airbnb.mvrx.Success
|
|
||||||
import com.airbnb.mvrx.Uninitialized
|
|
||||||
import im.vector.app.features.login.LoginMode
|
|
||||||
import im.vector.app.features.login.ServerType
|
|
||||||
import im.vector.app.features.login.SignMode
|
|
||||||
|
|
||||||
data class FTUEViewState(
|
|
||||||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
|
||||||
val asyncHomeServerLoginFlowRequest: Async<Unit> = Uninitialized,
|
|
||||||
val asyncResetPassword: Async<Unit> = Uninitialized,
|
|
||||||
val asyncResetMailConfirmed: Async<Unit> = Uninitialized,
|
|
||||||
val asyncRegistration: Async<Unit> = Uninitialized,
|
|
||||||
|
|
||||||
// User choices
|
|
||||||
@PersistState
|
|
||||||
val serverType: ServerType = ServerType.Unknown,
|
|
||||||
@PersistState
|
|
||||||
val signMode: SignMode = SignMode.Unknown,
|
|
||||||
@PersistState
|
|
||||||
val resetPasswordEmail: String? = null,
|
|
||||||
@PersistState
|
|
||||||
val homeServerUrlFromUser: String? = null,
|
|
||||||
|
|
||||||
// Can be modified after a Wellknown request
|
|
||||||
@PersistState
|
|
||||||
val homeServerUrl: String? = null,
|
|
||||||
|
|
||||||
// For SSO session recovery
|
|
||||||
@PersistState
|
|
||||||
val deviceId: String? = null,
|
|
||||||
|
|
||||||
// Network result
|
|
||||||
@PersistState
|
|
||||||
val loginMode: LoginMode = LoginMode.Unknown,
|
|
||||||
// Supported types for the login. We cannot use a sealed class for LoginType because it is not serializable
|
|
||||||
@PersistState
|
|
||||||
val loginModeSupportedTypes: List<String> = emptyList(),
|
|
||||||
val knownCustomHomeServersUrls: List<String> = emptyList()
|
|
||||||
) : MavericksState {
|
|
||||||
|
|
||||||
fun isLoading(): Boolean {
|
|
||||||
return asyncLoginAction is Loading ||
|
|
||||||
asyncHomeServerLoginFlowRequest is Loading ||
|
|
||||||
asyncResetPassword is Loading ||
|
|
||||||
asyncResetMailConfirmed is Loading ||
|
|
||||||
asyncRegistration is Loading ||
|
|
||||||
// Keep loading when it is success because of the delay to switch to the next Activity
|
|
||||||
asyncLoginAction is Success
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isUserLogged(): Boolean {
|
|
||||||
return asyncLoginAction is Success
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -34,7 +34,6 @@ import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.databinding.DialogBaseEditTextBinding
|
import im.vector.app.databinding.DialogBaseEditTextBinding
|
||||||
import im.vector.app.databinding.FragmentLoginAccountCreatedBinding
|
import im.vector.app.databinding.FragmentLoginAccountCreatedBinding
|
||||||
import im.vector.app.features.displayname.getBestName
|
import im.vector.app.features.displayname.getBestName
|
||||||
import im.vector.app.features.ftue.FTUEActivity
|
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||||
import im.vector.app.features.login2.AbstractLoginFragment2
|
import im.vector.app.features.login2.AbstractLoginFragment2
|
||||||
|
|
|
@ -52,7 +52,6 @@ import im.vector.app.features.crypto.verification.SupportedVerificationMethodsPr
|
||||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||||
import im.vector.app.features.debug.DebugMenuActivity
|
import im.vector.app.features.debug.DebugMenuActivity
|
||||||
import im.vector.app.features.devtools.RoomDevToolActivity
|
import im.vector.app.features.devtools.RoomDevToolActivity
|
||||||
import im.vector.app.features.ftue.FTUEActivity
|
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
||||||
import im.vector.app.features.home.room.detail.search.SearchActivity
|
import im.vector.app.features.home.room.detail.search.SearchActivity
|
||||||
|
|
Loading…
Reference in New Issue