Soft Logout - request homeserver login flow
This commit is contained in:
parent
a464c910f8
commit
6811d31a6d
|
@ -41,6 +41,7 @@ class ErrorFormatter @Inject constructor(private val stringProvider: StringProvi
|
|||
stringProvider.getString(R.string.error_network_timeout)
|
||||
throwable.ioException is UnknownHostException ->
|
||||
// Invalid homeserver?
|
||||
// TODO Check network state, airplane mode, etc.
|
||||
stringProvider.getString(R.string.login_error_unknown_host)
|
||||
else ->
|
||||
stringProvider.getString(R.string.error_no_network)
|
||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.riotx.features.signout
|
|||
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class SoftLogoutAction : VectorViewModelAction {
|
||||
object RetryLoginFlow : SoftLogoutAction()
|
||||
data class SignInAgain(val password: String) : SoftLogoutAction()
|
||||
// TODO Add reset pwd...
|
||||
}
|
||||
|
|
|
@ -30,18 +30,22 @@ import im.vector.riotx.R
|
|||
import im.vector.riotx.core.dialogs.withColoredButton
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import im.vector.riotx.core.extensions.hideKeyboard
|
||||
import im.vector.riotx.core.extensions.setTextOrHide
|
||||
import im.vector.riotx.core.extensions.showPassword
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.MainActivity
|
||||
import im.vector.riotx.features.MainActivityArgs
|
||||
import im.vector.riotx.features.login.LoginMode
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import kotlinx.android.synthetic.main.fragment_soft_logout.*
|
||||
import kotlinx.android.synthetic.main.item_error_retry.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* In this screen:
|
||||
* - the user is asked to enter a password to sign in again to a homeserver.
|
||||
* - or to cleanup all the data
|
||||
* TODO: migrate to Epoxy (along with all the login screen?)
|
||||
*/
|
||||
class SoftLogoutFragment @Inject constructor(
|
||||
private val errorFormatter: ErrorFormatter
|
||||
|
@ -67,6 +71,11 @@ class SoftLogoutFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.itemErrorRetryButton)
|
||||
fun retry() {
|
||||
softLogoutViewModel.handle(SoftLogoutAction.RetryLoginFlow)
|
||||
}
|
||||
|
||||
@OnClick(R.id.softLogoutSubmit)
|
||||
fun submit() {
|
||||
cleanupUi()
|
||||
|
@ -75,29 +84,36 @@ class SoftLogoutFragment @Inject constructor(
|
|||
softLogoutViewModel.handle(SoftLogoutAction.SignInAgain(password))
|
||||
}
|
||||
|
||||
@OnClick(R.id.softLogoutFormSsoSubmit)
|
||||
fun ssoSubmit() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@OnClick(R.id.softLogoutClearDataSubmit)
|
||||
fun clearData() = withState(softLogoutViewModel) { state ->
|
||||
cleanupUi()
|
||||
fun clearData() {
|
||||
withState(softLogoutViewModel) { state ->
|
||||
cleanupUi()
|
||||
|
||||
val messageResId = if (state.hasUnsavedKeys) {
|
||||
R.string.soft_logout_clear_data_dialog_content
|
||||
} else {
|
||||
R.string.soft_logout_clear_data_dialog_e2e_warning_content
|
||||
val messageResId = if (state.hasUnsavedKeys) {
|
||||
R.string.soft_logout_clear_data_dialog_content
|
||||
} else {
|
||||
R.string.soft_logout_clear_data_dialog_e2e_warning_content
|
||||
}
|
||||
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.soft_logout_clear_data_dialog_title)
|
||||
.setMessage(messageResId)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.soft_logout_clear_data_submit) { _, _ ->
|
||||
MainActivity.restartApp(requireActivity(), MainActivityArgs(
|
||||
clearCache = true,
|
||||
clearCredentials = true,
|
||||
isUserLoggedOut = true
|
||||
))
|
||||
}
|
||||
.show()
|
||||
.withColoredButton(DialogInterface.BUTTON_POSITIVE)
|
||||
}
|
||||
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.soft_logout_clear_data_dialog_title)
|
||||
.setMessage(messageResId)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.soft_logout_clear_data_submit) { _, _ ->
|
||||
MainActivity.restartApp(requireActivity(), MainActivityArgs(
|
||||
clearCache = true,
|
||||
clearCredentials = true,
|
||||
isUserLoggedOut = true
|
||||
))
|
||||
}
|
||||
.show()
|
||||
.withColoredButton(DialogInterface.BUTTON_POSITIVE)
|
||||
}
|
||||
|
||||
private fun cleanupUi() {
|
||||
|
@ -114,6 +130,14 @@ class SoftLogoutFragment @Inject constructor(
|
|||
softLogoutE2eWarningNotice.isVisible = state.hasUnsavedKeys
|
||||
}
|
||||
|
||||
private fun setupForm(state: SoftLogoutViewState) {
|
||||
softLogoutFormLoading.isVisible = state.asyncHomeServerLoginFlowRequest is Loading
|
||||
softLogoutFormSsoSubmit.isVisible = state.asyncHomeServerLoginFlowRequest.invoke() == LoginMode.Sso
|
||||
softLogoutFormPassword.isVisible = state.asyncHomeServerLoginFlowRequest.invoke() == LoginMode.Password
|
||||
softLogoutFormError.isVisible = state.asyncHomeServerLoginFlowRequest is Fail
|
||||
itemErrorRetryText.setTextOrHide((state.asyncHomeServerLoginFlowRequest as? Fail)?.error?.let { errorFormatter.toHumanReadable(it) })
|
||||
}
|
||||
|
||||
private fun setupSubmitButton() {
|
||||
softLogoutPasswordField.textChanges()
|
||||
.map { it.trim().isNotEmpty() }
|
||||
|
@ -156,6 +180,7 @@ class SoftLogoutFragment @Inject constructor(
|
|||
|
||||
override fun invalidate() = withState(softLogoutViewModel) { state ->
|
||||
setupUi(state)
|
||||
setupForm(state)
|
||||
setupAutoFill()
|
||||
|
||||
when (state.asyncLoginAction) {
|
||||
|
|
|
@ -20,12 +20,16 @@ import com.airbnb.mvrx.*
|
|||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.AuthenticationService
|
||||
import im.vector.matrix.android.api.auth.data.LoginFlowResult
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||
import im.vector.riotx.core.extensions.hasUnsavedKeys
|
||||
import im.vector.riotx.core.extensions.toReducedUrl
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.login.LoginMode
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -33,8 +37,9 @@ import im.vector.riotx.core.platform.VectorViewModel
|
|||
class SoftLogoutViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: SoftLogoutViewState,
|
||||
private val session: Session,
|
||||
private val activeSessionHolder: ActiveSessionHolder)
|
||||
: VectorViewModel<SoftLogoutViewState, SoftLogoutAction>(initialState) {
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val authenticationService: AuthenticationService
|
||||
) : VectorViewModel<SoftLogoutViewState, SoftLogoutAction>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
|
@ -63,13 +68,83 @@ class SoftLogoutViewModel @AssistedInject constructor(
|
|||
|
||||
private var currentTask: Cancelable? = null
|
||||
|
||||
init {
|
||||
// Get the supported login flow
|
||||
getSupportedLoginFlow()
|
||||
}
|
||||
|
||||
private fun getSupportedLoginFlow() {
|
||||
val homeServerConnectionConfig = session.sessionParams.homeServerConnectionConfig
|
||||
|
||||
currentTask?.cancel()
|
||||
currentTask = null
|
||||
authenticationService.cancelPendingLoginOrRegistration()
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentTask = authenticationService.getLoginFlow(homeServerConnectionConfig, object : MatrixCallback<LoginFlowResult> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// TODO _viewEvents.post(LoginViewEvents.Error(failure))
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuccess(data: LoginFlowResult) {
|
||||
when (data) {
|
||||
is LoginFlowResult.Success -> {
|
||||
val loginMode = when {
|
||||
// SSO login is taken first
|
||||
data.loginFlowResponse.flows.any { it.type == LoginFlowTypes.SSO } -> LoginMode.Sso
|
||||
data.loginFlowResponse.flows.any { it.type == LoginFlowTypes.PASSWORD } -> LoginMode.Password
|
||||
else -> LoginMode.Unsupported
|
||||
}
|
||||
|
||||
if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported)
|
||||
|| loginMode == LoginMode.Unsupported) {
|
||||
notSupported()
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Success(loginMode)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is LoginFlowResult.OutdatedHomeserver -> {
|
||||
notSupported()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun notSupported() {
|
||||
// Should not happen since it's a re-logout
|
||||
// Notify the UI
|
||||
// _viewEvents.post(LoginViewEvents.OutdatedHomeserver)
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Fail(IllegalStateException("Should not happen"))
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO Cleanup
|
||||
// private val _viewEvents = PublishDataSource<LoginViewEvents>()
|
||||
// val viewEvents: DataSource<LoginViewEvents> = _viewEvents
|
||||
|
||||
override fun handle(action: SoftLogoutAction) {
|
||||
when (action) {
|
||||
is SoftLogoutAction.SignInAgain -> handleSignInAgain(action)
|
||||
is SoftLogoutAction.RetryLoginFlow -> getSupportedLoginFlow()
|
||||
is SoftLogoutAction.SignInAgain -> handleSignInAgain(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,10 @@ import com.airbnb.mvrx.Async
|
|||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.riotx.features.login.LoginMode
|
||||
|
||||
data class SoftLogoutViewState(
|
||||
val asyncHomeServerLoginFlowRequest: Async<LoginMode> = Uninitialized,
|
||||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
||||
val homeServerUrl: String,
|
||||
val userId: String,
|
||||
|
|
|
@ -55,73 +55,117 @@
|
|||
tools:visibility="visible" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/softLogoutPasswordContainer"
|
||||
android:id="@+id/softLogoutFormContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/softLogoutPasswordFieldTil"
|
||||
style="@style/VectorTextInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/soft_logout_signin_password_hint"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@null">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/softLogoutPasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="48dp"
|
||||
android:paddingRight="48dp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/softLogoutPasswordReveal"
|
||||
android:layout_width="@dimen/layout_touch_size"
|
||||
android:layout_height="@dimen/layout_touch_size"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_eye_black"
|
||||
android:tint="?attr/colorAccent"
|
||||
tools:contentDescription="@string/a11y_show_password" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/softLogoutForgetPasswordButton"
|
||||
style="@style/Style.Vector.Login.Button.Text"
|
||||
<!-- Displayed while loading -->
|
||||
<ProgressBar
|
||||
android:id="@+id/softLogoutFormLoading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:text="@string/auth_forgot_password" />
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<!-- Displayed for SSO mode -->
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/softLogoutSubmit"
|
||||
android:id="@+id/softLogoutFormSsoSubmit"
|
||||
style="@style/Style.Vector.Login.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/soft_logout_signin_submit"
|
||||
tools:enabled="false"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/login_signin_sso"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</RelativeLayout>
|
||||
<!-- Displayed in case of error -->
|
||||
<FrameLayout
|
||||
android:id="@+id/softLogoutFormError"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include layout="@layout/item_error_retry" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Displayed for password mode -->
|
||||
<LinearLayout
|
||||
android:id="@+id/softLogoutFormPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/softLogoutPasswordContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/softLogoutPasswordFieldTil"
|
||||
style="@style/VectorTextInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/soft_logout_signin_password_hint"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@null">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/softLogoutPasswordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="48dp"
|
||||
android:paddingRight="48dp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/softLogoutPasswordReveal"
|
||||
android:layout_width="@dimen/layout_touch_size"
|
||||
android:layout_height="@dimen/layout_touch_size"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_eye_black"
|
||||
android:tint="?attr/colorAccent"
|
||||
tools:contentDescription="@string/a11y_show_password" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/softLogoutForgetPasswordButton"
|
||||
style="@style/Style.Vector.Login.Button.Text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:text="@string/auth_forgot_password" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/softLogoutSubmit"
|
||||
style="@style/Style.Vector.Login.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/soft_logout_signin_submit"
|
||||
tools:enabled="false"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?riotx_background"
|
||||
|
|
Loading…
Reference in New Issue