mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 20:10:04 +03:00
Login screens: forget password screens
This commit is contained in:
parent
b7bfb20a2e
commit
2871e4f5b1
13 changed files with 491 additions and 13 deletions
|
@ -68,4 +68,9 @@ interface Authenticator {
|
|||
* Create a session after a SSO successful login
|
||||
*/
|
||||
fun createSessionFromSso(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<Session>): Cancelable
|
||||
|
||||
/**
|
||||
* Reset user password
|
||||
*/
|
||||
fun resetPassword(homeServerConnectionConfig: HomeServerConnectionConfig, email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable
|
||||
}
|
||||
|
|
|
@ -131,6 +131,25 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||
sessionManager.getOrCreateSession(sessionParams)
|
||||
}
|
||||
|
||||
override fun resetPassword(homeServerConnectionConfig: HomeServerConnectionConfig, email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val result = runCatching {
|
||||
resetPasswordInternal(/*homeServerConnectionConfig, email, newPassword*/)
|
||||
}
|
||||
result.foldToCallback(callback)
|
||||
}
|
||||
return CancelableCoroutine(job)
|
||||
}
|
||||
|
||||
private fun resetPasswordInternal(/*homeServerConnectionConfig: HomeServerConnectionConfig, email: String, newPassword: String*/) {
|
||||
// TODO
|
||||
error("Not implemented")
|
||||
//val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||
//executeRequest<LoginFlowResponse> {
|
||||
// apiCall = authAPI.getLoginFlows()
|
||||
//}
|
||||
}
|
||||
|
||||
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
||||
val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
|
||||
return retrofit.create(AuthAPI::class.java)
|
||||
|
|
|
@ -26,6 +26,7 @@ sealed class LoginAction : VectorViewModelAction {
|
|||
data class Login(val login: String, val password: String) : LoginAction()
|
||||
data class WebLoginSuccess(val credentials: Credentials) : LoginAction()
|
||||
data class InitWith(val loginConfig: LoginConfig) : LoginAction()
|
||||
data class ResetPassword(val email: String, val newPassword: String) : LoginAction()
|
||||
|
||||
// Reset actions
|
||||
open class ResetAction : LoginAction()
|
||||
|
@ -34,4 +35,5 @@ sealed class LoginAction : VectorViewModelAction {
|
|||
object ResetHomeServerUrl : ResetAction()
|
||||
object ResetSignMode : ResetAction()
|
||||
object ResetLogin : ResetAction()
|
||||
object ResetResetPassword : ResetAction()
|
||||
}
|
||||
|
|
|
@ -65,11 +65,17 @@ class LoginActivity : VectorBaseActivity() {
|
|||
loginSharedActionViewModel.observe()
|
||||
.subscribe {
|
||||
when (it) {
|
||||
is LoginNavigation.OpenServerSelection -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java)
|
||||
is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone()
|
||||
is LoginNavigation.OnSignModeSelected -> onSignModeSelected()
|
||||
is LoginNavigation.OnLoginFlowRetrieved -> onLoginFlowRetrieved()
|
||||
is LoginNavigation.OnWebLoginError -> onWebLoginError(it)
|
||||
is LoginNavigation.OpenServerSelection -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java)
|
||||
is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone()
|
||||
is LoginNavigation.OnSignModeSelected -> onSignModeSelected()
|
||||
is LoginNavigation.OnLoginFlowRetrieved -> onLoginFlowRetrieved()
|
||||
is LoginNavigation.OnWebLoginError -> onWebLoginError(it)
|
||||
is LoginNavigation.OnForgetPasswordClicked -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginResetPasswordFragment::class.java)
|
||||
is LoginNavigation.OnResetPasswordSuccess -> {
|
||||
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
addFragmentToBackstack(R.id.loginFragmentContainer, LoginResetPasswordSuccessFragment::class.java)
|
||||
}
|
||||
is LoginNavigation.OnResetPasswordSuccessDone -> supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
}
|
||||
}
|
||||
.disposeOnDestroy()
|
||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.riotx.features.login
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import butterknife.OnClick
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
|
@ -50,7 +51,7 @@ class LoginFragment @Inject constructor(
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupUi()
|
||||
setupLoginButton()
|
||||
setupSubmitButton()
|
||||
setupPasswordReveal()
|
||||
}
|
||||
|
||||
|
@ -74,19 +75,17 @@ class LoginFragment @Inject constructor(
|
|||
loginServerIcon.setImageResource(R.drawable.ic_logo_modular)
|
||||
// TODO
|
||||
loginTitle.text = getString(R.string.login_connect_to, "TODO")
|
||||
// TODO Remove https://
|
||||
loginNotice.text = loginViewModel.getHomeServerUrl()
|
||||
loginNotice.text = loginViewModel.getHomeServerUrlSimple()
|
||||
}
|
||||
ServerType.Other -> {
|
||||
loginServerIcon.isVisible = false
|
||||
loginTitle.text = getString(R.string.login_server_other_title)
|
||||
// TODO Remove https://
|
||||
loginNotice.text = loginViewModel.getHomeServerUrl()
|
||||
loginNotice.text = loginViewModel.getHomeServerUrlSimple()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupLoginButton() {
|
||||
private fun setupSubmitButton() {
|
||||
Observable
|
||||
.combineLatest(
|
||||
loginField.textChanges().map { it.trim().isNotEmpty() },
|
||||
|
@ -105,6 +104,11 @@ class LoginFragment @Inject constructor(
|
|||
loginSubmit.setOnClickListener { authenticate() }
|
||||
}
|
||||
|
||||
@OnClick(R.id.forgetPasswordButton)
|
||||
fun forgetPasswordClicked() {
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnForgetPasswordClicked)
|
||||
}
|
||||
|
||||
private fun setupPasswordReveal() {
|
||||
passwordShown = false
|
||||
|
||||
|
|
|
@ -24,5 +24,9 @@ sealed class LoginNavigation : VectorSharedAction {
|
|||
object OnServerSelectionDone : LoginNavigation()
|
||||
object OnLoginFlowRetrieved : LoginNavigation()
|
||||
object OnSignModeSelected : LoginNavigation()
|
||||
object OnForgetPasswordClicked : LoginNavigation()
|
||||
object OnResetPasswordSuccess : LoginNavigation()
|
||||
object OnResetPasswordSuccessDone : LoginNavigation()
|
||||
|
||||
data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : LoginNavigation()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.riotx.features.login
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.jakewharton.rxbinding3.widget.textChanges
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import im.vector.riotx.core.extensions.showPassword
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import kotlinx.android.synthetic.main.fragment_login.*
|
||||
import kotlinx.android.synthetic.main.fragment_login.passwordField
|
||||
import kotlinx.android.synthetic.main.fragment_login.passwordFieldTil
|
||||
import kotlinx.android.synthetic.main.fragment_login.passwordReveal
|
||||
import kotlinx.android.synthetic.main.fragment_login_reset_password.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* In this screen, the user is asked for email and new password to reset his password
|
||||
*/
|
||||
class LoginResetPasswordFragment @Inject constructor(
|
||||
private val errorFormatter: ErrorFormatter
|
||||
) : AbstractLoginFragment() {
|
||||
|
||||
private var passwordShown = false
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_login_reset_password
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupUi()
|
||||
setupSubmitButton()
|
||||
setupPasswordReveal()
|
||||
}
|
||||
|
||||
private fun setupUi() {
|
||||
resetPasswordTitle.text = getString(R.string.login_reset_password_on, loginViewModel.getHomeServerUrlSimple())
|
||||
}
|
||||
|
||||
private fun setupSubmitButton() {
|
||||
Observable
|
||||
.combineLatest(
|
||||
resetPasswordEmail.textChanges().map { it.trim().isNotEmpty() },
|
||||
passwordField.textChanges().map { it.trim().isNotEmpty() },
|
||||
BiFunction<Boolean, Boolean, Boolean> { isEmailNotEmpty, isPasswordNotEmpty ->
|
||||
isEmailNotEmpty && isPasswordNotEmpty
|
||||
}
|
||||
)
|
||||
.subscribeBy {
|
||||
resetPasswordEmail.error = null
|
||||
passwordFieldTil.error = null
|
||||
loginSubmit.isEnabled = it
|
||||
}
|
||||
.disposeOnDestroy()
|
||||
|
||||
resetPasswordSubmit.setOnClickListener { submit() }
|
||||
}
|
||||
|
||||
private fun submit() {
|
||||
val email = resetPasswordEmail.text?.trim().toString()
|
||||
val password = passwordField.text?.trim().toString()
|
||||
|
||||
// TODO Add static check?
|
||||
|
||||
loginViewModel.handle(LoginAction.ResetPassword(email, password))
|
||||
}
|
||||
|
||||
private fun setupPasswordReveal() {
|
||||
passwordShown = false
|
||||
|
||||
passwordReveal.setOnClickListener {
|
||||
passwordShown = !passwordShown
|
||||
|
||||
renderPasswordField()
|
||||
}
|
||||
|
||||
renderPasswordField()
|
||||
}
|
||||
|
||||
private fun renderPasswordField() {
|
||||
passwordField.showPassword(passwordShown)
|
||||
|
||||
if (passwordShown) {
|
||||
passwordReveal.setImageResource(R.drawable.ic_eye_closed_black)
|
||||
passwordReveal.contentDescription = getString(R.string.a11y_hide_password)
|
||||
} else {
|
||||
passwordReveal.setImageResource(R.drawable.ic_eye_black)
|
||||
passwordReveal.contentDescription = getString(R.string.a11y_show_password)
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetViewModel() {
|
||||
loginViewModel.handle(LoginAction.ResetResetPassword)
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(loginViewModel) { state ->
|
||||
when (state.asyncResetPassword) {
|
||||
is Loading -> {
|
||||
// Ensure new password is hidden
|
||||
passwordShown = false
|
||||
renderPasswordField()
|
||||
}
|
||||
is Fail -> {
|
||||
// TODO This does not work, we want the error to be on without text. Fix that
|
||||
resetPasswordEmailTil.error = ""
|
||||
// TODO Handle error text properly
|
||||
passwordFieldTil.error = errorFormatter.toHumanReadable(state.asyncResetPassword.error)
|
||||
}
|
||||
is Success -> {
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.riotx.features.login
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import butterknife.OnClick
|
||||
import im.vector.riotx.R
|
||||
import kotlinx.android.synthetic.main.fragment_login_reset_password_success.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* In this screen, the user is asked for email and new password to reset his password
|
||||
*/
|
||||
class LoginResetPasswordSuccessFragment @Inject constructor() : AbstractLoginFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_login_reset_password_success
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupUi()
|
||||
}
|
||||
|
||||
private fun setupUi() {
|
||||
resetPasswordSuccessNotice.text = getString(R.string.login_reset_password_success_notice, loginViewModel.resetPasswordEmail)
|
||||
}
|
||||
|
||||
@OnClick(R.id.resetPasswordSuccessSubmit)
|
||||
fun submit() {
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordSuccessDone)
|
||||
}
|
||||
|
||||
override fun resetViewModel() {
|
||||
loginViewModel.handle(LoginAction.ResetResetPassword)
|
||||
}
|
||||
}
|
|
@ -57,9 +57,10 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
|
||||
var serverType: ServerType = ServerType.MatrixOrg
|
||||
private set
|
||||
|
||||
var signMode: SignMode = SignMode.Unknown
|
||||
private set
|
||||
var resetPasswordEmail: String? = null
|
||||
private set
|
||||
|
||||
private var loginConfig: LoginConfig? = null
|
||||
|
||||
|
@ -74,6 +75,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
is LoginAction.UpdateHomeServer -> handleUpdateHomeserver(action)
|
||||
is LoginAction.Login -> handleLogin(action)
|
||||
is LoginAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||
is LoginAction.ResetPassword -> handleResetPassword(action)
|
||||
is LoginAction.ResetAction -> handleResetAction(action)
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +111,14 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
)
|
||||
}
|
||||
}
|
||||
LoginAction.ResetResetPassword -> {
|
||||
resetPasswordEmail = null
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +134,45 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
loginConfig = action.loginConfig
|
||||
}
|
||||
|
||||
private fun handleResetPassword(action: LoginAction.ResetPassword) {
|
||||
val homeServerConnectionConfigFinal = homeServerConnectionConfig
|
||||
|
||||
if (homeServerConnectionConfigFinal == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Fail(Throwable("Bad configuration"))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
resetPasswordEmail = action.email
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentTask = authenticator.resetPassword(homeServerConnectionConfigFinal, action.email, action.newPassword, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Success(data)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// TODO Handled JobCancellationException
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLogin(action: LoginAction.Login) {
|
||||
val homeServerConnectionConfigFinal = homeServerConnectionConfig
|
||||
|
||||
|
@ -259,4 +308,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
fun getHomeServerUrl(): String {
|
||||
return homeServerConnectionConfig?.homeServerUri?.toString() ?: ""
|
||||
}
|
||||
|
||||
fun getHomeServerUrlSimple(): String {
|
||||
return getHomeServerUrl().substringAfter("://")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,17 +16,21 @@
|
|||
|
||||
package im.vector.riotx.features.login
|
||||
|
||||
|
||||
import com.airbnb.mvrx.*
|
||||
|
||||
data class LoginViewState(
|
||||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
||||
val asyncHomeServerLoginFlowRequest: Async<LoginMode> = Uninitialized
|
||||
val asyncHomeServerLoginFlowRequest: Async<LoginMode> = Uninitialized,
|
||||
val asyncResetPassword: Async<Unit> = Uninitialized
|
||||
) : MvRxState {
|
||||
|
||||
|
||||
fun isLoading(): Boolean {
|
||||
// TODO Add other async here
|
||||
return asyncLoginAction is Loading
|
||||
|| asyncHomeServerLoginFlowRequest is Loading
|
||||
|| asyncResetPassword is Loading
|
||||
}
|
||||
|
||||
fun isUserLogged(): Boolean {
|
||||
|
|
118
vector/src/main/res/layout/fragment_login_reset_password.xml
Normal file
118
vector/src/main/res/layout/fragment_login_reset_password.xml
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoImageView"
|
||||
style="@style/LoginTopIcon"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/resetPasswordTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="84dp"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Title"
|
||||
tools:text="@string/login_reset_password_on" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginNotice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:gravity="start"
|
||||
android:text="@string/login_reset_password_notice"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text.Small" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/resetPasswordEmailTil"
|
||||
style="@style/VectorTextInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/login_reset_password_email_hint"
|
||||
app:errorEnabled="true">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/resetPasswordEmail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress"
|
||||
android:maxLines="1" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/passwordContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/passwordFieldTil"
|
||||
style="@style/VectorTextInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_reset_password_password_hint"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@null">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/passwordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textPassword"
|
||||
android:maxLines="1"
|
||||
android:paddingEnd="48dp"
|
||||
android:paddingRight="48dp"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/passwordReveal"
|
||||
android:layout_width="@dimen/layout_touch_size"
|
||||
android:layout_height="@dimen/layout_touch_size"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_eye_black"
|
||||
android:tint="?attr/colorAccent"
|
||||
tools:contentDescription="@string/a11y_show_password" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/resetPasswordSubmit"
|
||||
style="@style/Style.Vector.Login.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/login_reset_password_submit"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoImageView"
|
||||
style="@style/LoginTopIcon"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="84dp"
|
||||
android:text="@string/login_reset_password_success_title"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/resetPasswordSuccessNotice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text.Small"
|
||||
tools:text="@string/login_reset_password_success_notice" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/login_reset_password_success_notice_2"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text.Small" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/resetPasswordSuccessSubmit"
|
||||
style="@style/Style.Vector.Login.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/login_reset_password_success_submit" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -38,9 +38,11 @@
|
|||
<string name="login_server_other_text">Custom & advanced settings</string>
|
||||
|
||||
<string name="login_continue">Continue</string>
|
||||
<!-- Replaced string is the homeserver url -->
|
||||
<string name="login_connect_to">Connect to %1$s</string>
|
||||
<string name="login_connect_to_modular">Connect to Modular</string>
|
||||
<string name="login_connect_to_a_custom_server">Connect to a custom server</string>
|
||||
<!-- Replaced string is the homeserver url -->
|
||||
<string name="login_signin_to">Sign in to %1$s</string>
|
||||
<string name="login_signup">Sign Up</string>
|
||||
<string name="login_signin">Sign In</string>
|
||||
|
@ -55,4 +57,16 @@
|
|||
<string name="login_sso_error_message">An error occurred when loading the page: %1$s (%2$d)</string>
|
||||
<string name="login_mode_not_supported">The application is not able to signin to this homeserver. The homeserver supports the following signin type(s): %1$s.\n\nDo you want to signin using a web client?</string>
|
||||
|
||||
<!-- Replaced string is the homeserver url -->
|
||||
<string name="login_reset_password_on">Reset password on %1$s</string>
|
||||
<string name="login_reset_password_notice">A verification email will be sent to your inbox to confirm setting your new password.</string>
|
||||
<string name="login_reset_password_submit">Next</string>
|
||||
<string name="login_reset_password_email_hint">Email</string>
|
||||
<string name="login_reset_password_password_hint">New password</string>
|
||||
<string name="login_reset_password_success_title">Check your inbox</string>
|
||||
<!-- Replaced string is an email -->
|
||||
<string name="login_reset_password_success_notice">A verification email was sent to %1$s.</string>
|
||||
<string name="login_reset_password_success_notice_2">Tap on the link to confirm your new password.</string>
|
||||
<string name="login_reset_password_success_submit">Back to Sign In</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Reference in a new issue