diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 08059ab..bde0fcf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + xmlns:tools="http://schemas.android.com/tools" + android:theme="@style/Theme.PDFReaderPro" + tools:ignore="DiscouragedApi,LockedOrientationActivity,RedundantLabel"> @@ -32,6 +34,7 @@ android:name=".ui.act.SplashActivity" android:exported="true" android:label="@string/app_name" + android:screenOrientation="portrait" android:theme="@style/Theme.PDFReaderPro"> @@ -43,12 +46,13 @@ android:name=".ui.act.MainActivity" android:exported="true" android:label="@string/app_name" - android:theme="@style/Theme.PDFReaderPro" /> + android:screenOrientation="portrait" /> diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt index d23e2b3..c62ad0f 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt @@ -2,6 +2,7 @@ package com.all.pdfreader.pro.app.ui.adapter import android.annotation.SuppressLint import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.all.pdfreader.pro.app.databinding.AdapterPdfItemBinding @@ -34,9 +35,16 @@ class PdfAdapter( holder.binding.tvFileName.text = item.fileName holder.binding.tvFileSize.text = item.fileSize.toFormatFileSize() holder.binding.tvFileDate.text = item.lastModified.toSlashDate() - Glide.with(holder.binding.root).load(item.thumbnailPath) - .transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context))) - .into(holder.binding.tvFileImg) + if (item.isPassword) { + holder.binding.lockLayout.visibility = View.VISIBLE + holder.binding.tvFileImg.visibility = View.GONE + } else { + holder.binding.lockLayout.visibility = View.GONE + holder.binding.tvFileImg.visibility = View.VISIBLE + Glide.with(holder.binding.root).load(item.thumbnailPath) + .transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context))) + .into(holder.binding.tvFileImg) + } holder.binding.root.setOnClickListener { onItemClick(item) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfPasswordProtectionDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfPasswordProtectionDialogFragment.kt index 50fbf4f..8b87451 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfPasswordProtectionDialogFragment.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfPasswordProtectionDialogFragment.kt @@ -1,28 +1,15 @@ package com.all.pdfreader.pro.app.ui.dialog -import android.content.Context import android.graphics.Color -import android.graphics.drawable.ColorDrawable import android.os.Bundle +import android.text.InputType import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowManager -import android.view.inputmethod.InputMethodManager +import androidx.core.graphics.drawable.toDrawable import androidx.fragment.app.DialogFragment import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.databinding.DialogPdfPasswordProtectionBinding -import com.all.pdfreader.pro.app.databinding.DialogPermissionBinding -import com.all.pdfreader.pro.app.databinding.DialogSortBinding -import com.all.pdfreader.pro.app.model.SortConfig -import com.all.pdfreader.pro.app.model.SortDirection -import com.all.pdfreader.pro.app.model.SortField -import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity -import com.all.pdfreader.pro.app.sp.AppStore -import com.all.pdfreader.pro.app.ui.act.MainActivity.SortableFragment -import com.all.pdfreader.pro.app.ui.view.CustomPasswordTransformation -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import androidx.core.graphics.drawable.toDrawable import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard import com.all.pdfreader.pro.app.util.FileUtils.isPdfPasswordCorrect import java.io.File @@ -34,6 +21,7 @@ class PdfPasswordProtectionDialogFragment( ) : DialogFragment() { private lateinit var binding: DialogPdfPasswordProtectionBinding + var isPasswordVisible = false override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -61,22 +49,41 @@ class PdfPasswordProtectionDialogFragment( binding.etPassword.showKeyboard() + binding.tvCancel.setOnClickListener { onCancelClick() dismiss() } binding.tvConfirm.setOnClickListener { val password = binding.etPassword.text.toString() - if(password.isEmpty()){ + if (password.isEmpty()) { binding.tilPassword.error = getString(R.string.password_not_empty) return@setOnClickListener } - if(isPdfPasswordCorrect(requireActivity(),file,password)){ + if (isPdfPasswordCorrect(requireActivity(), file, password)) { onOkClick(password) dismiss() - }else{ + } else { binding.tilPassword.error = getString(R.string.incorrect_password) } } + binding.showPasswordBtn.setOnClickListener { + isPasswordVisible = !isPasswordVisible + showOrHidePassword(isPasswordVisible) + } + } + + private fun showOrHidePassword(b: Boolean) { + if (b) { + binding.etPassword.inputType = + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD + binding.showPasswordIv.setImageResource(R.drawable.show_password) + } else { + binding.etPassword.inputType = + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + binding.showPasswordIv.setImageResource(R.drawable.hide_password) + } + // 保持光标在末尾 + binding.etPassword.setSelection(binding.etPassword.text?.length ?: 0) } } \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfSetPasswordDialog.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfSetPasswordDialog.kt index c65e88d..4082ce4 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfSetPasswordDialog.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfSetPasswordDialog.kt @@ -1,63 +1,88 @@ package com.all.pdfreader.pro.app.ui.dialog -import android.content.Context +import android.graphics.Color import android.os.Bundle import android.text.Editable +import android.text.InputType import android.text.TextWatcher import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView +import androidx.core.graphics.drawable.toDrawable import androidx.fragment.app.DialogFragment +import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.databinding.DialogPdfSetPasswordBinding +import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard +import com.google.android.material.textfield.TextInputEditText -class PdfSetPasswordDialog : DialogFragment() { +class PdfSetPasswordDialog( + private val onCancelled: () -> Unit, private val onPasswordSet: (String) -> Unit +) : DialogFragment( - private var _binding: DialogPdfSetPasswordBinding? = null - private val binding get() = _binding!! - private var listener: PasswordDialogListener? = null - - interface PasswordDialogListener { - fun onPasswordSet(password: String?) - fun onPasswordDialogCancelled() - } - - companion object { - fun newInstance(): PdfSetPasswordDialog { - return PdfSetPasswordDialog() - } - } +) { + private lateinit var binding: DialogPdfSetPasswordBinding + private var isEnterPasswordVisible = false + private var isConfirmPasswordVisible = false override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - _binding = DialogPdfSetPasswordBinding.inflate(inflater, container, false) + binding = DialogPdfSetPasswordBinding.inflate(layoutInflater) return binding.root } + override fun onStart() { + super.onStart() + isCancelable = false + dialog?.window?.apply { + // 去掉系统默认的背景 padding + setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + // 设置宽度为全屏减去 16dp + val margin = resources.getDimensionPixelSize(R.dimen.dialog_margin) // 16dp + val width = resources.displayMetrics.widthPixels - margin * 2 + setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + binding.etPassword.showKeyboard() setupListeners() setupTextWatchers() } private fun setupListeners() { binding.tvCancel.setOnClickListener { - listener?.onPasswordDialogCancelled() + onCancelled() dismiss() } binding.tvConfirm.setOnClickListener { val password = binding.etPassword.text.toString() val confirmPassword = binding.etConfirmPassword.text.toString() - if (validatePassword(password, confirmPassword)) { - listener?.onPasswordSet(password) + onPasswordSet(password) dismiss() } } + + binding.enterShowPasswordBtn.setOnClickListener { + isEnterPasswordVisible = !isEnterPasswordVisible + showOrHidePassword( + binding.etPassword, + binding.enterShowPasswordIv, + isEnterPasswordVisible + ) + } + binding.confirmShowPasswordBtn.setOnClickListener { + isConfirmPasswordVisible = !isConfirmPasswordVisible + showOrHidePassword( + binding.etConfirmPassword, + binding.confirmShowPasswordIv, + isConfirmPasswordVisible + ) + } } private fun setupTextWatchers() { @@ -86,7 +111,7 @@ class PdfSetPasswordDialog : DialogFragment() { binding.tilConfirmPassword.error = null if (password.isNotEmpty() && confirmPassword.isNotEmpty() && password != confirmPassword) { - binding.tilConfirmPassword.error = "密码不匹配" + binding.tilConfirmPassword.error = getString(R.string.password_not_match) } } @@ -94,42 +119,38 @@ class PdfSetPasswordDialog : DialogFragment() { binding.tilPassword.error = null binding.tilConfirmPassword.error = null - // 允许空密码(表示不设置密码) + // 不允许空密码 if (password.isEmpty()) { - return true + binding.tilPassword.error = getString(R.string.password_not_empty) + return false } // 密码长度验证 if (password.length < 4) { - binding.tilPassword.error = "密码长度至少为4位" + binding.tilPassword.error = getString(R.string.password_too_short) return false } // 密码匹配验证 if (password != confirmPassword) { - binding.tilConfirmPassword.error = "密码不匹配" + binding.tilConfirmPassword.error = getString(R.string.password_not_match) return false } return true } - override fun onAttach(context: Context) { - super.onAttach(context) - if (context is PasswordDialogListener) { - listener = context + private fun showOrHidePassword(inputView: TextInputEditText, imageView: ImageView, b: Boolean) { + if (b) { + inputView.inputType = + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD + imageView.setImageResource(R.drawable.show_password) } else { - throw RuntimeException("$context must implement PasswordDialogListener") + inputView.inputType = + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + imageView.setImageResource(R.drawable.hide_password) } - } - - override fun onDetach() { - super.onDetach() - listener = null - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null + // 保持光标在末尾 + inputView.setSelection(inputView.text?.length ?: 0) } } \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt index 1001a40..5fe2256 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -40,9 +39,8 @@ class HomeFrag : BaseFrag(), MainActivity.SortableFragment { adapter = PdfAdapter(pdfList = mutableListOf(), onItemClick = { pdf -> val intent = PdfViewActivity.createIntent(requireContext(), pdf.filePath) startActivity(intent) - }, onMoreClick = { pdf -> - Toast.makeText(requireContext(), "更多操作: ${pdf.fileName}", Toast.LENGTH_SHORT).show() + }) binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/view/CustomPasswordTransformation.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/view/CustomPasswordTransformation.kt deleted file mode 100644 index ad12944..0000000 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/view/CustomPasswordTransformation.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.all.pdfreader.pro.app.ui.view - -import android.text.method.PasswordTransformationMethod -import android.view.View - -class CustomPasswordTransformation : PasswordTransformationMethod() { - override fun getTransformation(source: CharSequence, view: View): CharSequence { - return PasswordCharSequence(source) - } - - private class PasswordCharSequence(private val mSource: CharSequence) : CharSequence { - override val length: Int - get() = mSource.length - - override fun get(index: Int): Char { - // 最后一位显示原字符,其余显示● - return if (index == mSource.length - 1) mSource[index] else '●' - } - - override fun subSequence(startIndex: Int, endIndex: Int): CharSequence { - return mSource.subSequence(startIndex, endIndex) - } - } -} diff --git a/app/src/main/res/drawable/dr_item_lock_bg.xml b/app/src/main/res/drawable/dr_item_lock_bg.xml new file mode 100644 index 0000000..e36a68c --- /dev/null +++ b/app/src/main/res/drawable/dr_item_lock_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/hide_password.xml b/app/src/main/res/drawable/hide_password.xml new file mode 100644 index 0000000..978912e --- /dev/null +++ b/app/src/main/res/drawable/hide_password.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/lock.xml b/app/src/main/res/drawable/lock.xml new file mode 100644 index 0000000..3c17f6f --- /dev/null +++ b/app/src/main/res/drawable/lock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/show_password.xml b/app/src/main/res/drawable/show_password.xml new file mode 100644 index 0000000..eb91df0 --- /dev/null +++ b/app/src/main/res/drawable/show_password.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/unlock.xml b/app/src/main/res/drawable/unlock.xml new file mode 100644 index 0000000..fb37054 --- /dev/null +++ b/app/src/main/res/drawable/unlock.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/adapter_pdf_item.xml b/app/src/main/res/layout/adapter_pdf_item.xml index dc2d91a..e26fb08 100644 --- a/app/src/main/res/layout/adapter_pdf_item.xml +++ b/app/src/main/res/layout/adapter_pdf_item.xml @@ -25,6 +25,20 @@ android:layout_height="56dp" android:src="@mipmap/ic_launcher_round" /> + + + + + + android:textSize="16sp" /> @@ -67,8 +81,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" - android:text="8.1MB" android:fontFamily="@font/poppins_medium" + android:text="8.1MB" android:textColor="@color/black_60" android:textSize="14sp" /> diff --git a/app/src/main/res/layout/dialog_pdf_password_protection.xml b/app/src/main/res/layout/dialog_pdf_password_protection.xml index 221f2df..60f710e 100644 --- a/app/src/main/res/layout/dialog_pdf_password_protection.xml +++ b/app/src/main/res/layout/dialog_pdf_password_protection.xml @@ -27,24 +27,46 @@ android:textColor="@color/black_80" android:textSize="14sp" /> - + android:layout_marginBottom="8dp"> - + android:hint="@string/enter_password" + app:endIconMode="password_toggle" + app:passwordToggleEnabled="true"> + + + + + + + + + + + - - + android:layout_marginBottom="8dp"> - + android:hint="@string/enter_password" + app:endIconMode="password_toggle" + app:passwordToggleEnabled="true"> - + - + + + + + + + + + + + android:layout_marginBottom="24dp"> - + android:hint="@string/confirm_password" + app:endIconMode="password_toggle" + app:passwordToggleEnabled="true"> - + + + + + + + + + + + + android:textColor="@color/black_80" + android:textSize="16sp" /> + android:textColor="@color/black" + android:textSize="16sp" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 63f9c1c..ddc10e8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -29,7 +29,7 @@ Eye Protect Bookmarks Set PDF Password - Set a protection password for the PDF file,Leaving it blank means no password is set. + Set a protection password for the PDF file, Leaving it blank means no password is set. PDF Password Protection Enter password Confirm password @@ -37,4 +37,6 @@ Password cannot be empty Incorrect password PDF loading failed + Password must be at least 4 characters + Passwords do not match \ No newline at end of file