From b04a8c91423b356dc950b9118602a21402bef9c5 Mon Sep 17 00:00:00 2001 From: ocean <503259349@qq.com> Date: Sat, 11 Oct 2025 15:15:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A7=A3=E5=AF=86=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E3=80=82=E4=BC=98=E5=8C=96=E7=BB=93=E6=9E=9C=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pro/app/ui/act/PdfPickerActivity.kt | 10 ++- .../pro/app/ui/act/PdfResultActivity.kt | 26 +++++-- .../app/ui/dialog/ListMoreDialogFragment.kt | 5 +- .../app/ui/dialog/PdfRemovePasswordDialog.kt | 27 +++---- .../pro/app/ui/fragment/ToolsFrag.kt | 4 + .../pro/app/util/PdfSecurityUtils.kt | 77 +++++++++++++++++-- 6 files changed, 115 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfPickerActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfPickerActivity.kt index f0c60d9..01c1b6a 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfPickerActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfPickerActivity.kt @@ -15,6 +15,7 @@ import com.all.pdfreader.pro.app.model.SortConfig import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.room.repository.PdfRepository import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter +import com.all.pdfreader.pro.app.ui.dialog.PdfRemovePasswordDialog import com.all.pdfreader.pro.app.ui.dialog.PdfSetPasswordDialog import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation import com.all.pdfreader.pro.app.util.AppUtils.setOnSingleClickListener @@ -93,7 +94,14 @@ class PdfPickerActivity : BaseActivity() { } PdfPickerSource.UNLOCK -> { - + PdfRemovePasswordDialog(pdf.filePath, onOkClick = { password -> + val intent = PdfResultActivity.createIntentLock( + this, pdf.filePath, password, + PdfPickerSource.UNLOCK + ) + startActivity(intent) + finish() + }).show(supportFragmentManager, "PdfRemovePasswordDialog") } PdfPickerSource.TO_IMAGES -> {} diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfResultActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfResultActivity.kt index 5cf9c8d..ff7963f 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfResultActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfResultActivity.kt @@ -15,10 +15,10 @@ import com.all.pdfreader.pro.app.databinding.ActivityPdfSplitResultBinding import com.all.pdfreader.pro.app.model.PdfPickerSource import com.all.pdfreader.pro.app.model.PdfSelectedPagesItem import com.all.pdfreader.pro.app.model.PdfSplitResultItem -import com.all.pdfreader.pro.app.room.repository.PdfRepository import com.all.pdfreader.pro.app.ui.adapter.PdfResultAdapter import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment import com.all.pdfreader.pro.app.util.AppUtils +import com.all.pdfreader.pro.app.util.AppUtils.generateFastThumbnail import com.all.pdfreader.pro.app.util.FileUtils.toUnderscoreDateTime import com.all.pdfreader.pro.app.util.PdfScanner import com.all.pdfreader.pro.app.util.PdfSecurityUtils @@ -111,7 +111,7 @@ class PdfResultActivity : BaseActivity() { finish() return } - } else if (source == PdfPickerSource.LOCK) { + } else if (source == PdfPickerSource.LOCK || source == PdfPickerSource.UNLOCK) { if (filepath.isEmpty() || password.isEmpty()) { showToast(getString(R.string.pdf_loading_failed)) finish() @@ -166,8 +166,7 @@ class PdfResultActivity : BaseActivity() { binding.progressBar.progress = percent } })?.let { resultFile -> - val thumbnails = - AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile) + val thumbnails = generateFastThumbnail(this@PdfResultActivity, resultFile) val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) { resultList.add(result) @@ -198,8 +197,7 @@ class PdfResultActivity : BaseActivity() { binding.progressTv.text = "$progressPercent" } })?.let { resultFile -> - val thumbnails = - AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile) + val thumbnails = generateFastThumbnail(this@PdfResultActivity, resultFile) val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) { resultList.add(result) @@ -214,11 +212,23 @@ class PdfResultActivity : BaseActivity() { binding.progressTv.text = "$progress" }.let { it -> if (it) { - PdfRepository.getInstance().updateIsPassword(filepath, true) + pdfRepository.updateIsPassword(filepath, true) } val result = PdfSplitResultItem(filePath = filepath, isPassword = true) resultList.add(result) } + } else if (source == PdfPickerSource.UNLOCK) { + binding.congratulationsDesc.text = getString(R.string.remove_password_successfully) + PdfSecurityUtils.removePasswordFromPdfWithProgress(filepath, password) { progress -> + binding.progressTv.text = "$progress" + }?.let { it -> + val result = PdfSplitResultItem( + filePath = filepath, + thumbnailPath = it, + isPassword = false + ) + resultList.add(result) + } } withContext(Dispatchers.Main) { binding.processingLayout.visibility = View.GONE @@ -227,7 +237,7 @@ class PdfResultActivity : BaseActivity() { resultList[0].isSelected = true } adapter.updateAdapter() - isProcessing = false//拆分结束 + isProcessing = false exitDialog?.dismissAllowingStateLoss() exitDialog = null } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/ListMoreDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/ListMoreDialogFragment.kt index b247b3a..ae06bba 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/ListMoreDialogFragment.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/ListMoreDialogFragment.kt @@ -2,7 +2,6 @@ package com.all.pdfreader.pro.app.ui.dialog import android.net.Uri import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -195,7 +194,9 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment() } binding.setPasswordBtn.setOnClickListener { if (pdfDocument.isPassword) { - PdfRemovePasswordDialog().show(parentFragmentManager, "PdfRemovePasswordDialog") + PdfRemovePasswordDialog(pdfDocument.filePath, onOkClick = { password -> + viewModel.removePassword(pdfDocument.filePath, password) + }).show(parentFragmentManager, "PdfRemovePasswordDialog") } else { PdfSetPasswordDialog(onOkClick = { password -> viewModel.setPassword(pdfDocument.filePath, password) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfRemovePasswordDialog.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfRemovePasswordDialog.kt index 359483f..6b4f260 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfRemovePasswordDialog.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PdfRemovePasswordDialog.kt @@ -7,22 +7,19 @@ import android.view.View import android.view.ViewGroup import androidx.core.graphics.drawable.toDrawable import androidx.fragment.app.DialogFragment -import androidx.fragment.app.activityViewModels import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.databinding.DialogPdfRemovePasswordBinding -import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard import com.all.pdfreader.pro.app.util.FileUtils.isPdfPasswordCorrect import com.all.pdfreader.pro.app.util.ToastUtils -import com.all.pdfreader.pro.app.viewmodel.PdfViewModel import java.io.File -class PdfRemovePasswordDialog() : DialogFragment( - -) { +class PdfRemovePasswordDialog( + val filePath: String, + val onOkClick: (password: String) -> Unit = {} +) : + DialogFragment() { private lateinit var binding: DialogPdfRemovePasswordBinding - private val viewModel: PdfViewModel by activityViewModels() - private lateinit var pdfDocument: PdfDocumentEntity override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -46,14 +43,8 @@ class PdfRemovePasswordDialog() : DialogFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.pdfDocument.value?.let { - pdfDocument = it - binding.etPassword.showKeyboard() - setupListeners() - } ?: run { - showToast(getString(R.string.file_not)) - dismiss() - } + binding.etPassword.showKeyboard() + setupListeners() } private fun setupListeners() { @@ -63,8 +54,8 @@ class PdfRemovePasswordDialog() : DialogFragment( binding.tvConfirm.setOnClickListener { val password = binding.etPassword.text.toString() - if (isPdfPasswordCorrect(requireActivity(), File(pdfDocument.filePath), password)) { - viewModel.removePassword(pdfDocument.filePath, password) + if (isPdfPasswordCorrect(requireActivity(), File(filePath), password)) { + onOkClick(password) dismiss() } else { binding.tilPassword.error = getString(R.string.incorrect_password) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/ToolsFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/ToolsFrag.kt index 200f792..36bb404 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/ToolsFrag.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/ToolsFrag.kt @@ -37,5 +37,9 @@ class ToolsFrag : Fragment() { val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.LOCK) startActivity(intent) } + binding.unLockBtn.setOnClickListener { + val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.UNLOCK) + startActivity(intent) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfSecurityUtils.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfSecurityUtils.kt index a1667d8..c6d23ad 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfSecurityUtils.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfSecurityUtils.kt @@ -1,6 +1,9 @@ package com.all.pdfreader.pro.app.util import android.graphics.pdf.PdfDocument +import com.all.pdfreader.pro.app.PRApp +import com.all.pdfreader.pro.app.room.repository.PdfRepository +import com.all.pdfreader.pro.app.util.AppUtils.generateFastThumbnail import com.tom_roush.pdfbox.pdmodel.PDDocument import com.tom_roush.pdfbox.pdmodel.encryption.AccessPermission import com.tom_roush.pdfbox.pdmodel.encryption.StandardProtectionPolicy @@ -38,14 +41,13 @@ object PdfSecurityUtils { userPassword: String, ownerPassword: String, onProgress: (Int) -> Unit - ): Boolean = withContext(Dispatchers.IO) { + ): Boolean = withContext(Dispatchers.IO) { var success = false // 假进度变量 var progress = 0 val job = launch { while (isActive && progress < 98) { delay(50) - // 专业曲线:前 80%慢,后 20%快 progress += when { progress < 80 -> 1 // 前 80% 每次 +1 progress < 90 -> 2 // 80~90% 每次 +2 @@ -63,9 +65,17 @@ object PdfSecurityUtils { // 停止假进度 job.cancel() - // 最终完成直接 100% - withContext(Dispatchers.Main) { - onProgress(100) + // 最后 98 -> 100 有一个 1 秒左右的平滑视觉过渡 + val finalStart = progress + val finalEnd = 100 + val steps = (finalEnd - finalStart).coerceAtLeast(2) // 至少两步 + val delayPerStep = 1000L / steps + + for (p in finalStart..finalEnd) { + withContext(Dispatchers.Main) { + onProgress(p) + } + delay(delayPerStep) } success } @@ -84,4 +94,61 @@ object PdfSecurityUtils { } } + suspend fun removePasswordFromPdfWithProgress( + inputFilePath: String, + password: String, + onProgress: (Int) -> Unit + ): String? = withContext(Dispatchers.IO) { + var newThumbnail: String? = null + var success = false + var progress = 0 + + // 启动假进度 + val job = launch { + while (isActive && progress < 98) { + delay(50) + progress += when { + progress < 80 -> 1 // 前 80% 每次 +1 + progress < 90 -> 2 // 80~90% 每次 +2 + else -> 3 // 90~98% 每次 +3 + } + if (progress > 98) progress = 98 + withContext(Dispatchers.Main) { + onProgress(progress) + } + } + } + + // 执行移除密码操作 + success = removePasswordFromPdf(inputFilePath, password) + if (success) { + val pdfRepository = PdfRepository.getInstance() + pdfRepository.updateIsPassword(inputFilePath, false) + newThumbnail = generateFastThumbnail(PRApp.getContext(), File(inputFilePath)) + val document = pdfRepository.getDocumentByPath(inputFilePath) + if (document?.thumbnailPath.isNullOrEmpty()) { + if (!newThumbnail.isNullOrEmpty() && inputFilePath != newThumbnail) { + pdfRepository.updateThumbnailPath(inputFilePath, newThumbnail) + } + } + } + // 停止假进度 + job.cancel() + + // 最后 98 -> 100 平滑过渡 1 秒 + val finalStart = progress + val finalEnd = 100 + val steps = (finalEnd - finalStart).coerceAtLeast(2) + val delayPerStep = 1000L / steps + + for (p in finalStart..finalEnd) { + withContext(Dispatchers.Main) { + onProgress(p) + } + delay(delayPerStep) + } + + newThumbnail + } + }