添加解密工具。优化结果页面。

This commit is contained in:
ocean 2025-10-11 15:15:43 +08:00
parent 43ece1be95
commit b04a8c9142
6 changed files with 115 additions and 34 deletions

View File

@ -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.entity.PdfDocumentEntity
import com.all.pdfreader.pro.app.room.repository.PdfRepository 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.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.ui.dialog.PdfSetPasswordDialog
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
import com.all.pdfreader.pro.app.util.AppUtils.setOnSingleClickListener import com.all.pdfreader.pro.app.util.AppUtils.setOnSingleClickListener
@ -93,7 +94,14 @@ class PdfPickerActivity : BaseActivity() {
} }
PdfPickerSource.UNLOCK -> { 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 -> {} PdfPickerSource.TO_IMAGES -> {}

View File

@ -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.PdfPickerSource
import com.all.pdfreader.pro.app.model.PdfSelectedPagesItem import com.all.pdfreader.pro.app.model.PdfSelectedPagesItem
import com.all.pdfreader.pro.app.model.PdfSplitResultItem 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.adapter.PdfResultAdapter
import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment 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
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.FileUtils.toUnderscoreDateTime
import com.all.pdfreader.pro.app.util.PdfScanner import com.all.pdfreader.pro.app.util.PdfScanner
import com.all.pdfreader.pro.app.util.PdfSecurityUtils import com.all.pdfreader.pro.app.util.PdfSecurityUtils
@ -111,7 +111,7 @@ class PdfResultActivity : BaseActivity() {
finish() finish()
return return
} }
} else if (source == PdfPickerSource.LOCK) { } else if (source == PdfPickerSource.LOCK || source == PdfPickerSource.UNLOCK) {
if (filepath.isEmpty() || password.isEmpty()) { if (filepath.isEmpty() || password.isEmpty()) {
showToast(getString(R.string.pdf_loading_failed)) showToast(getString(R.string.pdf_loading_failed))
finish() finish()
@ -166,8 +166,7 @@ class PdfResultActivity : BaseActivity() {
binding.progressBar.progress = percent binding.progressBar.progress = percent
} }
})?.let { resultFile -> })?.let { resultFile ->
val thumbnails = val thumbnails = generateFastThumbnail(this@PdfResultActivity, resultFile)
AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile)
val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false)
pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) { pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) {
resultList.add(result) resultList.add(result)
@ -198,8 +197,7 @@ class PdfResultActivity : BaseActivity() {
binding.progressTv.text = "$progressPercent" binding.progressTv.text = "$progressPercent"
} }
})?.let { resultFile -> })?.let { resultFile ->
val thumbnails = val thumbnails = generateFastThumbnail(this@PdfResultActivity, resultFile)
AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile)
val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false)
pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) { pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) {
resultList.add(result) resultList.add(result)
@ -214,11 +212,23 @@ class PdfResultActivity : BaseActivity() {
binding.progressTv.text = "$progress" binding.progressTv.text = "$progress"
}.let { it -> }.let { it ->
if (it) { if (it) {
PdfRepository.getInstance().updateIsPassword(filepath, true) pdfRepository.updateIsPassword(filepath, true)
} }
val result = PdfSplitResultItem(filePath = filepath, isPassword = true) val result = PdfSplitResultItem(filePath = filepath, isPassword = true)
resultList.add(result) 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) { withContext(Dispatchers.Main) {
binding.processingLayout.visibility = View.GONE binding.processingLayout.visibility = View.GONE
@ -227,7 +237,7 @@ class PdfResultActivity : BaseActivity() {
resultList[0].isSelected = true resultList[0].isSelected = true
} }
adapter.updateAdapter() adapter.updateAdapter()
isProcessing = false//拆分结束 isProcessing = false
exitDialog?.dismissAllowingStateLoss() exitDialog?.dismissAllowingStateLoss()
exitDialog = null exitDialog = null
} }

View File

@ -2,7 +2,6 @@ package com.all.pdfreader.pro.app.ui.dialog
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -195,7 +194,9 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment()
} }
binding.setPasswordBtn.setOnClickListener { binding.setPasswordBtn.setOnClickListener {
if (pdfDocument.isPassword) { if (pdfDocument.isPassword) {
PdfRemovePasswordDialog().show(parentFragmentManager, "PdfRemovePasswordDialog") PdfRemovePasswordDialog(pdfDocument.filePath, onOkClick = { password ->
viewModel.removePassword(pdfDocument.filePath, password)
}).show(parentFragmentManager, "PdfRemovePasswordDialog")
} else { } else {
PdfSetPasswordDialog(onOkClick = { password -> PdfSetPasswordDialog(onOkClick = { password ->
viewModel.setPassword(pdfDocument.filePath, password) viewModel.setPassword(pdfDocument.filePath, password)

View File

@ -7,22 +7,19 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.DialogPdfRemovePasswordBinding 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.AppUtils.showKeyboard
import com.all.pdfreader.pro.app.util.FileUtils.isPdfPasswordCorrect import com.all.pdfreader.pro.app.util.FileUtils.isPdfPasswordCorrect
import com.all.pdfreader.pro.app.util.ToastUtils import com.all.pdfreader.pro.app.util.ToastUtils
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
import java.io.File import java.io.File
class PdfRemovePasswordDialog() : DialogFragment( class PdfRemovePasswordDialog(
val filePath: String,
) { val onOkClick: (password: String) -> Unit = {}
) :
DialogFragment() {
private lateinit var binding: DialogPdfRemovePasswordBinding private lateinit var binding: DialogPdfRemovePasswordBinding
private val viewModel: PdfViewModel by activityViewModels()
private lateinit var pdfDocument: PdfDocumentEntity
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
@ -46,14 +43,8 @@ class PdfRemovePasswordDialog() : DialogFragment(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewModel.pdfDocument.value?.let {
pdfDocument = it
binding.etPassword.showKeyboard() binding.etPassword.showKeyboard()
setupListeners() setupListeners()
} ?: run {
showToast(getString(R.string.file_not))
dismiss()
}
} }
private fun setupListeners() { private fun setupListeners() {
@ -63,8 +54,8 @@ class PdfRemovePasswordDialog() : DialogFragment(
binding.tvConfirm.setOnClickListener { binding.tvConfirm.setOnClickListener {
val password = binding.etPassword.text.toString() val password = binding.etPassword.text.toString()
if (isPdfPasswordCorrect(requireActivity(), File(pdfDocument.filePath), password)) { if (isPdfPasswordCorrect(requireActivity(), File(filePath), password)) {
viewModel.removePassword(pdfDocument.filePath, password) onOkClick(password)
dismiss() dismiss()
} else { } else {
binding.tilPassword.error = getString(R.string.incorrect_password) binding.tilPassword.error = getString(R.string.incorrect_password)

View File

@ -37,5 +37,9 @@ class ToolsFrag : Fragment() {
val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.LOCK) val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.LOCK)
startActivity(intent) startActivity(intent)
} }
binding.unLockBtn.setOnClickListener {
val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.UNLOCK)
startActivity(intent)
}
} }
} }

View File

@ -1,6 +1,9 @@
package com.all.pdfreader.pro.app.util package com.all.pdfreader.pro.app.util
import android.graphics.pdf.PdfDocument 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.PDDocument
import com.tom_roush.pdfbox.pdmodel.encryption.AccessPermission import com.tom_roush.pdfbox.pdmodel.encryption.AccessPermission
import com.tom_roush.pdfbox.pdmodel.encryption.StandardProtectionPolicy import com.tom_roush.pdfbox.pdmodel.encryption.StandardProtectionPolicy
@ -45,7 +48,6 @@ object PdfSecurityUtils {
val job = launch { val job = launch {
while (isActive && progress < 98) { while (isActive && progress < 98) {
delay(50) delay(50)
// 专业曲线:前 80%慢,后 20%快
progress += when { progress += when {
progress < 80 -> 1 // 前 80% 每次 +1 progress < 80 -> 1 // 前 80% 每次 +1
progress < 90 -> 2 // 80~90% 每次 +2 progress < 90 -> 2 // 80~90% 每次 +2
@ -63,9 +65,17 @@ object PdfSecurityUtils {
// 停止假进度 // 停止假进度
job.cancel() job.cancel()
// 最终完成直接 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) { withContext(Dispatchers.Main) {
onProgress(100) onProgress(p)
}
delay(delayPerStep)
} }
success 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
}
} }