mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-07 14:33:26 +01:00
Login screens: Registration WIP
This commit is contained in:
parent
08ea3d049e
commit
41ac2c6d70
@ -23,6 +23,7 @@ import androidx.work.WorkManager
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.BuildConfig
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationService
|
||||
import im.vector.matrix.android.internal.SessionManager
|
||||
import im.vector.matrix.android.internal.di.DaggerMatrixComponent
|
||||
import im.vector.matrix.android.internal.network.UserAgentHolder
|
||||
@ -47,6 +48,7 @@ data class MatrixConfiguration(
|
||||
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
|
||||
|
||||
@Inject internal lateinit var authenticator: Authenticator
|
||||
@Inject internal lateinit var registrationService: RegistrationService
|
||||
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||
@Inject internal lateinit var olmManager: OlmManager
|
||||
@ -68,6 +70,10 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
|
||||
return authenticator
|
||||
}
|
||||
|
||||
fun registrationService(): RegistrationService {
|
||||
return registrationService
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private lateinit var instance: Matrix
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.matrix.android.api.auth.registration
|
||||
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
|
||||
// Either a session or an object containing data about registration stages
|
||||
sealed class RegistrationResult {
|
||||
data class Success(val session: Session) : RegistrationResult()
|
||||
data class FlowResponse(val flowResult: FlowResult) : RegistrationResult()
|
||||
}
|
||||
|
||||
|
||||
data class FlowResult(
|
||||
val missingStages: List<Stage>,
|
||||
val completedStages: List<Stage>
|
||||
)
|
@ -17,14 +17,15 @@
|
||||
package im.vector.matrix.android.api.auth.registration
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
|
||||
interface RegistrationWizard {
|
||||
|
||||
fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?, callback: MatrixCallback<Session>): Cancelable
|
||||
fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
fun performReCaptcha(response: String, callback: MatrixCallback<Session>): Cancelable
|
||||
fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
// TODO Add other method here
|
||||
}
|
||||
|
@ -16,32 +16,29 @@
|
||||
|
||||
package im.vector.matrix.android.api.auth.registration
|
||||
|
||||
import im.vector.matrix.android.api.util.JsonDict
|
||||
|
||||
sealed class Stage(open val mandatory: Boolean) {
|
||||
|
||||
// m.login.password
|
||||
data class Password(override val mandatory: Boolean, val publicKey: String) : Stage(mandatory)
|
||||
|
||||
// m.login.recaptcha
|
||||
data class ReCaptcha(override val mandatory: Boolean, val publicKey: String) : Stage(mandatory)
|
||||
|
||||
// m.login.oauth2
|
||||
// m.login.email.identity
|
||||
data class Email(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
|
||||
data class Email(override val mandatory: Boolean) : Stage(mandatory)
|
||||
|
||||
// m.login.msisdn
|
||||
data class Msisdn(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
|
||||
data class Msisdn(override val mandatory: Boolean) : Stage(mandatory)
|
||||
|
||||
// m.login.token
|
||||
|
||||
// m.login.dummy
|
||||
object Dummy : Stage(false)
|
||||
|
||||
// Undocumented yet: m.login.terms
|
||||
data class Terms(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
|
||||
|
||||
// TODO SSO
|
||||
|
||||
// For unknown stages
|
||||
data class Other(override val mandatory: Boolean, val type: String, val params: JsonDict?) : Stage(mandatory)
|
||||
data class Other(override val mandatory: Boolean, val type: String, val params: Map<*, *>?) : Stage(mandatory)
|
||||
}
|
||||
|
||||
//TODO
|
||||
class TermPolicies
|
||||
|
@ -36,8 +36,8 @@ internal interface AuthAPI {
|
||||
* Register to the homeserver
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
|
||||
*/
|
||||
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
|
||||
fun register(registrationParams: RegistrationParams): Call<Credentials>
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
|
||||
fun register(@Body registrationParams: RegistrationParams): Call<Credentials>
|
||||
|
||||
/**
|
||||
* Get the supported login flow
|
||||
|
@ -21,8 +21,10 @@ import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationService
|
||||
import im.vector.matrix.android.internal.auth.db.AuthRealmModule
|
||||
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
|
||||
import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationService
|
||||
import im.vector.matrix.android.internal.database.RealmKeysUtils
|
||||
import im.vector.matrix.android.internal.di.AuthDatabase
|
||||
import io.realm.RealmConfiguration
|
||||
@ -60,4 +62,7 @@ internal abstract class AuthModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindAuthenticator(authenticator: DefaultAuthenticator): Authenticator
|
||||
|
||||
@Binds
|
||||
abstract fun bindRegistrationService(service: DefaultRegistrationService): RegistrationService
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package im.vector.matrix.android.internal.auth.data
|
||||
|
||||
// TODO Move to [InteractiveAuthenticationFlow]
|
||||
object LoginFlowTypes {
|
||||
const val PASSWORD = "m.login.password"
|
||||
const val OAUTH2 = "m.login.oauth2"
|
||||
@ -25,4 +26,5 @@ object LoginFlowTypes {
|
||||
const val MSISDN = "m.login.msisdn"
|
||||
const val RECAPTCHA = "m.login.recaptcha"
|
||||
const val DUMMY = "m.login.dummy"
|
||||
const val TERMS = "m.login.terms"
|
||||
}
|
||||
|
@ -26,14 +26,14 @@ import im.vector.matrix.android.internal.di.Unauthenticated
|
||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO Add @Inject
|
||||
internal class DefaultRegistrationService(@Unauthenticated
|
||||
private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sessionParamsStore: SessionParamsStore,
|
||||
private val sessionManager: SessionManager) : RegistrationService {
|
||||
internal class DefaultRegistrationService @Inject constructor(@Unauthenticated
|
||||
private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sessionParamsStore: SessionParamsStore,
|
||||
private val sessionManager: SessionManager) : RegistrationService {
|
||||
|
||||
override fun getOrCreateRegistrationWizard(homeServerConnectionConfig: HomeServerConnectionConfig): RegistrationWizard {
|
||||
// TODO Persist the wizard?
|
||||
|
@ -20,9 +20,9 @@ import dagger.Lazy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||
import im.vector.matrix.android.internal.SessionManager
|
||||
@ -47,10 +47,14 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
private val authAPI = buildAuthAPI()
|
||||
private val registerTask = DefaultRegisterTask(authAPI)
|
||||
|
||||
override fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
return performRegistrationRequest(RegistrationParams(), callback)
|
||||
}
|
||||
|
||||
override fun createAccount(userName: String,
|
||||
password: String,
|
||||
initialDeviceDisplayName: String?,
|
||||
callback: MatrixCallback<Session>): Cancelable {
|
||||
callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
return performRegistrationRequest(RegistrationParams(
|
||||
username = userName,
|
||||
password = password,
|
||||
@ -58,7 +62,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
), callback)
|
||||
}
|
||||
|
||||
override fun performReCaptcha(response: String, callback: MatrixCallback<Session>): Cancelable {
|
||||
override fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeSession = currentSession ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||
return NoOpCancellable
|
||||
@ -72,7 +76,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
), callback)
|
||||
}
|
||||
|
||||
private fun performRegistrationRequest(registrationParams: RegistrationParams, callback: MatrixCallback<Session>): Cancelable {
|
||||
private fun performRegistrationRequest(registrationParams: RegistrationParams, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val result = runCatching {
|
||||
registerTask.execute(RegisterTask.Params(registrationParams))
|
||||
@ -83,13 +87,15 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||
sessionParamsStore.save(sessionParams)
|
||||
val session = sessionManager.getOrCreateSession(sessionParams)
|
||||
|
||||
callback.onSuccess(session)
|
||||
callback.onSuccess(RegistrationResult.Success(session))
|
||||
},
|
||||
{
|
||||
if (it is Failure.RegistrationFlowError) {
|
||||
currentSession = it.registrationFlowResponse.session
|
||||
callback.onSuccess(RegistrationResult.FlowResponse(it.registrationFlowResponse.toFlowResult()))
|
||||
} else {
|
||||
callback.onFailure(it)
|
||||
}
|
||||
callback.onFailure(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -18,8 +18,12 @@ package im.vector.matrix.android.internal.auth.registration
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.auth.registration.FlowResult
|
||||
import im.vector.matrix.android.api.auth.registration.Stage
|
||||
import im.vector.matrix.android.api.auth.registration.TermPolicies
|
||||
import im.vector.matrix.android.api.util.JsonDict
|
||||
import im.vector.matrix.android.internal.auth.data.InteractiveAuthenticationFlow
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RegistrationFlowResponse(
|
||||
@ -51,3 +55,39 @@ data class RegistrationFlowResponse(
|
||||
@Json(name = "params")
|
||||
var params: JsonDict? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Convert to something easier to exploit on client side
|
||||
*/
|
||||
fun RegistrationFlowResponse.toFlowResult(): FlowResult {
|
||||
// Get all the returned stages
|
||||
val allFlowTypes = mutableSetOf<String>()
|
||||
|
||||
val missingStage = mutableListOf<Stage>()
|
||||
val completedStage = mutableListOf<Stage>()
|
||||
|
||||
this.flows?.forEach { it.stages?.mapTo(allFlowTypes) { type -> type } }
|
||||
|
||||
allFlowTypes.forEach { type ->
|
||||
val isMandatory = flows?.all { type in it.stages ?: emptyList() } == true
|
||||
|
||||
val stage = when (type) {
|
||||
LoginFlowTypes.RECAPTCHA -> Stage.ReCaptcha(isMandatory, ((params?.get(type) as? Map<*, *>)?.get("public_key") as? String)
|
||||
?: "")
|
||||
LoginFlowTypes.DUMMY -> Stage.Dummy
|
||||
LoginFlowTypes.TERMS -> Stage.Terms(isMandatory, TermPolicies())
|
||||
LoginFlowTypes.EMAIL_IDENTITY -> Stage.Email(isMandatory)
|
||||
LoginFlowTypes.MSISDN -> Stage.Msisdn(isMandatory)
|
||||
else -> Stage.Other(isMandatory, type, (params?.get(type) as? Map<*, *>))
|
||||
}
|
||||
|
||||
if (type in completedStages ?: emptyList()) {
|
||||
completedStage.add(stage)
|
||||
} else {
|
||||
missingStage.add(stage)
|
||||
}
|
||||
}
|
||||
|
||||
return FlowResult(missingStage, completedStage)
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationService
|
||||
import im.vector.matrix.android.internal.SessionManager
|
||||
import im.vector.matrix.android.internal.auth.AuthModule
|
||||
import im.vector.matrix.android.internal.auth.SessionParamsStore
|
||||
@ -46,6 +47,8 @@ internal interface MatrixComponent {
|
||||
|
||||
fun authenticator(): Authenticator
|
||||
|
||||
fun registrationService(): RegistrationService
|
||||
|
||||
fun context(): Context
|
||||
|
||||
fun resources(): Resources
|
||||
|
@ -32,8 +32,8 @@ import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsB
|
||||
import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
|
||||
import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
|
||||
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
|
||||
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||
import im.vector.riotx.features.home.room.list.RoomListModule
|
||||
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||
import im.vector.riotx.features.invite.VectorInviteView
|
||||
import im.vector.riotx.features.link.LinkHandlerActivity
|
||||
import im.vector.riotx.features.login.LoginActivity
|
||||
@ -47,7 +47,7 @@ import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
|
||||
import im.vector.riotx.features.reactions.widget.ReactionButton
|
||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
|
||||
import im.vector.riotx.features.settings.*
|
||||
import im.vector.riotx.features.settings.VectorSettingsActivity
|
||||
import im.vector.riotx.features.share.IncomingShareActivity
|
||||
import im.vector.riotx.features.ui.UiStateRepository
|
||||
|
||||
|
@ -22,6 +22,7 @@ import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationService
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.ActiveSessionDataSource
|
||||
import im.vector.riotx.EmojiCompatFontProvider
|
||||
@ -99,6 +100,8 @@ interface VectorComponent {
|
||||
|
||||
fun authenticator(): Authenticator
|
||||
|
||||
fun registrationService(): RegistrationService
|
||||
|
||||
fun bugReporter(): BugReporter
|
||||
|
||||
fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler
|
||||
|
@ -25,6 +25,7 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationService
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotx.features.navigation.DefaultNavigator
|
||||
import im.vector.riotx.features.navigation.Navigator
|
||||
@ -67,6 +68,12 @@ abstract class VectorModule {
|
||||
fun providesAuthenticator(matrix: Matrix): Authenticator {
|
||||
return matrix.authenticator()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun providesRegistrationService(matrix: Matrix): RegistrationService {
|
||||
return matrix.registrationService()
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
|
@ -28,6 +28,15 @@ sealed class LoginAction : VectorViewModelAction {
|
||||
data class InitWith(val loginConfig: LoginConfig) : LoginAction()
|
||||
data class ResetPassword(val email: String, val newPassword: String) : LoginAction()
|
||||
|
||||
// Register actions
|
||||
open class RegisterAction : LoginAction()
|
||||
|
||||
data class RegisterWith(val username: String, val password: String) : RegisterAction()
|
||||
data class AddEmail(val email: String) : RegisterAction()
|
||||
data class AddMsisdn(val msisdn: String) : RegisterAction()
|
||||
data class ConfirmMsisdn(val code: String) : RegisterAction()
|
||||
data class PerformCaptcha(val captcha: String /* TODO Add other params */) : RegisterAction()
|
||||
|
||||
// Reset actions
|
||||
open class ResetAction : LoginAction()
|
||||
|
||||
|
@ -126,7 +126,7 @@ class LoginActivity : VectorBaseActivity() {
|
||||
private fun onSignModeSelected() {
|
||||
when (loginViewModel.signMode) {
|
||||
SignMode.Unknown -> error("Sign mode has to be set before calling this method")
|
||||
SignMode.SignUp -> Unit // TODO addFragmentToBackstack(R.id.loginFragmentContainer, SignUpFragment::class.java)
|
||||
SignMode.SignUp -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginFragment::class.java)
|
||||
SignMode.SignIn -> {
|
||||
// It depends on the LoginMode
|
||||
withState(loginViewModel) {
|
||||
|
@ -35,8 +35,11 @@ import kotlinx.android.synthetic.main.fragment_login.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* In this screen, the user is asked for login and password to sign in to a homeserver.
|
||||
* He also can reset his password
|
||||
* In this screen, in signin mode:
|
||||
* - the user is asked for login and password to sign in to a homeserver.
|
||||
* - He also can reset his password
|
||||
* In signup mode:
|
||||
* - the user is asked for login and password
|
||||
*/
|
||||
class LoginFragment @Inject constructor(
|
||||
private val errorFormatter: ErrorFormatter
|
||||
@ -53,38 +56,61 @@ class LoginFragment @Inject constructor(
|
||||
setupUi()
|
||||
setupSubmitButton()
|
||||
setupPasswordReveal()
|
||||
setupButtons()
|
||||
}
|
||||
|
||||
private fun authenticate() {
|
||||
@OnClick(R.id.loginSubmit)
|
||||
fun submit() {
|
||||
val login = loginField.text?.trim().toString()
|
||||
val password = passwordField.text?.trim().toString()
|
||||
|
||||
loginViewModel.handle(LoginAction.Login(login, password))
|
||||
when (loginViewModel.signMode) {
|
||||
SignMode.Unknown -> error("developer error")
|
||||
SignMode.SignUp -> loginViewModel.handle(LoginAction.RegisterWith(login, password))
|
||||
SignMode.SignIn -> loginViewModel.handle(LoginAction.Login(login, password))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupUi() {
|
||||
val resId = when (loginViewModel.signMode) {
|
||||
SignMode.Unknown -> error("developer error")
|
||||
SignMode.SignUp -> R.string.login_signup_to
|
||||
SignMode.SignIn -> R.string.login_connect_to
|
||||
}
|
||||
|
||||
when (loginViewModel.serverType) {
|
||||
ServerType.MatrixOrg -> {
|
||||
loginServerIcon.isVisible = true
|
||||
loginServerIcon.setImageResource(R.drawable.ic_logo_matrix_org)
|
||||
loginTitle.text = getString(R.string.login_connect_to, loginViewModel.getHomeServerUrlSimple())
|
||||
loginTitle.text = getString(resId, loginViewModel.getHomeServerUrlSimple())
|
||||
loginNotice.text = getString(R.string.login_server_matrix_org_text)
|
||||
}
|
||||
ServerType.Modular -> {
|
||||
loginServerIcon.isVisible = true
|
||||
loginServerIcon.setImageResource(R.drawable.ic_logo_modular)
|
||||
// TODO
|
||||
loginTitle.text = getString(R.string.login_connect_to, "TODO")
|
||||
loginTitle.text = getString(resId, "TODO")
|
||||
loginNotice.text = loginViewModel.getHomeServerUrlSimple()
|
||||
}
|
||||
ServerType.Other -> {
|
||||
loginServerIcon.isVisible = false
|
||||
loginTitle.text = getString(R.string.login_server_other_title)
|
||||
loginNotice.text = getString(R.string.login_connect_to, loginViewModel.getHomeServerUrlSimple())
|
||||
loginNotice.text = getString(resId, loginViewModel.getHomeServerUrlSimple())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupButtons() {
|
||||
forgetPasswordButton.isVisible = loginViewModel.signMode == SignMode.SignIn
|
||||
|
||||
loginSubmit.text = getString(when (loginViewModel.signMode) {
|
||||
SignMode.Unknown -> error("developer error")
|
||||
SignMode.SignUp -> R.string.login_signup_submit
|
||||
SignMode.SignIn -> R.string.login_signin
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
private fun setupSubmitButton() {
|
||||
Observable
|
||||
.combineLatest(
|
||||
@ -100,8 +126,6 @@ class LoginFragment @Inject constructor(
|
||||
loginSubmit.isEnabled = it
|
||||
}
|
||||
.disposeOnDestroyView()
|
||||
|
||||
loginSubmit.setOnClickListener { authenticate() }
|
||||
}
|
||||
|
||||
@OnClick(R.id.forgetPasswordButton)
|
||||
|
@ -20,6 +20,12 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import butterknife.OnClick
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.matrix.android.api.auth.registration.FlowResult
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
||||
import im.vector.matrix.android.api.auth.registration.Stage
|
||||
import im.vector.riotx.R
|
||||
import kotlinx.android.synthetic.main.fragment_login_signup_signin_selection.*
|
||||
import javax.inject.Inject
|
||||
@ -63,7 +69,6 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
|
||||
@OnClick(R.id.loginSignupSigninSignUp)
|
||||
fun signUp() {
|
||||
loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignUp))
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnSignModeSelected)
|
||||
}
|
||||
|
||||
@OnClick(R.id.loginSignupSigninSignIn)
|
||||
@ -75,4 +80,35 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
|
||||
override fun resetViewModel() {
|
||||
loginViewModel.handle(LoginAction.ResetSignMode)
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(loginViewModel) {
|
||||
when (it.asyncRegistration) {
|
||||
is Success -> {
|
||||
when (val res = it.asyncRegistration()) {
|
||||
is RegistrationResult.Success ->
|
||||
// Should not happen
|
||||
Unit
|
||||
is RegistrationResult.FlowResponse -> handleFlowResult(res.flowResult)
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
// TODO Registration disabled, etc
|
||||
when (it.asyncRegistration.error) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFlowResult(flowResult: FlowResult) {
|
||||
// Check that all flows are supported by the application
|
||||
if (flowResult.missingStages.any { it is Stage.Other }) {
|
||||
// Display a popup to propose use web fallback
|
||||
// TODO
|
||||
} else {
|
||||
// Go on with registration flow
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnSignModeSelected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.auth.Authenticator
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.api.auth.registration.FlowResult
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationService
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.auth.data.InteractiveAuthenticationFlow
|
||||
@ -36,6 +40,7 @@ import timber.log.Timber
|
||||
|
||||
class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginViewState,
|
||||
private val authenticator: Authenticator,
|
||||
private val registrationService: RegistrationService,
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val pushRuleTriggerListener: PushRuleTriggerListener,
|
||||
private val sessionListener: SessionListener)
|
||||
@ -55,6 +60,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
}
|
||||
}
|
||||
|
||||
private var registrationWizard: RegistrationWizard? = null
|
||||
|
||||
var serverType: ServerType = ServerType.MatrixOrg
|
||||
private set
|
||||
var signMode: SignMode = SignMode.Unknown
|
||||
@ -89,7 +96,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
LoginAction.ResetLogin -> {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Uninitialized
|
||||
asyncLoginAction = Uninitialized,
|
||||
asyncRegistration = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -124,6 +132,10 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
|
||||
private fun handleUpdateSignMode(action: LoginAction.UpdateSignMode) {
|
||||
signMode = action.signMode
|
||||
|
||||
if (signMode == SignMode.SignUp) {
|
||||
startRegistrationFlow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateServerType(action: LoginAction.UpdateServerType) {
|
||||
@ -206,6 +218,53 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
}
|
||||
}
|
||||
|
||||
private fun startRegistrationFlow() {
|
||||
val homeServerConnectionConfigFinal = homeServerConnectionConfig
|
||||
|
||||
if (homeServerConnectionConfigFinal == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Fail(Throwable("Bad configuration"))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
registrationWizard = registrationService.getOrCreateRegistrationWizard(homeServerConnectionConfigFinal)
|
||||
|
||||
currentTask = registrationWizard?.getRegistrationFlow(object : MatrixCallback<RegistrationResult> {
|
||||
override fun onSuccess(data: RegistrationResult) {
|
||||
when (data) {
|
||||
is RegistrationResult.Success -> onSessionCreated(data.session)
|
||||
is RegistrationResult.FlowResponse -> onFlowResponse(data.flowResult)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// TODO Handled JobCancellationException
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFlowResponse(flowResult: FlowResult) {
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Success(RegistrationResult.FlowResponse(flowResult))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun onSessionCreated(session: Session) {
|
||||
activeSessionHolder.setActiveSession(session)
|
||||
session.configureAndStart(pushRuleTriggerListener, sessionListener)
|
||||
@ -246,7 +305,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||
|
||||
// Do not retry if we already have flows for this config -> causes infinite focus loop
|
||||
if (newConfig?.homeServerUri?.toString() == homeServerConnectionConfig?.homeServerUri?.toString()
|
||||
&& state.asyncHomeServerLoginFlowRequest is Success) return@withState
|
||||
&& state.asyncHomeServerLoginFlowRequest is Success) return@withState
|
||||
|
||||
currentTask?.cancel()
|
||||
currentTask = null
|
||||
|
@ -17,11 +17,13 @@
|
||||
package im.vector.riotx.features.login
|
||||
|
||||
import com.airbnb.mvrx.*
|
||||
import im.vector.matrix.android.api.auth.registration.RegistrationResult
|
||||
|
||||
data class LoginViewState(
|
||||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
||||
val asyncHomeServerLoginFlowRequest: Async<LoginMode> = Uninitialized,
|
||||
val asyncResetPassword: Async<Unit> = Uninitialized
|
||||
val asyncResetPassword: Async<Unit> = Uninitialized,
|
||||
val asyncRegistration: Async<RegistrationResult> = Uninitialized
|
||||
) : MvRxState {
|
||||
|
||||
fun isLoading(): Boolean {
|
||||
@ -29,10 +31,10 @@ data class LoginViewState(
|
||||
return asyncLoginAction is Loading
|
||||
|| asyncHomeServerLoginFlowRequest is Loading
|
||||
|| asyncResetPassword is Loading
|
||||
|| asyncRegistration is Loading
|
||||
}
|
||||
|
||||
fun isUserLogged(): Boolean {
|
||||
// TODO Add other async here
|
||||
return asyncLoginAction is Success
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/auth_user_name_placeholder"
|
||||
android:hint="@string/login_signup_username_hint"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
@ -81,7 +81,7 @@
|
||||
style="@style/VectorTextInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/auth_password_placeholder"
|
||||
android:hint="@string/login_signup_password_hint"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@null">
|
||||
|
||||
|
@ -88,4 +88,10 @@
|
||||
<string name="login_msisdn_confirm_send_again">Send again</string>
|
||||
<string name="login_msisdn_confirm_submit">Next</string>
|
||||
|
||||
<!-- Replaced string is the homeserver url -->
|
||||
<string name="login_signup_to">Sign up to %1$s</string>
|
||||
<string name="login_signup_username_hint">Username</string>
|
||||
<string name="login_signup_password_hint">Password</string>
|
||||
<string name="login_signup_submit">Next</string>
|
||||
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user