Improve password UI/UX

This commit is contained in:
Pratyush 2022-05-06 22:42:13 +05:30 committed by Daniel Micay
parent 68fc48d3a9
commit ea9a57fabe
4 changed files with 80 additions and 13 deletions

View File

@ -34,6 +34,7 @@ import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat; import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader; 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.PasswordPromptFragment;
import app.grapheneos.pdfviewer.fragment.JumpToPageFragment; import app.grapheneos.pdfviewer.fragment.JumpToPageFragment;
import app.grapheneos.pdfviewer.loader.DocumentPropertiesLoader; import app.grapheneos.pdfviewer.loader.DocumentPropertiesLoader;
import app.grapheneos.pdfviewer.viewModel.PasswordStatus;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -126,6 +128,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
private Toast mToast; private Toast mToast;
private Snackbar snackbar; private Snackbar snackbar;
private PasswordPromptFragment mPasswordPromptFragment; private PasswordPromptFragment mPasswordPromptFragment;
public PasswordStatus passwordValidationViewModel;
private final ActivityResultLauncher<Intent> openDocumentLauncher = registerForActivityResult( private final ActivityResultLauncher<Intent> openDocumentLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), result -> { new ActivityResultContracts.StartActivityForResult(), result -> {
@ -190,20 +193,23 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
@JavascriptInterface @JavascriptInterface
public void showPasswordPrompt() { public void showPasswordPrompt() {
if (getPasswordPromptFragment().isAdded()) { if (!getPasswordPromptFragment().isAdded()){
getPasswordPromptFragment().dismiss();
}
getPasswordPromptFragment().show(getSupportFragmentManager(), PasswordPromptFragment.class.getName()); getPasswordPromptFragment().show(getSupportFragmentManager(), PasswordPromptFragment.class.getName());
} }
passwordValidationViewModel.passwordMissing();
}
@JavascriptInterface @JavascriptInterface
public void invalidPassword() { public void invalidPassword() {
runOnUiThread(PdfViewer.this::notifyInvalidPassword); runOnUiThread(() -> passwordValidationViewModel.invalid());
showPasswordPrompt();
} }
@JavascriptInterface @JavascriptInterface
public void onLoaded() { public void onLoaded() {
passwordValidationViewModel.validated();
if (getPasswordPromptFragment().isAdded()) {
getPasswordPromptFragment().dismiss();
}
} }
@JavascriptInterface @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 @Override
@SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"}) @SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"})
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -223,6 +225,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
binding = PdfviewerBinding.inflate(getLayoutInflater()); binding = PdfviewerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
passwordValidationViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())).get(PasswordStatus.class);
WindowCompat.setDecorFitsSystemWindows(getWindow(), false); WindowCompat.setDecorFitsSystemWindows(getWindow(), false);

View File

@ -14,16 +14,21 @@ import androidx.fragment.app.DialogFragment
import app.grapheneos.pdfviewer.PdfViewer import app.grapheneos.pdfviewer.PdfViewer
import app.grapheneos.pdfviewer.R import app.grapheneos.pdfviewer.R
import app.grapheneos.pdfviewer.databinding.PasswordDialogFragmentBinding 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.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
class PasswordPromptFragment : DialogFragment() { class PasswordPromptFragment : DialogFragment() {
private lateinit var passwordLayout : TextInputLayout
private lateinit var passwordEditText : TextInputEditText private lateinit var passwordEditText : TextInputEditText
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val passwordPrompt = AlertDialog.Builder(requireContext()) val passwordPrompt = MaterialAlertDialogBuilder(requireContext())
val passwordDialogFragmentBinding = val passwordDialogFragmentBinding =
PasswordDialogFragmentBinding.inflate(LayoutInflater.from(requireContext())) PasswordDialogFragmentBinding.inflate(LayoutInflater.from(requireContext()))
passwordLayout = passwordDialogFragmentBinding.pdfPasswordTextInputLayout
passwordEditText = passwordDialogFragmentBinding.pdfPasswordEditText passwordEditText = passwordDialogFragmentBinding.pdfPasswordEditText
passwordPrompt.setView(passwordDialogFragmentBinding.root) passwordPrompt.setView(passwordDialogFragmentBinding.root)
passwordEditText.addTextChangedListener(object : TextWatcher { passwordEditText.addTextChangedListener(object : TextWatcher {
@ -38,15 +43,39 @@ class PasswordPromptFragment : DialogFragment() {
sendPassword() sendPassword()
true true
} }
passwordPrompt.setPositiveButton(R.string.open) { _, _ -> sendPassword() } passwordPrompt.setPositiveButton(R.string.open, null)
passwordPrompt.setNegativeButton(R.string.cancel, null) passwordPrompt.setNegativeButton(R.string.cancel, null)
val dialog = passwordPrompt.create() val dialog = passwordPrompt.create()
passwordPrompt.setCancelable(false)
isCancelable = false
dialog.setCanceledOnTouchOutside(false) dialog.setCanceledOnTouchOutside(false)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) 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 return dialog
} }
private fun updatePositiveButton() { private fun updatePositiveButton() {
passwordLayout.error = ""
val btn = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) val btn = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE)
btn.isEnabled = passwordEditText.text?.isNotEmpty() ?: false btn.isEnabled = passwordEditText.text?.isNotEmpty() ?: false
} }
@ -55,7 +84,6 @@ class PasswordPromptFragment : DialogFragment() {
val password = passwordEditText.text.toString() val password = passwordEditText.text.toString()
if (!TextUtils.isEmpty(password)) { if (!TextUtils.isEmpty(password)) {
(activity as PdfViewer).loadPdfWithPassword(password) (activity as PdfViewer).loadPdfWithPassword(password)
dialog?.dismiss()
} }
} }
@ -64,4 +92,11 @@ class PasswordPromptFragment : DialogFragment() {
updatePositiveButton() updatePositiveButton()
passwordEditText.requestFocus() passwordEditText.requestFocus()
} }
override fun onResume() {
super.onResume()
(dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
sendPassword()
}
}
} }

View File

@ -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<Status> = MutableLiveData(Status.MissingPassword)
fun passwordMissing() {
status.postValue(Status.MissingPassword)
}
fun invalid() {
status.postValue(Status.InvalidPassword)
}
fun validated() {
status.postValue(Status.Validated)
}
}

View File

@ -5,7 +5,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<TextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
@ -17,6 +18,7 @@
android:textSize="20sp" /> android:textSize="20sp" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/pdf_password_text_input_layout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox" style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"