Improve prompt password dialog

Reveal password, inline error when empty
This commit is contained in:
Benoit Marty 2020-02-03 14:48:15 +01:00
parent ce13e824b6
commit 783a514496
5 changed files with 143 additions and 77 deletions

View file

@ -29,7 +29,7 @@ import im.vector.riotx.core.platform.SimpleTextWatcher
class ExportKeysDialog {
var passwordVisible = false
private var passwordVisible = false
fun show(activity: Activity, exportKeyDialogListener: ExportKeyDialogListener) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_export_e2e_keys, null)

View file

@ -0,0 +1,87 @@
/*
* Copyright 2020 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.core.dialogs
import android.app.Activity
import android.content.DialogInterface
import android.text.Editable
import android.view.KeyEvent
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import im.vector.riotx.R
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.SimpleTextWatcher
class PromptPasswordDialog {
private var passwordVisible = false
fun show(activity: Activity, listener: (String) -> Unit) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_prompt_password, null)
val passwordTil = dialogLayout.findViewById<TextInputLayout>(R.id.promptPasswordTil)
val passwordEditText = dialogLayout.findViewById<TextInputEditText>(R.id.promptPassword)
val textWatcher = object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
passwordTil.error = null
}
}
passwordEditText.addTextChangedListener(textWatcher)
val showPassword = dialogLayout.findViewById<ImageView>(R.id.promptPasswordPasswordReveal)
showPassword.setOnClickListener {
passwordVisible = !passwordVisible
passwordEditText.showPassword(passwordVisible)
showPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
}
AlertDialog.Builder(activity)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.devices_delete_dialog_title)
.setView(dialogLayout)
.setPositiveButton(R.string.auth_submit, null)
.setNegativeButton(R.string.cancel, null)
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
dialog.cancel()
return@OnKeyListener true
}
false
})
.setOnDismissListener {
dialogLayout.hideKeyboard()
}
.create()
.apply {
setOnShowListener {
getButton(AlertDialog.BUTTON_POSITIVE)
.setOnClickListener {
if (passwordEditText.text.toString().isEmpty()) {
passwordTil.error = activity.getString(R.string.error_empty_field_your_password)
} else {
listener.invoke(passwordEditText.text.toString())
dismiss()
}
}
}
}
.show()
}
}

View file

@ -15,11 +15,8 @@
*/
package im.vector.riotx.features.settings.crosssigning
import android.content.DialogInterface
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.widget.EditText
import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Success
@ -27,12 +24,12 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.riotx.R
import im.vector.riotx.core.dialogs.PromptPasswordDialog
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.toast
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
import javax.inject.Inject
@ -93,36 +90,13 @@ class CrossSigningSettingsFragment @Inject constructor(
}
private fun requestPassword(sessionId: String) {
val inflater = requireActivity().layoutInflater
val layout = inflater.inflate(R.layout.dialog_prompt_password, null)
val passwordEditText = layout.findViewById<EditText>(R.id.prompt_password)
AlertDialog.Builder(requireActivity())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.devices_delete_dialog_title)
.setView(layout)
.setPositiveButton(R.string.auth_submit, DialogInterface.OnClickListener { _, _ ->
if (passwordEditText.toString().isEmpty()) {
requireActivity().toast(R.string.error_empty_field_your_password)
return@OnClickListener
}
val pass = passwordEditText.text.toString()
PromptPasswordDialog().show(requireActivity()) { password ->
// TODO sessionId should never get out the ViewModel
viewModel.handle(CrossSigningAction.InitializeCrossSigning(UserPasswordAuth(
session = sessionId,
password = pass
password = password
)))
})
.setNegativeButton(R.string.cancel, null)
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
dialog.cancel()
return@OnKeyListener true
}
false
})
.show()
}
override fun onInitializeCrossSigningKeys() {

View file

@ -16,9 +16,7 @@
package im.vector.riotx.features.settings.devices
import android.content.DialogInterface
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.widget.EditText
import androidx.appcompat.app.AlertDialog
@ -30,13 +28,13 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.riotx.R
import im.vector.riotx.core.dialogs.PromptPasswordDialog
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.observeEvent
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.utils.toast
import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
@ -167,31 +165,10 @@ class VectorSettingsDevicesFragment @Inject constructor(
if (mAccountPassword.isNotEmpty()) {
viewModel.handle(DevicesAction.Password(mAccountPassword))
} else {
val inflater = requireActivity().layoutInflater
val layout = inflater.inflate(R.layout.dialog_prompt_password, null)
val passwordEditText = layout.findViewById<EditText>(R.id.prompt_password)
AlertDialog.Builder(requireActivity())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.devices_delete_dialog_title)
.setView(layout)
.setPositiveButton(R.string.devices_delete_submit_button_label, DialogInterface.OnClickListener { _, _ ->
if (passwordEditText.toString().isEmpty()) {
requireActivity().toast(R.string.error_empty_field_your_password)
return@OnClickListener
}
mAccountPassword = passwordEditText.text.toString()
PromptPasswordDialog().show(requireActivity()) { password ->
mAccountPassword = password
viewModel.handle(DevicesAction.Password(mAccountPassword))
})
.setNegativeButton(R.string.cancel, null)
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
dialog.cancel()
return@OnKeyListener true
}
false
})
.show()
}
}

View file

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="wrap_content">
@ -14,28 +16,54 @@
android:paddingRight="?dialogPreferredPadding">
<TextView
android:id="@+id/delete_dialog_info_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/devices_delete_dialog_text"
android:textSize="12sp" />
<TextView
android:id="@+id/password_label"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/devices_delete_pswd"
android:textSize="12sp"
android:textStyle="bold" />
android:layout_marginTop="8dp">
<EditText
android:id="@+id/prompt_password"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/promptPasswordTil"
style="@style/VectorTextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/soft_logout_signin_password_hint"
app:errorEnabled="true"
app:errorIconDrawable="@null">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/promptPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword"
android:maxLines="1" />
android:maxLines="1"
android:paddingEnd="48dp"
android:paddingRight="48dp"
tools:ignore="RtlSymmetry"
tools:text="abc" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/promptPasswordPasswordReveal"
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>
</LinearLayout>
</ScrollView>