From ea9a57fabee34072f596a73d4813ced56ebfba85 Mon Sep 17 00:00:00 2001 From: Pratyush Date: Fri, 6 May 2022 22:42:13 +0530 Subject: [PATCH] Improve password UI/UX --- .../app/grapheneos/pdfviewer/PdfViewer.java | 21 ++++++---- .../fragment/PasswordPromptFragment.kt | 41 +++++++++++++++++-- .../pdfviewer/viewModel/PasswordStatus.kt | 27 ++++++++++++ .../res/layout/password_dialog_fragment.xml | 4 +- 4 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/app/grapheneos/pdfviewer/viewModel/PasswordStatus.kt diff --git a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java index f8250c7..e2d9562 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java @@ -34,6 +34,7 @@ import androidx.core.view.ViewCompat; import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; @@ -44,6 +45,7 @@ import app.grapheneos.pdfviewer.fragment.DocumentPropertiesFragment; import app.grapheneos.pdfviewer.fragment.PasswordPromptFragment; import app.grapheneos.pdfviewer.fragment.JumpToPageFragment; import app.grapheneos.pdfviewer.loader.DocumentPropertiesLoader; +import app.grapheneos.pdfviewer.viewModel.PasswordStatus; import java.io.IOException; import java.io.InputStream; @@ -126,6 +128,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader private Toast mToast; private Snackbar snackbar; private PasswordPromptFragment mPasswordPromptFragment; + public PasswordStatus passwordValidationViewModel; private final ActivityResultLauncher openDocumentLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { @@ -190,20 +193,23 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader @JavascriptInterface public void showPasswordPrompt() { - if (getPasswordPromptFragment().isAdded()) { - getPasswordPromptFragment().dismiss(); + if (!getPasswordPromptFragment().isAdded()){ + getPasswordPromptFragment().show(getSupportFragmentManager(), PasswordPromptFragment.class.getName()); } - getPasswordPromptFragment().show(getSupportFragmentManager(), PasswordPromptFragment.class.getName()); + passwordValidationViewModel.passwordMissing(); } @JavascriptInterface public void invalidPassword() { - runOnUiThread(PdfViewer.this::notifyInvalidPassword); - showPasswordPrompt(); + runOnUiThread(() -> passwordValidationViewModel.invalid()); } @JavascriptInterface public void onLoaded() { + passwordValidationViewModel.validated(); + if (getPasswordPromptFragment().isAdded()) { + getPasswordPromptFragment().dismiss(); + } } @JavascriptInterface @@ -212,10 +218,6 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader } } - private void notifyInvalidPassword() { - snackbar.setText(R.string.password_prompt_invalid_password).show(); - } - @Override @SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"}) protected void onCreate(Bundle savedInstanceState) { @@ -223,6 +225,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader binding = PdfviewerBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); + passwordValidationViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())).get(PasswordStatus.class); WindowCompat.setDecorFitsSystemWindows(getWindow(), false); diff --git a/app/src/main/java/app/grapheneos/pdfviewer/fragment/PasswordPromptFragment.kt b/app/src/main/java/app/grapheneos/pdfviewer/fragment/PasswordPromptFragment.kt index 545b5f0..75adbae 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/fragment/PasswordPromptFragment.kt +++ b/app/src/main/java/app/grapheneos/pdfviewer/fragment/PasswordPromptFragment.kt @@ -14,16 +14,21 @@ import androidx.fragment.app.DialogFragment import app.grapheneos.pdfviewer.PdfViewer import app.grapheneos.pdfviewer.R import app.grapheneos.pdfviewer.databinding.PasswordDialogFragmentBinding +import app.grapheneos.pdfviewer.viewModel.PasswordStatus +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout class PasswordPromptFragment : DialogFragment() { + private lateinit var passwordLayout : TextInputLayout private lateinit var passwordEditText : TextInputEditText override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val passwordPrompt = AlertDialog.Builder(requireContext()) + val passwordPrompt = MaterialAlertDialogBuilder(requireContext()) val passwordDialogFragmentBinding = PasswordDialogFragmentBinding.inflate(LayoutInflater.from(requireContext())) + passwordLayout = passwordDialogFragmentBinding.pdfPasswordTextInputLayout passwordEditText = passwordDialogFragmentBinding.pdfPasswordEditText passwordPrompt.setView(passwordDialogFragmentBinding.root) passwordEditText.addTextChangedListener(object : TextWatcher { @@ -38,15 +43,39 @@ class PasswordPromptFragment : DialogFragment() { sendPassword() true } - passwordPrompt.setPositiveButton(R.string.open) { _, _ -> sendPassword() } + passwordPrompt.setPositiveButton(R.string.open, null) passwordPrompt.setNegativeButton(R.string.cancel, null) val dialog = passwordPrompt.create() + passwordPrompt.setCancelable(false) + isCancelable = false dialog.setCanceledOnTouchOutside(false) dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) + (requireActivity() as PdfViewer).passwordValidationViewModel.status.observe( + this + ) { + when (it) { + PasswordStatus.Status.MissingPassword -> { + passwordEditText.editableText.clear() + passwordDialogFragmentBinding.title.setText(R.string.password_prompt_description) + } + PasswordStatus.Status.InvalidPassword -> { + passwordEditText.editableText.clear() + passwordDialogFragmentBinding.pdfPasswordTextInputLayout.error = + "invalid password" + } + PasswordStatus.Status.Validated -> { + //Activity will dismiss the dialog + } + else -> { + throw NullPointerException("status shouldn't be null") + } + } + } return dialog } private fun updatePositiveButton() { + passwordLayout.error = "" val btn = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) btn.isEnabled = passwordEditText.text?.isNotEmpty() ?: false } @@ -55,7 +84,6 @@ class PasswordPromptFragment : DialogFragment() { val password = passwordEditText.text.toString() if (!TextUtils.isEmpty(password)) { (activity as PdfViewer).loadPdfWithPassword(password) - dialog?.dismiss() } } @@ -64,4 +92,11 @@ class PasswordPromptFragment : DialogFragment() { updatePositiveButton() passwordEditText.requestFocus() } + + override fun onResume() { + super.onResume() + (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { + sendPassword() + } + } } diff --git a/app/src/main/java/app/grapheneos/pdfviewer/viewModel/PasswordStatus.kt b/app/src/main/java/app/grapheneos/pdfviewer/viewModel/PasswordStatus.kt new file mode 100644 index 0000000..c45d9d4 --- /dev/null +++ b/app/src/main/java/app/grapheneos/pdfviewer/viewModel/PasswordStatus.kt @@ -0,0 +1,27 @@ +package app.grapheneos.pdfviewer.viewModel + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class PasswordStatus : ViewModel() { + + enum class Status { + MissingPassword, + InvalidPassword, + Validated + } + + val status: MutableLiveData = MutableLiveData(Status.MissingPassword) + + fun passwordMissing() { + status.postValue(Status.MissingPassword) + } + + fun invalid() { + status.postValue(Status.InvalidPassword) + } + + fun validated() { + status.postValue(Status.Validated) + } +} diff --git a/app/src/main/res/layout/password_dialog_fragment.xml b/app/src/main/res/layout/password_dialog_fragment.xml index a2448fd..888ca3f 100644 --- a/app/src/main/res/layout/password_dialog_fragment.xml +++ b/app/src/main/res/layout/password_dialog_fragment.xml @@ -5,7 +5,8 @@ android:layout_height="wrap_content" android:orientation="vertical"> -