From 783a5144968ae58ff82c3755a488743ad7bf746f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 3 Feb 2020 14:48:15 +0100 Subject: [PATCH] Improve prompt password dialog Reveal password, inline error when empty --- .../riotx/core/dialogs/ExportKeysDialog.kt | 2 +- .../core/dialogs/PromptPasswordDialog.kt | 87 +++++++++++++++++++ .../CrossSigningSettingsFragment.kt | 42 ++------- .../devices/VectorSettingsDevicesFragment.kt | 33 ++----- .../res/layout/dialog_prompt_password.xml | 56 +++++++++--- 5 files changed, 143 insertions(+), 77 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/dialogs/PromptPasswordDialog.kt diff --git a/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt b/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt index 1cb6c5406a..7497caeeb9 100644 --- a/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt +++ b/vector/src/main/java/im/vector/riotx/core/dialogs/ExportKeysDialog.kt @@ -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) diff --git a/vector/src/main/java/im/vector/riotx/core/dialogs/PromptPasswordDialog.kt b/vector/src/main/java/im/vector/riotx/core/dialogs/PromptPasswordDialog.kt new file mode 100644 index 0000000000..a05c9dec05 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/dialogs/PromptPasswordDialog.kt @@ -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(R.id.promptPasswordTil) + val passwordEditText = dialogLayout.findViewById(R.id.promptPassword) + val textWatcher = object : SimpleTextWatcher() { + override fun afterTextChanged(s: Editable) { + passwordTil.error = null + } + } + passwordEditText.addTextChangedListener(textWatcher) + + val showPassword = dialogLayout.findViewById(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() + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsFragment.kt index 97e9d6d923..fef10019a5 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsFragment.kt @@ -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(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() - - // TODO sessionId should never get out the ViewModel - viewModel.handle(CrossSigningAction.InitializeCrossSigning(UserPasswordAuth( - session = sessionId, - password = pass - ))) - }) - .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() + PromptPasswordDialog().show(requireActivity()) { password -> + // TODO sessionId should never get out the ViewModel + viewModel.handle(CrossSigningAction.InitializeCrossSigning(UserPasswordAuth( + session = sessionId, + password = password + ))) + } } override fun onInitializeCrossSigningKeys() { diff --git a/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt index c3d389f43a..a1cc93d0b0 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/devices/VectorSettingsDevicesFragment.kt @@ -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(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() - 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() + PromptPasswordDialog().show(requireActivity()) { password -> + mAccountPassword = password + viewModel.handle(DevicesAction.Password(mAccountPassword)) + } } } diff --git a/vector/src/main/res/layout/dialog_prompt_password.xml b/vector/src/main/res/layout/dialog_prompt_password.xml index 6315f20373..1382caa729 100644 --- a/vector/src/main/res/layout/dialog_prompt_password.xml +++ b/vector/src/main/res/layout/dialog_prompt_password.xml @@ -1,5 +1,7 @@ @@ -14,28 +16,54 @@ android:paddingRight="?dialogPreferredPadding"> - + android:layout_marginTop="8dp"> + + + + + + + + + + - + \ No newline at end of file