Login screens: add MSISDN
This commit is contained in:
parent
248a584e1a
commit
127916a8d9
|
@ -33,9 +33,9 @@ interface RegistrationWizard {
|
||||||
|
|
||||||
fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable
|
fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
fun confirmMsisdn(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
fun validateEmail(callback: MatrixCallback<RegistrationResult>): Cancelable
|
fun checkIfEmailHasBeenValidated(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||||
|
|
||||||
val currentThreePid: String?
|
val currentThreePid: String?
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
|
||||||
data class Cancelled(val throwable: Throwable? = null) : Failure(throwable)
|
data class Cancelled(val throwable: Throwable? = null) : Failure(throwable)
|
||||||
data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
|
data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
|
||||||
data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
|
data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
|
||||||
|
object SuccessError : Failure(RuntimeException(RuntimeException("SuccessResult is false")))
|
||||||
// When server send an error, but it cannot be interpreted as a MatrixError
|
// When server send an error, but it cannot be interpreted as a MatrixError
|
||||||
data class OtherServerError(val errorBody: String, val httpCode: Int) : Failure(RuntimeException(errorBody))
|
data class OtherServerError(val errorBody: String, val httpCode: Int) : Failure(RuntimeException(errorBody))
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,7 @@ package im.vector.matrix.android.internal.auth
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||||
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
||||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams
|
import im.vector.matrix.android.internal.auth.registration.*
|
||||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse
|
|
||||||
import im.vector.matrix.android.internal.auth.registration.RegistrationParams
|
|
||||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
|
@ -46,6 +44,12 @@ internal interface AuthAPI {
|
||||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register/{threePid}/requestToken")
|
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register/{threePid}/requestToken")
|
||||||
fun add3Pid(@Path("threePid") threePid: String, @Body params: AddThreePidRegistrationParams): Call<AddThreePidRegistrationResponse>
|
fun add3Pid(@Path("threePid") threePid: String, @Body params: AddThreePidRegistrationParams): Call<AddThreePidRegistrationResponse>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate 3pid
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
fun validate3Pid(@Url url: String, @Body params: ValidationCodeBody): Call<SuccessResult>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the supported login flow
|
* Get the supported login flow
|
||||||
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-login
|
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-login
|
||||||
|
|
|
@ -61,6 +61,10 @@ internal data class AuthParams(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that there is a bug in Synapse (I have to investigate where), but if we pass LoginFlowTypes.MSISDN,
|
||||||
|
* the homeserver answer with the login flow with MatrixError fields and not with a simple MatrixError 401.
|
||||||
|
*/
|
||||||
fun createForMsisdnIdentity(session: String, threePidCredentials: ThreePidCredentials): AuthParams {
|
fun createForMsisdnIdentity(session: String, threePidCredentials: ThreePidCredentials): AuthParams {
|
||||||
return AuthParams(
|
return AuthParams(
|
||||||
type = LoginFlowTypes.MSISDN,
|
type = LoginFlowTypes.MSISDN,
|
||||||
|
|
|
@ -63,6 +63,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||||
private val authAPI = buildAuthAPI()
|
private val authAPI = buildAuthAPI()
|
||||||
private val registerTask = DefaultRegisterTask(authAPI)
|
private val registerTask = DefaultRegisterTask(authAPI)
|
||||||
private val registerAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI)
|
private val registerAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI)
|
||||||
|
private val validateCodeTask = DefaultValidateCodeTask(authAPI)
|
||||||
|
|
||||||
private var currentThreePidData: ThreePidData? = null
|
private var currentThreePidData: ThreePidData? = null
|
||||||
|
|
||||||
|
@ -168,7 +169,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||||
return CancelableCoroutine(job)
|
return CancelableCoroutine(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun validateEmail(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
override fun checkIfEmailHasBeenValidated(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||||
val safeParam = currentThreePidData?.registrationParams ?: run {
|
val safeParam = currentThreePidData?.registrationParams ?: run {
|
||||||
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
|
@ -178,18 +179,49 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
||||||
return performRegistrationRequest(safeParam, callback, 10_000)
|
return performRegistrationRequest(safeParam, callback, 10_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun confirmMsisdn(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
override fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||||
val safeSession = currentSession ?: run {
|
val safeParam = currentThreePidData?.registrationParams ?: run {
|
||||||
|
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
||||||
|
return NoOpCancellable
|
||||||
|
}
|
||||||
|
|
||||||
|
val safeCurrentData = currentThreePidData ?: run {
|
||||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||||
return NoOpCancellable
|
return NoOpCancellable
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
val url = safeCurrentData.addThreePidRegistrationResponse.submitUrl ?: run {
|
||||||
return performRegistrationRequest(
|
callback.onFailure(IllegalStateException("Missing url the send the code"))
|
||||||
RegistrationParams(
|
return NoOpCancellable
|
||||||
// TODO
|
}
|
||||||
auth = AuthParams.createForEmailIdentity(safeSession, ThreePidCredentials(code))
|
|
||||||
), callback)
|
val params = ValidationCodeBody(
|
||||||
|
clientSecret = clientSecret,
|
||||||
|
sid = safeCurrentData.addThreePidRegistrationResponse.sid,
|
||||||
|
code = code
|
||||||
|
)
|
||||||
|
|
||||||
|
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
|
runCatching {
|
||||||
|
validateCodeTask.execute(ValidateCodeTask.Params(url, params))
|
||||||
|
}
|
||||||
|
.fold(
|
||||||
|
{
|
||||||
|
if (it.success == true) {
|
||||||
|
// The entered code is correct
|
||||||
|
// Same that validate email
|
||||||
|
performRegistrationRequest(safeParam, callback, 3_000)
|
||||||
|
} else {
|
||||||
|
// The code is not correct
|
||||||
|
callback.onFailure(Failure.SuccessError)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
callback.onFailure(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return CancelableCoroutine(job)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dummy(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
override fun dummy(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.auth.registration
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class SuccessResult(
|
||||||
|
@Json(name = "success")
|
||||||
|
val success: Boolean?
|
||||||
|
)
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.auth.registration
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.auth.AuthAPI
|
||||||
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
|
import im.vector.matrix.android.internal.task.Task
|
||||||
|
|
||||||
|
internal interface ValidateCodeTask : Task<ValidateCodeTask.Params, SuccessResult> {
|
||||||
|
data class Params(
|
||||||
|
val url: String,
|
||||||
|
val body: ValidationCodeBody
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultValidateCodeTask(private val authAPI: AuthAPI)
|
||||||
|
: ValidateCodeTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: ValidateCodeTask.Params): SuccessResult {
|
||||||
|
return executeRequest {
|
||||||
|
apiCall = authAPI.validate3Pid(params.url, params.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.auth.registration
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is used to send a code received by SMS to validate Msisdn ownership
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class ValidationCodeBody(
|
||||||
|
@Json(name = "client_secret")
|
||||||
|
val clientSecret: String,
|
||||||
|
|
||||||
|
@Json(name = "sid")
|
||||||
|
val sid: String,
|
||||||
|
|
||||||
|
@Json(name = "token")
|
||||||
|
val code: String
|
||||||
|
)
|
|
@ -35,8 +35,9 @@ sealed class LoginAction : VectorViewModelAction {
|
||||||
data class RegisterWith(val username: String, val password: String, val initialDeviceName: String) : RegisterAction()
|
data class RegisterWith(val username: String, val password: String, val initialDeviceName: String) : RegisterAction()
|
||||||
data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction()
|
data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction()
|
||||||
// TODO Confirm Email (from link in the email, open in the phone, intercepted by RiotX)
|
// TODO Confirm Email (from link in the email, open in the phone, intercepted by RiotX)
|
||||||
data class ConfirmMsisdn(val code: String) : RegisterAction()
|
data class ValidateThreePid(val code: String) : RegisterAction()
|
||||||
object ValidateEmail : RegisterAction()
|
|
||||||
|
object CheckIfEmailHasBeenValidated : RegisterAction()
|
||||||
|
|
||||||
data class CaptchaDone(val captchaResponse: String) : RegisterAction()
|
data class CaptchaDone(val captchaResponse: String) : RegisterAction()
|
||||||
object AcceptTerms : RegisterAction()
|
object AcceptTerms : RegisterAction()
|
||||||
|
|
|
@ -121,10 +121,10 @@ class LoginGenericTextInputFormFragment @Inject constructor(private val errorFor
|
||||||
}
|
}
|
||||||
TextInputFormFragmentMode.SetMsisdn -> {
|
TextInputFormFragmentMode.SetMsisdn -> {
|
||||||
// TODO Country code
|
// TODO Country code
|
||||||
loginViewModel.handle(LoginAction.AddThreePid(RegisterThreePid.Msisdn(text, "TODO")))
|
loginViewModel.handle(LoginAction.AddThreePid(RegisterThreePid.Msisdn(text, "FR")))
|
||||||
}
|
}
|
||||||
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
||||||
loginViewModel.handle(LoginAction.ConfirmMsisdn(text))
|
loginViewModel.handle(LoginAction.ValidateThreePid(text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,12 @@ class LoginGenericTextInputFormFragment @Inject constructor(private val errorFor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
||||||
// TODO
|
if (throwable is Failure.SuccessError) {
|
||||||
|
// The entered code is not correct
|
||||||
|
loginGenericTextInputFormTil.error = getString(R.string.login_validation_code_is_not_correct)
|
||||||
|
} else {
|
||||||
|
loginGenericTextInputFormTil.error = errorFormatter.toHumanReadable(throwable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,25 +105,25 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
||||||
|
|
||||||
private fun handleRegisterAction(action: LoginAction.RegisterAction) {
|
private fun handleRegisterAction(action: LoginAction.RegisterAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is LoginAction.RegisterWith -> handleRegisterWith(action)
|
is LoginAction.RegisterWith -> handleRegisterWith(action)
|
||||||
is LoginAction.CaptchaDone -> handleCaptchaDone(action)
|
is LoginAction.CaptchaDone -> handleCaptchaDone(action)
|
||||||
is LoginAction.AcceptTerms -> handleAcceptTerms()
|
is LoginAction.AcceptTerms -> handleAcceptTerms()
|
||||||
is LoginAction.RegisterDummy -> handleRegisterDummy()
|
is LoginAction.RegisterDummy -> handleRegisterDummy()
|
||||||
is LoginAction.AddThreePid -> handleAddThreePid(action)
|
is LoginAction.AddThreePid -> handleAddThreePid(action)
|
||||||
is LoginAction.ConfirmMsisdn -> handleConfirmMsisdn(action)
|
is LoginAction.ValidateThreePid -> handleValidateThreePid(action)
|
||||||
is LoginAction.ValidateEmail -> handleValidateEmail()
|
is LoginAction.CheckIfEmailHasBeenValidated -> handleCheckIfEmailHasBeenValidated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleValidateEmail() {
|
private fun handleCheckIfEmailHasBeenValidated() {
|
||||||
// We do not want the common progress bar to be displayed, so we do not change asyncRegistration value in the state
|
// We do not want the common progress bar to be displayed, so we do not change asyncRegistration value in the state
|
||||||
currentTask?.cancel()
|
currentTask?.cancel()
|
||||||
currentTask = registrationWizard?.validateEmail(registrationCallback)
|
currentTask = registrationWizard?.checkIfEmailHasBeenValidated(registrationCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleConfirmMsisdn(action: LoginAction.ConfirmMsisdn) {
|
private fun handleValidateThreePid(action: LoginAction.ValidateThreePid) {
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
setState { copy(asyncRegistration = Loading()) }
|
||||||
currentTask = registrationWizard?.confirmMsisdn(action.code, registrationCallback)
|
currentTask = registrationWizard?.handleValidateThreePid(action.code, registrationCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val registrationCallback = object : MatrixCallback<RegistrationResult> {
|
private val registrationCallback = object : MatrixCallback<RegistrationResult> {
|
||||||
|
|
|
@ -49,7 +49,7 @@ class LoginWaitForEmailFragment @Inject constructor(private val errorFormatter:
|
||||||
|
|
||||||
setupUi()
|
setupUi()
|
||||||
|
|
||||||
loginViewModel.handle(LoginAction.ValidateEmail)
|
loginViewModel.handle(LoginAction.CheckIfEmailHasBeenValidated)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupUi() {
|
private fun setupUi() {
|
||||||
|
@ -59,7 +59,7 @@ class LoginWaitForEmailFragment @Inject constructor(private val errorFormatter:
|
||||||
override fun onRegistrationError(throwable: Throwable) {
|
override fun onRegistrationError(throwable: Throwable) {
|
||||||
if (throwable.is401()) {
|
if (throwable.is401()) {
|
||||||
// Try again, with a delay
|
// Try again, with a delay
|
||||||
loginViewModel.handle(LoginAction.ValidateEmail)
|
loginViewModel.handle(LoginAction.CheckIfEmailHasBeenValidated)
|
||||||
} else {
|
} else {
|
||||||
AlertDialog.Builder(requireActivity())
|
AlertDialog.Builder(requireActivity())
|
||||||
.setTitle(R.string.dialog_title_error)
|
.setTitle(R.string.dialog_title_error)
|
||||||
|
|
|
@ -110,5 +110,6 @@
|
||||||
|
|
||||||
<string name="login_wait_for_email_title">Please check your email</string>
|
<string name="login_wait_for_email_title">Please check your email</string>
|
||||||
<string name="login_wait_for_email_notice">We just sent an email to %1$s.\nPlease click on the link it contains to continue the account creation.</string>
|
<string name="login_wait_for_email_notice">We just sent an email to %1$s.\nPlease click on the link it contains to continue the account creation.</string>
|
||||||
|
<string name="login_validation_code_is_not_correct">The entered code is not correct. Please check.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue