mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
adding email input FTUE screen
- lifts the threepid email error handling to the RegistrationActionHandler rather than having the UI infer success from a 401
This commit is contained in:
parent
4094a66f3c
commit
d4a5b71a4d
@ -101,6 +101,7 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthAccountCreatedFragment
|
|||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseDisplayNameFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseDisplayNameFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseProfilePictureFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseProfilePictureFragment
|
||||||
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthEmailEntryFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyStyleCaptchaFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyStyleCaptchaFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthLoginFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthLoginFragment
|
||||||
@ -494,6 +495,11 @@ interface FragmentModule {
|
|||||||
@FragmentKey(FtueAuthAccountCreatedFragment::class)
|
@FragmentKey(FtueAuthAccountCreatedFragment::class)
|
||||||
fun bindFtueAuthAccountCreatedFragment(fragment: FtueAuthAccountCreatedFragment): Fragment
|
fun bindFtueAuthAccountCreatedFragment(fragment: FtueAuthAccountCreatedFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(FtueAuthEmailEntryFragment::class)
|
||||||
|
fun bindFtueAuthEmailEntryFragment(fragment: FtueAuthEmailEntryFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(FtueAuthChooseDisplayNameFragment::class)
|
@FragmentKey(FtueAuthChooseDisplayNameFragment::class)
|
||||||
|
@ -16,7 +16,11 @@
|
|||||||
|
|
||||||
package im.vector.app.core.extensions
|
package im.vector.app.core.extensions
|
||||||
|
|
||||||
|
import android.text.Editable
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import im.vector.app.core.platform.SimpleTextWatcher
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import reactivecircus.flowbinding.android.widget.textChanges
|
import reactivecircus.flowbinding.android.widget.textChanges
|
||||||
|
|
||||||
@ -30,3 +34,26 @@ fun TextInputLayout.hasSurroundingSpaces() = editText().text.toString().let { it
|
|||||||
fun TextInputLayout.hasContentFlow(mapper: (CharSequence) -> CharSequence = { it }) = editText().textChanges().map { mapper(it).isNotEmpty() }
|
fun TextInputLayout.hasContentFlow(mapper: (CharSequence) -> CharSequence = { it }) = editText().textChanges().map { mapper(it).isNotEmpty() }
|
||||||
|
|
||||||
fun TextInputLayout.content() = editText().text.toString()
|
fun TextInputLayout.content() = editText().text.toString()
|
||||||
|
|
||||||
|
fun TextInputLayout.hasContent() = !editText?.text.isNullOrEmpty()
|
||||||
|
|
||||||
|
fun TextInputLayout.associateContentStateWith(button: View) {
|
||||||
|
editText?.addTextChangedListener(object : SimpleTextWatcher() {
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
val newContent = s.toString()
|
||||||
|
button.isEnabled = newContent.isNotEmpty()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextInputLayout.setOnImeDone(action: () -> Unit) {
|
||||||
|
editText?.setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
when (actionId) {
|
||||||
|
EditorInfo.IME_ACTION_DONE -> {
|
||||||
|
action()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -50,7 +50,6 @@ 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.HomeServerConnectionConfig
|
||||||
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
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.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.RegistrationWizard
|
||||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
@ -275,8 +274,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
else -> when (it) {
|
else -> when (it) {
|
||||||
is RegistrationResult.Success -> onSessionCreated(it.session, isAccountCreated = true)
|
is RegistrationResult.Complete -> onSessionCreated(it.session, isAccountCreated = true)
|
||||||
is RegistrationResult.FlowResponse -> onFlowResponse(it.flowResult, onNextRegistrationStepAction)
|
is RegistrationResult.NextStep -> onFlowResponse(it.flowResult, onNextRegistrationStepAction)
|
||||||
|
is RegistrationResult.SendEmailSuccess -> _viewEvents.post(OnboardingViewEvents.OnSendEmailSuccess(it.email))
|
||||||
|
is RegistrationResult.Error -> _viewEvents.post(OnboardingViewEvents.Failure(it.cause))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -16,26 +16,73 @@
|
|||||||
|
|
||||||
package im.vector.app.features.onboarding
|
package im.vector.app.features.onboarding
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult.FlowResponse
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult.Success
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||||
|
import org.matrix.android.sdk.api.failure.is401
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult as SdkRegistrationResult
|
||||||
|
|
||||||
class RegistrationActionHandler @Inject constructor() {
|
class RegistrationActionHandler @Inject constructor() {
|
||||||
|
|
||||||
suspend fun handleRegisterAction(registrationWizard: RegistrationWizard, action: RegisterAction): RegistrationResult {
|
suspend fun handleRegisterAction(registrationWizard: RegistrationWizard, action: RegisterAction): RegistrationResult {
|
||||||
return when (action) {
|
return when (action) {
|
||||||
RegisterAction.StartRegistration -> registrationWizard.getRegistrationFlow()
|
RegisterAction.StartRegistration -> resultOf { registrationWizard.getRegistrationFlow() }
|
||||||
is RegisterAction.CaptchaDone -> registrationWizard.performReCaptcha(action.captchaResponse)
|
is RegisterAction.CaptchaDone -> resultOf { registrationWizard.performReCaptcha(action.captchaResponse) }
|
||||||
is RegisterAction.AcceptTerms -> registrationWizard.acceptTerms()
|
is RegisterAction.AcceptTerms -> resultOf { registrationWizard.acceptTerms() }
|
||||||
is RegisterAction.RegisterDummy -> registrationWizard.dummy()
|
is RegisterAction.RegisterDummy -> resultOf { registrationWizard.dummy() }
|
||||||
is RegisterAction.AddThreePid -> registrationWizard.addThreePid(action.threePid)
|
is RegisterAction.AddThreePid -> handleAddThreePid(registrationWizard, action)
|
||||||
is RegisterAction.SendAgainThreePid -> registrationWizard.sendAgainThreePid()
|
is RegisterAction.SendAgainThreePid -> resultOf { registrationWizard.sendAgainThreePid() }
|
||||||
is RegisterAction.ValidateThreePid -> registrationWizard.handleValidateThreePid(action.code)
|
is RegisterAction.ValidateThreePid -> resultOf { registrationWizard.handleValidateThreePid(action.code) }
|
||||||
is RegisterAction.CheckIfEmailHasBeenValidated -> registrationWizard.checkIfEmailHasBeenValidated(action.delayMillis)
|
is RegisterAction.CheckIfEmailHasBeenValidated -> resultOf { registrationWizard.checkIfEmailHasBeenValidated(action.delayMillis) }
|
||||||
is RegisterAction.CreateAccount -> registrationWizard.createAccount(action.username, action.password, action.initialDeviceName)
|
is RegisterAction.CreateAccount -> resultOf {
|
||||||
|
registrationWizard.createAccount(
|
||||||
|
action.username,
|
||||||
|
action.password,
|
||||||
|
action.initialDeviceName
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun handleAddThreePid(wizard: RegistrationWizard, action: RegisterAction.AddThreePid): RegistrationResult {
|
||||||
|
return runCatching { wizard.addThreePid(action.threePid) }.fold(
|
||||||
|
onSuccess = {
|
||||||
|
when (it) {
|
||||||
|
is Success -> RegistrationResult.Complete(it.session)
|
||||||
|
is FlowResponse -> RegistrationResult.NextStep(it.flowResult)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
when {
|
||||||
|
action.threePid is RegisterThreePid.Email && it.is401() -> RegistrationResult.SendEmailSuccess(action.threePid.email)
|
||||||
|
else -> RegistrationResult.Error(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun resultOf(block: () -> SdkRegistrationResult): RegistrationResult {
|
||||||
|
return runCatching { block() }.fold(
|
||||||
|
onSuccess = {
|
||||||
|
when (it) {
|
||||||
|
is FlowResponse -> RegistrationResult.NextStep(it.flowResult)
|
||||||
|
is Success -> RegistrationResult.Complete(it.session)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure = { RegistrationResult.Error(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface RegistrationResult {
|
||||||
|
data class Error(val cause: Throwable) : RegistrationResult
|
||||||
|
data class Complete(val session: Session) : RegistrationResult
|
||||||
|
data class NextStep(val flowResult: FlowResult) : RegistrationResult
|
||||||
|
data class SendEmailSuccess(val email: String) : RegistrationResult
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface RegisterAction {
|
sealed interface RegisterAction {
|
||||||
@ -56,7 +103,6 @@ sealed interface RegisterAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun RegisterAction.ignoresResult() = when (this) {
|
fun RegisterAction.ignoresResult() = when (this) {
|
||||||
is RegisterAction.AddThreePid -> true
|
|
||||||
is RegisterAction.SendAgainThreePid -> true
|
is RegisterAction.SendAgainThreePid -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import im.vector.app.core.extensions.hasContent
|
||||||
import im.vector.app.core.platform.SimpleTextWatcher
|
import im.vector.app.core.platform.SimpleTextWatcher
|
||||||
import im.vector.app.databinding.FragmentFtueDisplayNameBinding
|
import im.vector.app.databinding.FragmentFtueDisplayNameBinding
|
||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
@ -69,7 +70,7 @@ class FtueAuthChooseDisplayNameFragment @Inject constructor() : AbstractFtueAuth
|
|||||||
|
|
||||||
override fun updateWithState(state: OnboardingViewState) {
|
override fun updateWithState(state: OnboardingViewState) {
|
||||||
views.displayNameInput.editText?.setText(state.personalizationState.displayName)
|
views.displayNameInput.editText?.setText(state.personalizationState.displayName)
|
||||||
views.displayNameSubmit.isEnabled = views.displayNameInput.hasContentEmpty()
|
views.displayNameSubmit.isEnabled = views.displayNameInput.hasContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetViewModel() {
|
override fun resetViewModel() {
|
||||||
@ -81,5 +82,3 @@ class FtueAuthChooseDisplayNameFragment @Inject constructor() : AbstractFtueAuth
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TextInputLayout.hasContentEmpty() = !editText?.text.isNullOrEmpty()
|
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.onboarding.ftueauth
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import im.vector.app.core.extensions.associateContentStateWith
|
||||||
|
import im.vector.app.core.extensions.content
|
||||||
|
import im.vector.app.core.extensions.editText
|
||||||
|
import im.vector.app.core.extensions.hasContent
|
||||||
|
import im.vector.app.core.extensions.isEmail
|
||||||
|
import im.vector.app.core.extensions.setOnImeDone
|
||||||
|
import im.vector.app.databinding.FragmentFtueEmailInputBinding
|
||||||
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
|
import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||||
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
|
import im.vector.app.features.onboarding.RegisterAction
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
|
import org.matrix.android.sdk.api.failure.is401
|
||||||
|
import reactivecircus.flowbinding.android.widget.textChanges
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class FtueAuthEmailEntryFragment @Inject constructor() : AbstractFtueAuthFragment<FragmentFtueEmailInputBinding>() {
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueEmailInputBinding {
|
||||||
|
return FragmentFtueEmailInputBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupViews() {
|
||||||
|
views.emailEntryInput.associateContentStateWith(button = views.emailEntrySubmit)
|
||||||
|
views.emailEntryInput.setOnImeDone { updateEmail() }
|
||||||
|
views.emailEntrySubmit.debouncedClicks { updateEmail() }
|
||||||
|
|
||||||
|
views.emailEntryInput.editText().textChanges()
|
||||||
|
.onEach {
|
||||||
|
views.emailEntryInput.error = null
|
||||||
|
views.emailEntrySubmit.isEnabled = it.isEmail()
|
||||||
|
}
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateEmail() {
|
||||||
|
val email = views.emailEntryInput.content()
|
||||||
|
viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.AddThreePid(RegisterThreePid.Email(email))))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(throwable: Throwable) {
|
||||||
|
views.emailEntryInput.error = errorFormatter.toHumanReadable(throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateWithState(state: OnboardingViewState) {
|
||||||
|
views.emailEntrySubmit.isEnabled = views.emailEntryInput.content().isEmail()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetViewModel() {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||||
|
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -223,12 +223,7 @@ class FtueAuthGenericTextInputFormFragment @Inject constructor() : AbstractFtueA
|
|||||||
override fun onError(throwable: Throwable) {
|
override fun onError(throwable: Throwable) {
|
||||||
when (params.mode) {
|
when (params.mode) {
|
||||||
TextInputFormFragmentMode.SetEmail -> {
|
TextInputFormFragmentMode.SetEmail -> {
|
||||||
if (throwable.is401()) {
|
views.loginGenericTextInputFormTil.error = errorFormatter.toHumanReadable(throwable)
|
||||||
// This is normal use case, we go to the mail waiting screen
|
|
||||||
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnSendEmailSuccess(viewModel.currentThreePid ?: "")))
|
|
||||||
} else {
|
|
||||||
views.loginGenericTextInputFormTil.error = errorFormatter.toHumanReadable(throwable)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TextInputFormFragmentMode.SetMsisdn -> {
|
TextInputFormFragmentMode.SetMsisdn -> {
|
||||||
if (throwable.is401()) {
|
if (throwable.is401()) {
|
||||||
|
@ -393,10 +393,7 @@ class FtueAuthVariant(
|
|||||||
|
|
||||||
when (stage) {
|
when (stage) {
|
||||||
is Stage.ReCaptcha -> onCaptcha(stage)
|
is Stage.ReCaptcha -> onCaptcha(stage)
|
||||||
is Stage.Email -> addRegistrationStageFragmentToBackstack(
|
is Stage.Email -> onEmail(stage)
|
||||||
FtueAuthGenericTextInputFormFragment::class.java,
|
|
||||||
FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetEmail, stage.mandatory),
|
|
||||||
)
|
|
||||||
is Stage.Msisdn -> addRegistrationStageFragmentToBackstack(
|
is Stage.Msisdn -> addRegistrationStageFragmentToBackstack(
|
||||||
FtueAuthGenericTextInputFormFragment::class.java,
|
FtueAuthGenericTextInputFormFragment::class.java,
|
||||||
FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory),
|
FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory),
|
||||||
@ -406,6 +403,18 @@ class FtueAuthVariant(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onEmail(stage: Stage) {
|
||||||
|
when {
|
||||||
|
vectorFeatures.isOnboardingCombinedRegisterEnabled() -> addRegistrationStageFragmentToBackstack(
|
||||||
|
FtueAuthEmailEntryFragment::class.java
|
||||||
|
)
|
||||||
|
else -> addRegistrationStageFragmentToBackstack(
|
||||||
|
FtueAuthGenericTextInputFormFragment::class.java,
|
||||||
|
FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetEmail, stage.mandatory),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onTerms(stage: Stage.Terms) {
|
private fun onTerms(stage: Stage.Terms) {
|
||||||
when {
|
when {
|
||||||
vectorFeatures.isOnboardingCombinedRegisterEnabled() -> addRegistrationStageFragmentToBackstack(
|
vectorFeatures.isOnboardingCombinedRegisterEnabled() -> addRegistrationStageFragmentToBackstack(
|
||||||
|
10
vector/src/main/res/drawable/ic_email.xml
Normal file
10
vector/src/main/res/drawable/ic_email.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="71dp"
|
||||||
|
android:height="70dp"
|
||||||
|
android:viewportWidth="71"
|
||||||
|
android:viewportHeight="70">
|
||||||
|
<path
|
||||||
|
android:pathData="M16.261,23.576L34.905,42.161C35.545,42.799 36.581,42.799 37.221,42.161L55.773,23.667C55.92,24.084 56,24.533 56,25V46C56,48.209 54.209,50 52,50H20C17.791,50 16,48.209 16,46V25C16,24.498 16.092,24.018 16.261,23.576ZM18.582,21.258C19.023,21.091 19.501,21 20,21H52C52.533,21 53.042,21.104 53.508,21.294L36.063,38.684L18.582,21.258Z"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
133
vector/src/main/res/layout/fragment_ftue_email_input.xml
Normal file
133
vector/src/main/res/layout/fragment_ftue_email_input.xml
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/LoginFormScrollView"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?android:colorBackground"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingBottom="0dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/emailEntryGutterStart"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_start_percent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/emailEntryGutterEnd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_end_percent" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/headerSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/emailEntryHeaderIcon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/emailEntryHeaderIcon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:background="@drawable/circle"
|
||||||
|
android:backgroundTint="?colorSecondary"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/ic_email"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/emailEntryHeaderTitle"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/emailEntryGutterEnd"
|
||||||
|
app:layout_constraintHeight_percent="0.12"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/emailEntryGutterStart"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:tint="@color/palette_white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/emailEntryHeaderTitle"
|
||||||
|
style="@style/Widget.Vector.TextView.Title.Medium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/ftue_auth_email_title"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/emailEntryHeaderSubtitle"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/emailEntryGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/emailEntryGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/emailEntryHeaderIcon" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/emailEntryHeaderSubtitle"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/ftue_auth_email_subtitle"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/titleContentSpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/emailEntryGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/emailEntryGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/emailEntryHeaderTitle" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/titleContentSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/emailEntryInput"
|
||||||
|
app:layout_constraintHeight_percent="0.03"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/emailEntryHeaderSubtitle" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/emailEntryInput"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/ftue_auth_email_entry_title"
|
||||||
|
app:endIconMode="clear_text"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/emailEntryGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/emailEntryGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/titleContentSpacing">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:imeOptions="actionDone"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/entrySpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/emailEntrySubmit"
|
||||||
|
app:layout_constraintHeight_percent="0.03"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/emailEntryInput"
|
||||||
|
app:layout_constraintVertical_bias="0"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/emailEntrySubmit"
|
||||||
|
style="@style/Widget.Vector.Button.Login"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/login_set_email_submit"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/emailEntryGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/emailEntryGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/entrySpacing" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
|
@ -31,4 +31,8 @@
|
|||||||
<string name="ftue_auth_terms_title">Privacy policy</string>
|
<string name="ftue_auth_terms_title">Privacy policy</string>
|
||||||
<string name="ftue_auth_terms_subtitle">Please read through T&C. You must accept in order to continue.</string>
|
<string name="ftue_auth_terms_subtitle">Please read through T&C. You must accept in order to continue.</string>
|
||||||
|
|
||||||
|
<string name="ftue_auth_email_title">Enter your email address</string>
|
||||||
|
<string name="ftue_auth_email_subtitle">This will help verify your account and enables password recovery.</string>
|
||||||
|
<string name="ftue_auth_email_entry_title">Email Address</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -47,7 +47,6 @@ import org.junit.Test
|
|||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
|
||||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||||
@ -60,7 +59,7 @@ private val A_NON_LOADABLE_REGISTER_ACTION = RegisterAction.CheckIfEmailHasBeenV
|
|||||||
private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.AddThreePid(RegisterThreePid.Email("an email"))
|
private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.AddThreePid(RegisterThreePid.Email("an email"))
|
||||||
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
|
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
|
||||||
private val AN_IGNORED_FLOW_RESULT = FlowResult(missingStages = emptyList(), completedStages = emptyList())
|
private val AN_IGNORED_FLOW_RESULT = FlowResult(missingStages = emptyList(), completedStages = emptyList())
|
||||||
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationResult.FlowResponse(AN_IGNORED_FLOW_RESULT)
|
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationResult.NextStep(AN_IGNORED_FLOW_RESULT)
|
||||||
private val A_LOGIN_OR_REGISTER_ACTION = OnboardingAction.LoginOrRegister("@a-user:id.org", "a-password", "a-device-name")
|
private val A_LOGIN_OR_REGISTER_ACTION = OnboardingAction.LoginOrRegister("@a-user:id.org", "a-password", "a-device-name")
|
||||||
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
||||||
private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
|
private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
|
||||||
@ -230,7 +229,7 @@ class OnboardingViewModelTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `given register action ignores result, when handling action, then does nothing on success`() = runTest {
|
fun `given register action ignores result, when handling action, then does nothing on success`() = runTest {
|
||||||
val test = viewModel.test()
|
val test = viewModel.test()
|
||||||
givenRegistrationResultFor(A_RESULT_IGNORED_REGISTER_ACTION, RegistrationResult.FlowResponse(AN_IGNORED_FLOW_RESULT))
|
givenRegistrationResultFor(A_RESULT_IGNORED_REGISTER_ACTION, RegistrationResult.NextStep(AN_IGNORED_FLOW_RESULT))
|
||||||
|
|
||||||
viewModel.handle(OnboardingAction.PostRegisterAction(A_RESULT_IGNORED_REGISTER_ACTION))
|
viewModel.handle(OnboardingAction.PostRegisterAction(A_RESULT_IGNORED_REGISTER_ACTION))
|
||||||
|
|
||||||
@ -249,7 +248,7 @@ class OnboardingViewModelTest {
|
|||||||
viewModelWith(initialState.copy(onboardingFlow = OnboardingFlow.SignUp))
|
viewModelWith(initialState.copy(onboardingFlow = OnboardingFlow.SignUp))
|
||||||
fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, A_HOMESERVER_CONFIG)
|
fakeHomeServerConnectionConfigFactory.givenConfigFor(A_HOMESERVER_URL, A_HOMESERVER_CONFIG)
|
||||||
fakeStartAuthenticationFlowUseCase.givenResult(A_HOMESERVER_CONFIG, StartAuthenticationResult(isHomeserverOutdated = false, SELECTED_HOMESERVER_STATE))
|
fakeStartAuthenticationFlowUseCase.givenResult(A_HOMESERVER_CONFIG, StartAuthenticationResult(isHomeserverOutdated = false, SELECTED_HOMESERVER_STATE))
|
||||||
givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationResult.FlowResponse(AN_IGNORED_FLOW_RESULT))
|
givenRegistrationResultFor(RegisterAction.StartRegistration, RegistrationResult.NextStep(AN_IGNORED_FLOW_RESULT))
|
||||||
fakeHomeServerHistoryService.expectUrlToBeAdded(A_HOMESERVER_CONFIG.homeServerUri.toString())
|
fakeHomeServerHistoryService.expectUrlToBeAdded(A_HOMESERVER_CONFIG.homeServerUri.toString())
|
||||||
val test = viewModel.test()
|
val test = viewModel.test()
|
||||||
|
|
||||||
@ -291,7 +290,7 @@ class OnboardingViewModelTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `given personalisation enabled, when registering account, then updates state and emits account created event`() = runTest {
|
fun `given personalisation enabled, when registering account, then updates state and emits account created event`() = runTest {
|
||||||
fakeVectorFeatures.givenPersonalisationEnabled()
|
fakeVectorFeatures.givenPersonalisationEnabled()
|
||||||
givenRegistrationResultFor(A_LOADABLE_REGISTER_ACTION, RegistrationResult.Success(fakeSession))
|
givenRegistrationResultFor(A_LOADABLE_REGISTER_ACTION, RegistrationResult.Complete(fakeSession))
|
||||||
givenSuccessfullyCreatesAccount(A_HOMESERVER_CAPABILITIES)
|
givenSuccessfullyCreatesAccount(A_HOMESERVER_CAPABILITIES)
|
||||||
val test = viewModel.test()
|
val test = viewModel.test()
|
||||||
|
|
||||||
@ -495,8 +494,8 @@ class OnboardingViewModelTest {
|
|||||||
val flowResult = FlowResult(missingStages = missingStages, completedStages = emptyList())
|
val flowResult = FlowResult(missingStages = missingStages, completedStages = emptyList())
|
||||||
givenRegistrationResultsFor(
|
givenRegistrationResultsFor(
|
||||||
listOf(
|
listOf(
|
||||||
A_LOADABLE_REGISTER_ACTION to RegistrationResult.FlowResponse(flowResult),
|
A_LOADABLE_REGISTER_ACTION to RegistrationResult.NextStep(flowResult),
|
||||||
RegisterAction.RegisterDummy to RegistrationResult.Success(fakeSession)
|
RegisterAction.RegisterDummy to RegistrationResult.Complete(fakeSession)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
givenSuccessfullyCreatesAccount(A_HOMESERVER_CAPABILITIES)
|
givenSuccessfullyCreatesAccount(A_HOMESERVER_CAPABILITIES)
|
||||||
|
@ -18,16 +18,17 @@ package im.vector.app.features.onboarding
|
|||||||
|
|
||||||
import im.vector.app.test.fakes.FakeRegistrationWizard
|
import im.vector.app.test.fakes.FakeRegistrationWizard
|
||||||
import im.vector.app.test.fakes.FakeSession
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import im.vector.app.test.fixtures.a401ServerError
|
||||||
import io.mockk.coVerifyAll
|
import io.mockk.coVerifyAll
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
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.RegistrationWizard
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegistrationResult as SdkResult
|
||||||
|
|
||||||
private val A_SESSION = FakeSession()
|
private val A_SESSION = FakeSession()
|
||||||
private val AN_EXPECTED_RESULT = RegistrationResult.Success(A_SESSION)
|
private val AN_EXPECTED_RESULT = RegistrationResult.Complete(A_SESSION)
|
||||||
private const val A_USERNAME = "a username"
|
private const val A_USERNAME = "a username"
|
||||||
private const val A_PASSWORD = "a password"
|
private const val A_PASSWORD = "a password"
|
||||||
private const val AN_INITIAL_DEVICE_NAME = "a device name"
|
private const val AN_INITIAL_DEVICE_NAME = "a device name"
|
||||||
@ -57,6 +58,20 @@ class RegistrationActionHandlerTest {
|
|||||||
cases.forEach { testSuccessfulActionDelegation(it) }
|
cases.forEach { testSuccessfulActionDelegation(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given adding an email ThreePid fails with 401, when handling register action, then infer EmailSuccess`() = runTest {
|
||||||
|
val registrationActionHandler = RegistrationActionHandler()
|
||||||
|
val fakeRegistrationWizard = FakeRegistrationWizard()
|
||||||
|
fakeRegistrationWizard.givenAddEmailThreePidErrors(
|
||||||
|
cause = a401ServerError(),
|
||||||
|
email = A_PID_TO_REGISTER.email
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = registrationActionHandler.handleRegisterAction(fakeRegistrationWizard, RegisterAction.AddThreePid(A_PID_TO_REGISTER))
|
||||||
|
|
||||||
|
result shouldBeEqualTo RegistrationResult.SendEmailSuccess(A_PID_TO_REGISTER.email)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun testSuccessfulActionDelegation(case: Case) {
|
private suspend fun testSuccessfulActionDelegation(case: Case) {
|
||||||
val registrationActionHandler = RegistrationActionHandler()
|
val registrationActionHandler = RegistrationActionHandler()
|
||||||
val fakeRegistrationWizard = FakeRegistrationWizard()
|
val fakeRegistrationWizard = FakeRegistrationWizard()
|
||||||
@ -69,6 +84,6 @@ class RegistrationActionHandlerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun case(action: RegisterAction, expect: suspend RegistrationWizard.() -> RegistrationResult) = Case(action, expect)
|
private fun case(action: RegisterAction, expect: suspend RegistrationWizard.() -> SdkResult) = Case(action, expect)
|
||||||
|
|
||||||
private class Case(val action: RegisterAction, val expect: suspend RegistrationWizard.() -> RegistrationResult)
|
private class Case(val action: RegisterAction, val expect: suspend RegistrationWizard.() -> SdkResult)
|
||||||
|
@ -18,9 +18,9 @@ package im.vector.app.test.fakes
|
|||||||
|
|
||||||
import im.vector.app.features.onboarding.RegisterAction
|
import im.vector.app.features.onboarding.RegisterAction
|
||||||
import im.vector.app.features.onboarding.RegistrationActionHandler
|
import im.vector.app.features.onboarding.RegistrationActionHandler
|
||||||
|
import im.vector.app.features.onboarding.RegistrationResult
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
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.RegistrationWizard
|
||||||
|
|
||||||
class FakeRegisterActionHandler {
|
class FakeRegisterActionHandler {
|
||||||
|
@ -18,6 +18,7 @@ package im.vector.app.test.fakes
|
|||||||
|
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
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.RegistrationWizard
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
@ -27,4 +28,8 @@ class FakeRegistrationWizard : RegistrationWizard by mockk(relaxed = false) {
|
|||||||
fun givenSuccessFor(result: Session, expect: suspend RegistrationWizard.() -> RegistrationResult) {
|
fun givenSuccessFor(result: Session, expect: suspend RegistrationWizard.() -> RegistrationResult) {
|
||||||
coEvery { expect(this@FakeRegistrationWizard) } returns RegistrationResult.Success(result)
|
coEvery { expect(this@FakeRegistrationWizard) } returns RegistrationResult.Success(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenAddEmailThreePidErrors(cause: Throwable, email: String) {
|
||||||
|
coEvery { addThreePid(RegisterThreePid.Email(email)) } throws cause
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
25
vector/src/test/java/im/vector/app/test/fixtures/FailureFixture.kt
vendored
Normal file
25
vector/src/test/java/im/vector/app/test/fixtures/FailureFixture.kt
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.test.fixtures
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
|
fun a401ServerError() = Failure.ServerError(
|
||||||
|
MatrixError(MatrixError.M_UNAUTHORIZED, ""), HttpsURLConnection.HTTP_UNAUTHORIZED
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user