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

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.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 -> {}

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.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
}

View File

@ -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)

View File

@ -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)

View File

@ -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)
}
}
}

View File

@ -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
}
}