diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index 9e55e29c74..cc31a7dca6 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -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.discovery.DiscoverySettingsViewModel 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.HomeDetailViewModel import im.vector.app.features.home.PromoteRestrictedViewModel diff --git a/vector/src/main/java/im/vector/app/features/ftue/DefaultFTUEVariant.kt b/vector/src/main/java/im/vector/app/features/ftue/DefaultFTUEVariant.kt deleted file mode 100644 index 62f592ea2c..0000000000 --- a/vector/src/main/java/im/vector/app/features/ftue/DefaultFTUEVariant.kt +++ /dev/null @@ -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, - 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(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(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(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // Disable transition of text - // findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // No transition here now actually - // findViewById(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) { - 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 - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/ftue/FTUEAction.kt b/vector/src/main/java/im/vector/app/features/ftue/FTUEAction.kt deleted file mode 100644 index b43ffd3331..0000000000 --- a/vector/src/main/java/im/vector/app/features/ftue/FTUEAction.kt +++ /dev/null @@ -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?) : FTUEAction() - - data class PostViewEvent(val viewEvent: FTUEViewEvents) : FTUEAction() - - data class UserAcceptCertificate(val fingerprint: Fingerprint) : FTUEAction() -} diff --git a/vector/src/main/java/im/vector/app/features/ftue/FTUEActivity.kt b/vector/src/main/java/im/vector/app/features/ftue/FTUEActivity.kt deleted file mode 100644 index c54c0547a4..0000000000 --- a/vector/src/main/java/im/vector/app/features/ftue/FTUEActivity.kt +++ /dev/null @@ -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(), 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) -} diff --git a/vector/src/main/java/im/vector/app/features/ftue/FTUEVariantFactory.kt b/vector/src/main/java/im/vector/app/features/ftue/FTUEVariantFactory.kt deleted file mode 100644 index aabd0338a2..0000000000 --- a/vector/src/main/java/im/vector/app/features/ftue/FTUEVariantFactory.kt +++ /dev/null @@ -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, loginViewModel2: Lazy) = 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 - ) - } -} diff --git a/vector/src/main/java/im/vector/app/features/ftue/FTUEViewEvents.kt b/vector/src/main/java/im/vector/app/features/ftue/FTUEViewEvents.kt deleted file mode 100644 index d10063c797..0000000000 --- a/vector/src/main/java/im/vector/app/features/ftue/FTUEViewEvents.kt +++ /dev/null @@ -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() -} diff --git a/vector/src/main/java/im/vector/app/features/ftue/FTUEViewModel.kt b/vector/src/main/java/im/vector/app/features/ftue/FTUEViewModel.kt deleted file mode 100644 index 4c78d6fd30..0000000000 --- a/vector/src/main/java/im/vector/app/features/ftue/FTUEViewModel.kt +++ /dev/null @@ -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(initialState) { - - @AssistedFactory - interface Factory : MavericksAssistedViewModelFactory { - override fun create(initialState: FTUEViewState): FTUEViewModel - } - - companion object : MavericksViewModelFactory 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) - } -} diff --git a/vector/src/main/java/im/vector/app/features/ftue/FTUEViewState.kt b/vector/src/main/java/im/vector/app/features/ftue/FTUEViewState.kt deleted file mode 100644 index 9f34ed3782..0000000000 --- a/vector/src/main/java/im/vector/app/features/ftue/FTUEViewState.kt +++ /dev/null @@ -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 = Uninitialized, - val asyncHomeServerLoginFlowRequest: Async = Uninitialized, - val asyncResetPassword: Async = Uninitialized, - val asyncResetMailConfirmed: Async = Uninitialized, - val asyncRegistration: Async = 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 = emptyList(), - val knownCustomHomeServersUrls: List = 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 - } -} diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt index 9127e3400e..8223053ad8 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt @@ -34,7 +34,6 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.FragmentLoginAccountCreatedBinding 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.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.login2.AbstractLoginFragment2 diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index dbf0024ab6..30ead8a6bf 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -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.debug.DebugMenuActivity 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.RoomDetailArgs import im.vector.app.features.home.room.detail.search.SearchActivity