mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
adding dedicated login action
This commit is contained in:
parent
34e97112a4
commit
4b6f74364d
6 changed files with 493 additions and 13 deletions
|
@ -49,6 +49,7 @@ sealed interface OnboardingAction : VectorViewModelAction {
|
||||||
// Login or Register, depending on the signMode
|
// Login or Register, depending on the signMode
|
||||||
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
||||||
data class Register(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
data class Register(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
||||||
|
data class Login(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
||||||
object StopEmailValidationCheck : OnboardingAction
|
object StopEmailValidationCheck : OnboardingAction
|
||||||
|
|
||||||
data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
|
data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
|
||||||
|
|
|
@ -141,6 +141,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
is OnboardingAction.HomeServerChange -> withAction(action) { handleHomeserverChange(action) }
|
is OnboardingAction.HomeServerChange -> withAction(action) { handleHomeserverChange(action) }
|
||||||
is OnboardingAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
|
is OnboardingAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
|
||||||
is OnboardingAction.Register -> handleRegisterWith(action).also { lastAction = action }
|
is OnboardingAction.Register -> handleRegisterWith(action).also { lastAction = action }
|
||||||
|
is OnboardingAction.Login -> handleLogin(action).also { lastAction = action }
|
||||||
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
|
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
|
||||||
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||||
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
||||||
|
@ -188,18 +189,21 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
|
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
|
||||||
val nextOnboardingStep = when (onboardingFlow) {
|
when (onboardingFlow) {
|
||||||
OnboardingFlow.SignUp -> if (vectorFeatures.isOnboardingUseCaseEnabled()) {
|
OnboardingFlow.SignUp -> {
|
||||||
OnboardingViewEvents.OpenUseCaseSelection
|
_viewEvents.post(
|
||||||
} else {
|
if (vectorFeatures.isOnboardingUseCaseEnabled()) {
|
||||||
OnboardingViewEvents.OpenServerSelection
|
OnboardingViewEvents.OpenUseCaseSelection
|
||||||
|
} else {
|
||||||
|
OnboardingViewEvents.OpenServerSelection
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
OnboardingFlow.SignIn -> if (vectorFeatures.isOnboardingCombinedRegisterEnabled()) {
|
OnboardingFlow.SignIn -> if (vectorFeatures.isOnboardingCombinedLoginEnabled()) {
|
||||||
OnboardingViewEvents.OpenCombinedLogin
|
handle(OnboardingAction.HomeServerChange.SelectHomeServer(defaultHomeserverUrl))
|
||||||
} else OnboardingViewEvents.OpenServerSelection
|
} else _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
|
||||||
OnboardingFlow.SignInSignUp -> OnboardingViewEvents.OpenServerSelection
|
OnboardingFlow.SignInSignUp -> _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
|
||||||
}
|
}
|
||||||
_viewEvents.post(nextOnboardingStep)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
|
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
|
||||||
|
@ -487,7 +491,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
private fun handleLoginOrRegister(action: OnboardingAction.LoginOrRegister) = withState { state ->
|
private fun handleLoginOrRegister(action: OnboardingAction.LoginOrRegister) = withState { state ->
|
||||||
when (state.signMode) {
|
when (state.signMode) {
|
||||||
SignMode.Unknown -> error("Developer error, invalid sign mode")
|
SignMode.Unknown -> error("Developer error, invalid sign mode")
|
||||||
SignMode.SignIn -> handleLogin(action)
|
SignMode.SignIn -> handleLogin(OnboardingAction.Login(action.username, action.password, action.initialDeviceName))
|
||||||
SignMode.SignUp -> handleRegisterWith(OnboardingAction.Register(action.username, action.password, action.initialDeviceName))
|
SignMode.SignUp -> handleRegisterWith(OnboardingAction.Register(action.username, action.password, action.initialDeviceName))
|
||||||
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
|
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
|
||||||
}
|
}
|
||||||
|
@ -506,7 +510,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLogin(action: OnboardingAction.LoginOrRegister) {
|
private fun handleLogin(action: OnboardingAction.Login) {
|
||||||
val safeLoginWizard = loginWizard
|
val safeLoginWizard = loginWizard
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
if (safeLoginWizard == null) {
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.onboarding.ftueauth
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import androidx.autofill.HintConstants
|
||||||
|
import androidx.core.text.isDigitsOnly
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.content
|
||||||
|
import im.vector.app.core.extensions.editText
|
||||||
|
import im.vector.app.core.extensions.hasContentFlow
|
||||||
|
import im.vector.app.core.extensions.hasSurroundingSpaces
|
||||||
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
|
import im.vector.app.core.extensions.hidePassword
|
||||||
|
import im.vector.app.core.extensions.realignPercentagesToParent
|
||||||
|
import im.vector.app.core.extensions.toReducedUrl
|
||||||
|
import im.vector.app.databinding.FragmentFtueCombinedLoginBinding
|
||||||
|
import im.vector.app.features.login.LoginMode
|
||||||
|
import im.vector.app.features.login.SSORedirectRouterActivity
|
||||||
|
import im.vector.app.features.login.SocialLoginButtonsView
|
||||||
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
|
import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||||
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||||
|
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||||
|
import org.matrix.android.sdk.api.failure.isInvalidUsername
|
||||||
|
import org.matrix.android.sdk.api.failure.isLoginEmailUnknown
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class FtueAuthCombinedLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<FragmentFtueCombinedLoginBinding>() {
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueCombinedLoginBinding {
|
||||||
|
return FragmentFtueCombinedLoginBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupSubmitButton()
|
||||||
|
views.createAccountRoot.realignPercentagesToParent()
|
||||||
|
views.editServerButton.debouncedClicks {
|
||||||
|
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.EditServerSelection))
|
||||||
|
}
|
||||||
|
|
||||||
|
views.createAccountPasswordInput.editText().setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
|
submit()
|
||||||
|
return@setOnEditorActionListener true
|
||||||
|
}
|
||||||
|
return@setOnEditorActionListener false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSubmitButton() {
|
||||||
|
views.createAccountSubmit.setOnClickListener { submit() }
|
||||||
|
observeInputFields()
|
||||||
|
.onEach {
|
||||||
|
views.createAccountPasswordInput.error = null
|
||||||
|
views.createAccountInput.error = null
|
||||||
|
views.createAccountSubmit.isEnabled = it
|
||||||
|
}
|
||||||
|
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeInputFields() = combine(
|
||||||
|
views.createAccountInput.hasContentFlow { it.trim() },
|
||||||
|
views.createAccountPasswordInput.hasContentFlow(),
|
||||||
|
transform = { isLoginNotEmpty, isPasswordNotEmpty -> isLoginNotEmpty && isPasswordNotEmpty }
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun submit() {
|
||||||
|
withState(viewModel) { state ->
|
||||||
|
cleanupUi()
|
||||||
|
|
||||||
|
val login = views.createAccountInput.content()
|
||||||
|
val password = views.createAccountPasswordInput.content()
|
||||||
|
|
||||||
|
// This can be called by the IME action, so deal with empty cases
|
||||||
|
var error = 0
|
||||||
|
if (login.isEmpty()) {
|
||||||
|
views.createAccountInput.error = getString(R.string.error_empty_field_choose_user_name)
|
||||||
|
error++
|
||||||
|
}
|
||||||
|
if (state.isNumericOnlyUserIdForbidden() && login.isDigitsOnly()) {
|
||||||
|
views.createAccountInput.error = getString(R.string.error_forbidden_digits_only_username)
|
||||||
|
error++
|
||||||
|
}
|
||||||
|
if (password.isEmpty()) {
|
||||||
|
views.createAccountPasswordInput.error = getString(R.string.error_empty_field_choose_password)
|
||||||
|
error++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == 0) {
|
||||||
|
viewModel.handle(OnboardingAction.Login(login, password, getString(R.string.login_default_session_public_name)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanupUi() {
|
||||||
|
views.createAccountSubmit.hideKeyboard()
|
||||||
|
views.createAccountInput.error = null
|
||||||
|
views.createAccountPasswordInput.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetViewModel() {
|
||||||
|
viewModel.handle(OnboardingAction.ResetAuthenticationAttempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(throwable: Throwable) {
|
||||||
|
// Trick to display the error without text.
|
||||||
|
views.createAccountInput.error = " "
|
||||||
|
when {
|
||||||
|
throwable.isInvalidUsername() -> {
|
||||||
|
views.createAccountInput.error = errorFormatter.toHumanReadable(throwable)
|
||||||
|
}
|
||||||
|
throwable.isLoginEmailUnknown() -> {
|
||||||
|
views.createAccountInput.error = getString(R.string.login_login_with_email_error)
|
||||||
|
}
|
||||||
|
throwable.isInvalidPassword() && views.createAccountPasswordInput.hasSurroundingSpaces() -> {
|
||||||
|
views.createAccountPasswordInput.error = getString(R.string.auth_invalid_login_param_space_in_password)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
super.onError(throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateWithState(state: OnboardingViewState) {
|
||||||
|
setupUi(state)
|
||||||
|
setupAutoFill()
|
||||||
|
|
||||||
|
views.selectedServerName.text = state.selectedHomeserver.userFacingUrl.toReducedUrl()
|
||||||
|
views.selectedServerDescription.text = state.selectedHomeserver.description
|
||||||
|
|
||||||
|
if (state.isLoading) {
|
||||||
|
// Ensure password is hidden
|
||||||
|
views.createAccountPasswordInput.editText().hidePassword()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupUi(state: OnboardingViewState) {
|
||||||
|
when (state.selectedHomeserver.preferredLoginMode) {
|
||||||
|
is LoginMode.SsoAndPassword -> renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
|
||||||
|
else -> hideSsoProviders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
|
||||||
|
views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
|
||||||
|
views.ssoButtons.mode = SocialLoginButtonsView.Mode.MODE_CONTINUE
|
||||||
|
views.ssoButtons.ssoIdentityProviders = ssoProviders?.sorted()
|
||||||
|
views.ssoButtons.listener = SocialLoginButtonsView.InteractionListener { id ->
|
||||||
|
viewModel.getSsoUrl(
|
||||||
|
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||||
|
deviceId = deviceId,
|
||||||
|
providerId = id
|
||||||
|
)?.let { openInCustomTab(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideSsoProviders() {
|
||||||
|
views.ssoGroup.isVisible = false
|
||||||
|
views.ssoButtons.ssoIdentityProviders = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupAutoFill() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
views.createAccountInput.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_USERNAME)
|
||||||
|
views.createAccountPasswordInput.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun OnboardingViewState.isNumericOnlyUserIdForbidden() = selectedHomeserver.userFacingUrl == getString(R.string.matrix_org_server_url)
|
||||||
|
}
|
|
@ -233,7 +233,7 @@ class FtueAuthVariant(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onStartCombinedLogin() {
|
private fun onStartCombinedLogin() {
|
||||||
addRegistrationStageFragmentToBackstack(FtueAuthCombinedRegisterFragment::class.java)
|
addRegistrationStageFragmentToBackstack(FtueAuthCombinedLoginFragment::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onRegistrationFlow(viewEvents: OnboardingViewEvents.RegistrationFlowResult) {
|
private fun onRegistrationFlow(viewEvents: OnboardingViewEvents.RegistrationFlowResult) {
|
||||||
|
|
275
vector/src/main/res/layout/fragment_ftue_combined_login.xml
Normal file
275
vector/src/main/res/layout/fragment_ftue_combined_login.xml
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
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:id="@+id/createAccountRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/createAccountGutterStart"
|
||||||
|
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/createAccountGutterEnd"
|
||||||
|
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/createAccountHeaderTitle"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/createAccountHeaderTitle"
|
||||||
|
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_welcome_back_title"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/createAccountHeaderSubtitle"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/headerSpacing" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/createAccountHeaderSubtitle"
|
||||||
|
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_create_account_subtitle"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/titleContentSpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountHeaderTitle" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/titleContentSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/chooseYourServerHeader"
|
||||||
|
app:layout_constraintHeight_percent="0.03"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountHeaderSubtitle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/chooseYourServerHeader"
|
||||||
|
style="@style/Widget.Vector.TextView.Caption"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_choose_server_header"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/selectedServerName"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/editServerButton"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/titleContentSpacing" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/selectedServerName"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/selectedServerDescription"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/editServerButton"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/chooseYourServerHeader" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/selectedServerDescription"
|
||||||
|
style="@style/Widget.Vector.TextView.Micro"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:textColor="?vctr_content_tertiary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/serverSelectionSpacing"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/editServerButton"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/selectedServerName" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/editServerButton"
|
||||||
|
style="@style/Widget.Vector.Button.Outlined"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_edit_server_selection"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/selectedServerDescription"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/chooseYourServerHeader" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/serverSelectionSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/createAccountInput"
|
||||||
|
app:layout_constraintHeight_percent="0.05"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/selectedServerDescription" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?vctr_content_quaternary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/serverSelectionSpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/serverSelectionSpacing" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/createAccountInput"
|
||||||
|
style="@style/Widget.Vector.TextInputLayout.Username"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/username"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/createAccountEntryFooter"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/serverSelectionSpacing">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:imeOptions="actionNext"
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:nextFocusForward="@id/createAccountPasswordInput" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/createAccountEntryFooter"
|
||||||
|
style="@style/Widget.Vector.TextView.Micro"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_username_entry_footer"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/entrySpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountInput" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/entrySpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/createAccountPasswordInput"
|
||||||
|
app:layout_constraintHeight_percent="0.03"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountEntryFooter" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/createAccountPasswordInput"
|
||||||
|
style="@style/Widget.Vector.TextInputLayout.Password"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/login_signup_password_hint"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/createAccountPasswordEntryFooter"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/entrySpacing">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:imeOptions="actionDone"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/createAccountPasswordEntryFooter"
|
||||||
|
style="@style/Widget.Vector.TextView.Micro"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_password_entry_footer"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/actionSpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountPasswordInput" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/actionSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/createAccountSubmit"
|
||||||
|
app:layout_constraintHeight_percent="0.02"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountPasswordEntryFooter" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/createAccountSubmit"
|
||||||
|
style="@style/Widget.Vector.Button.Login"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/login_signup_submit"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/actionSpacing" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/ssoGroup"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="ssoButtonsHeader,ssoButtons"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountSubmit"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ssoButtonsHeader"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle.Medium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_sso_section_header"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/ssoButtons"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/createAccountSubmit" />
|
||||||
|
|
||||||
|
<im.vector.app.features.login.SocialLoginButtonsView
|
||||||
|
android:id="@+id/ssoButtons"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/createAccountGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/createAccountGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/ssoButtonsHeader"
|
||||||
|
tools:signMode="signup" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
|
@ -19,6 +19,8 @@
|
||||||
<string name="ftue_auth_create_account_matrix_dot_org_server_description">Join millions for free on the largest public server</string>
|
<string name="ftue_auth_create_account_matrix_dot_org_server_description">Join millions for free on the largest public server</string>
|
||||||
<string name="ftue_auth_create_account_edit_server_selection">Edit</string>
|
<string name="ftue_auth_create_account_edit_server_selection">Edit</string>
|
||||||
|
|
||||||
|
<string name="ftue_auth_welcome_back_title">Welcome back!</string>
|
||||||
|
|
||||||
<string name="ftue_auth_choose_server_title">Choose your server</string>
|
<string name="ftue_auth_choose_server_title">Choose your server</string>
|
||||||
<string name="ftue_auth_choose_server_subtitle">What is the address of your server? Server is like a home for all your data.</string>
|
<string name="ftue_auth_choose_server_subtitle">What is the address of your server? Server is like a home for all your data.</string>
|
||||||
<string name="ftue_auth_choose_server_entry_hint">Server URL</string>
|
<string name="ftue_auth_choose_server_entry_hint">Server URL</string>
|
||||||
|
|
Loading…
Reference in a new issue