From 5bf2952c03a5edcdd492f8025ea1b55e39bb44c7 Mon Sep 17 00:00:00 2001 From: ocean <503259349@qq.com> Date: Fri, 10 Oct 2025 18:47:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=90=9C=E7=B4=A2=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=B7=A5=E5=85=B7=E5=8A=A0?= =?UTF-8?q?=E5=AF=86=E7=AD=89=E6=93=8D=E4=BD=9C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../pro/app/model/PdfPickerSource.kt | 3 +- .../pro/app/model/PdfSplitResultItem.kt | 3 +- .../pro/app/ui/act/MergePdfActivity.kt | 2 +- .../pro/app/ui/act/PdfPickerActivity.kt | 35 ++++++++-- ...ResultActivity.kt => PdfResultActivity.kt} | 68 +++++++++++++++---- .../pro/app/ui/act/SearchActivity.kt | 6 +- .../pro/app/ui/act/SplitPdfActivity.kt | 4 +- ...dfResultAdapter.kt => PdfResultAdapter.kt} | 15 ++-- .../pro/app/ui/adapter/SearchPdfAdapter.kt | 17 +++-- .../ui/dialog/FileDetailsDialogFragment.kt | 2 +- .../app/ui/dialog/ListMoreDialogFragment.kt | 4 +- .../pro/app/ui/dialog/PdfSetPasswordDialog.kt | 27 ++------ .../pro/app/ui/fragment/ToolsFrag.kt | 4 ++ .../pro/app/util/PdfSecurityUtils.kt | 50 ++++++++++++-- .../res/layout/activity_pdf_split_result.xml | 3 +- app/src/main/res/layout/adapter_pdf_item.xml | 19 +++--- ...m.xml => adapter_selected_result_item.xml} | 15 ++++ app/src/main/res/layout/fragment_tools.xml | 13 ++++ app/src/main/res/values/strings.xml | 2 + 20 files changed, 217 insertions(+), 77 deletions(-) rename app/src/main/java/com/all/pdfreader/pro/app/ui/act/{SplitPdfResultActivity.kt => PdfResultActivity.kt} (81%) rename app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/{SplitPdfResultAdapter.kt => PdfResultAdapter.kt} (84%) rename app/src/main/res/layout/{adapter_split_selected_result_item.xml => adapter_selected_result_item.xml} (86%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 73ff0cd..6c79ea4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -74,7 +74,7 @@ android:screenOrientation="portrait" /> diff --git a/app/src/main/java/com/all/pdfreader/pro/app/model/PdfPickerSource.kt b/app/src/main/java/com/all/pdfreader/pro/app/model/PdfPickerSource.kt index 552a9c0..8b0d120 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/model/PdfPickerSource.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/model/PdfPickerSource.kt @@ -4,7 +4,8 @@ enum class PdfPickerSource { NONE, // 没有任何 MERGE, // PDF合并 SPLIT, // PDF拆分 - LOCK, // PDF加密/解密 + LOCK, // PDF加密 + UNLOCK, // PDF解密 TO_IMAGES, // PDF转图片 TO_LONG_IMAGE // PDF转长图 } \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/model/PdfSplitResultItem.kt b/app/src/main/java/com/all/pdfreader/pro/app/model/PdfSplitResultItem.kt index 1be956b..e17e3ac 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/model/PdfSplitResultItem.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/model/PdfSplitResultItem.kt @@ -3,5 +3,6 @@ package com.all.pdfreader.pro.app.model data class PdfSplitResultItem( val filePath: String, val thumbnailPath: String? = null, - var isSelected: Boolean + var isSelected: Boolean = false, + var isPassword: Boolean = false ) \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MergePdfActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MergePdfActivity.kt index 030e25a..2bdabb6 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MergePdfActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MergePdfActivity.kt @@ -62,7 +62,7 @@ class MergePdfActivity : BaseActivity() { } binding.continueNowBtn.setOnSingleClickListener { val list = selectedList.map { it.filePath } - val intent = SplitPdfResultActivity.createIntentInputFile( + val intent = PdfResultActivity.createIntentInputFile( this, ArrayList(list), PdfPickerSource.MERGE ) startActivity(intent) 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 9c32b0c..f0c60d9 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.PdfSetPasswordDialog import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation import com.all.pdfreader.pro.app.util.AppUtils.setOnSingleClickListener import com.gyf.immersionbar.ImmersionBar @@ -80,7 +81,21 @@ class PdfPickerActivity : BaseActivity() { finish() } - PdfPickerSource.LOCK -> {} + PdfPickerSource.LOCK -> { + PdfSetPasswordDialog(onOkClick = { password -> + val intent = PdfResultActivity.createIntentLock( + this, pdf.filePath, password, + PdfPickerSource.LOCK + ) + startActivity(intent) + finish() + }).show(supportFragmentManager, "PdfSetPasswordDialog") + } + + PdfPickerSource.UNLOCK -> { + + } + PdfPickerSource.TO_IMAGES -> {} PdfPickerSource.TO_LONG_IMAGE -> {} PdfPickerSource.NONE -> {} @@ -109,17 +124,25 @@ class PdfPickerActivity : BaseActivity() { private fun initData() { lifecycleScope.launch { PdfRepository.getInstance().getAllDocuments().collect { list -> + if (list.isEmpty()) { + binding.noFilesLayout.visibility = View.VISIBLE + return@collect + } val sortedList = sortDocuments(list) // 标记已选择项 if (fromActivityResult == MergePdfActivity.TAG) { markSelectedItems(sortedList, historyList) } - if (list.isNotEmpty()) { - adapter.updateData(sortedList) - binding.noFilesLayout.visibility = View.GONE - } else { - binding.noFilesLayout.visibility = View.VISIBLE + // 再根据来源过滤 + val displayList = when (source) { + PdfPickerSource.LOCK -> sortedList.filter { !it.isPassword }//只展示没有被加密的 + PdfPickerSource.UNLOCK -> sortedList.filter { it.isPassword }//只展示已经被加密的 + else -> sortedList } + + adapter.updateData(displayList) + binding.noFilesLayout.visibility = + if (displayList.isEmpty()) View.VISIBLE else View.GONE } } } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfResultActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfResultActivity.kt similarity index 81% rename from app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfResultActivity.kt rename to app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfResultActivity.kt index 1029958..e1fb587 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfResultActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfResultActivity.kt @@ -1,6 +1,5 @@ package com.all.pdfreader.pro.app.ui.act -import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.os.Build @@ -16,11 +15,13 @@ 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.ui.adapter.SplitPdfResultAdapter +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.FileUtils.toUnderscoreDateTime import com.all.pdfreader.pro.app.util.PdfScanner +import com.all.pdfreader.pro.app.util.PdfSecurityUtils import com.all.pdfreader.pro.app.util.PdfUtils import com.gyf.immersionbar.ImmersionBar import kotlinx.coroutines.Dispatchers @@ -29,18 +30,20 @@ import kotlinx.coroutines.withContext import java.io.File import java.io.Serializable -class SplitPdfResultActivity : BaseActivity() { - override val TAG: String = "SplitPdfResultActivity" +class PdfResultActivity : BaseActivity() { + override val TAG: String = "PdfResultActivity" companion object { private const val EXTRA_SELECTED_LIST = "extra_selected_list" private const val EXTRA_FILE_LIST = "extra_file_list" private const val EXTRA_SOURCE = "extra_source" + private const val EXTRA_PASSWORD = "extra_password" + private const val EXTRA_FILE_PATH = "extra_file_path" fun createIntentPdfSelectedPagesItem( context: Context, list: ArrayList, source: PdfPickerSource ): Intent { - return Intent(context, SplitPdfResultActivity::class.java).apply { + return Intent(context, PdfResultActivity::class.java).apply { putParcelableArrayListExtra(EXTRA_SELECTED_LIST, list) putExtra(EXTRA_SOURCE, source) } @@ -49,23 +52,35 @@ class SplitPdfResultActivity : BaseActivity() { fun createIntentInputFile( context: Context, list: ArrayList, source: PdfPickerSource ): Intent { - return Intent(context, SplitPdfResultActivity::class.java).apply { + return Intent(context, PdfResultActivity::class.java).apply { putStringArrayListExtra(EXTRA_FILE_LIST, list) putExtra(EXTRA_SOURCE, source) } } + + fun createIntentLock( + context: Context, filepath: String, password: String, source: PdfPickerSource + ): Intent { + return Intent(context, PdfResultActivity::class.java).apply { + putExtra(EXTRA_FILE_PATH, filepath) + putExtra(EXTRA_PASSWORD, password) + putExtra(EXTRA_SOURCE, source) + } + } } private lateinit var binding: ActivityPdfSplitResultBinding - private lateinit var adapter: SplitPdfResultAdapter + private lateinit var adapter: PdfResultAdapter private var resultList: MutableList = mutableListOf() private lateinit var selectedList: ArrayList private lateinit var inputFile: ArrayList private lateinit var source: PdfPickerSource - private var isSplitting = false + private var isProcessing = false private var exitDialog: PromptDialogFragment? = null private val pdfRepository = getRepository() private lateinit var pdfScanner: PdfScanner + private lateinit var filepath: String + private lateinit var password: String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -76,6 +91,8 @@ class SplitPdfResultActivity : BaseActivity() { .navigationBarColor(R.color.bg_color).init() selectedList = requireParcelableArrayList(EXTRA_SELECTED_LIST) inputFile = requireStringArrayList(EXTRA_FILE_LIST) + filepath = intent.getStringExtra(EXTRA_FILE_PATH) ?: "" + password = intent.getStringExtra(EXTRA_PASSWORD) ?: "" source = getSerializableOrDefault(EXTRA_SOURCE, PdfPickerSource.NONE) if (source == PdfPickerSource.NONE) { showToast(getString(R.string.pdf_loading_failed)) @@ -94,6 +111,12 @@ class SplitPdfResultActivity : BaseActivity() { finish() return } + } else if (source == PdfPickerSource.LOCK) { + if (filepath.isEmpty() || password.isEmpty()) { + showToast(getString(R.string.pdf_loading_failed)) + finish() + return + } } } pdfScanner = PdfScanner(this, pdfRepository) @@ -103,7 +126,7 @@ class SplitPdfResultActivity : BaseActivity() { } private fun initView() { - adapter = SplitPdfResultAdapter(resultList) + adapter = PdfResultAdapter(resultList) binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = adapter } @@ -111,7 +134,7 @@ class SplitPdfResultActivity : BaseActivity() { private fun initData() { lifecycleScope.launch(Dispatchers.IO) { withContext(Dispatchers.Main) { - isSplitting = true + isProcessing = true binding.processingLayout.visibility = View.VISIBLE binding.progressBar.isIndeterminate = false binding.progressBar.progress = 0 @@ -143,7 +166,7 @@ class SplitPdfResultActivity : BaseActivity() { } })?.let { resultFile -> val thumbnails = - AppUtils.generateFastThumbnail(this@SplitPdfResultActivity, resultFile) + AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile) val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) { resultList.add(result) @@ -172,13 +195,27 @@ class SplitPdfResultActivity : BaseActivity() { } })?.let { resultFile -> val thumbnails = - AppUtils.generateFastThumbnail(this@SplitPdfResultActivity, resultFile) + AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile) val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) { resultList.add(result) } } } + } else if (source == PdfPickerSource.LOCK) { + binding.congratulationsDesc.text = getString(R.string.set_password_successfully) + PdfSecurityUtils.setPasswordToPdfWithProgress( + filepath, password, password + ) { progress -> + binding.progressBar.progress = progress + binding.progressTv.text = "$progress" + }.let { it -> + if (it) { + PdfRepository.getInstance().updateIsPassword(filepath, true) + } + val result = PdfSplitResultItem(filePath = filepath, isPassword = true) + resultList.add(result) + } } withContext(Dispatchers.Main) { binding.processingLayout.visibility = View.GONE @@ -187,7 +224,7 @@ class SplitPdfResultActivity : BaseActivity() { resultList[0].isSelected = true } adapter.updateAdapter() - isSplitting = false//拆分结束 + isProcessing = false//拆分结束 exitDialog?.dismissAllowingStateLoss() exitDialog = null } @@ -216,12 +253,15 @@ class SplitPdfResultActivity : BaseActivity() { override fun onDestroy() { super.onDestroy() + isProcessing = false + exitDialog?.dismissAllowingStateLoss() + exitDialog = null } private fun setupBackPressedCallback() { onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (isSplitting) { + if (isProcessing) { exitDialog = PromptDialogFragment( getString(R.string.exit_split), getString(R.string.confirm_discard_changes), diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt index dda8d32..42bec52 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import org.json.JSONArray +import kotlin.math.log class SearchActivity : BaseActivity() { override val TAG: String = "SearchActivity" @@ -87,8 +88,9 @@ class SearchActivity : BaseActivity() { delay(150) pdfRepository.searchDocuments(query).collectLatest { list -> if (list.isNotEmpty()) { - adapter.updateData(list) - adapter.highlightItems(query.trim()) + adapter.updateData(list) { + adapter.highlightItems(query.trim()) + } binding.noFilesLayout.visibility = View.GONE } else { adapter.updateData(emptyList()) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfActivity.kt index 0c57c36..b60fee6 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfActivity.kt @@ -5,9 +5,7 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.activity.OnBackPressedCallback -import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import com.all.pdfreader.pro.app.R @@ -140,7 +138,7 @@ class SplitPdfActivity : BaseActivity() { binding.splitBtn.setOnSingleClickListener { logDebug("${selectedList.size}") //因为图片做的路径缓存方式,所以这里直接传入整个集合到result页处理 - val intent = SplitPdfResultActivity.createIntentPdfSelectedPagesItem( + val intent = PdfResultActivity.createIntentPdfSelectedPagesItem( this, ArrayList(selectedList), PdfPickerSource.SPLIT diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitPdfResultAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfResultAdapter.kt similarity index 84% rename from app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitPdfResultAdapter.kt rename to app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfResultAdapter.kt index 701f572..da33187 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitPdfResultAdapter.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfResultAdapter.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.all.pdfreader.pro.app.R -import com.all.pdfreader.pro.app.databinding.AdapterSplitSelectedResultItemBinding +import com.all.pdfreader.pro.app.databinding.AdapterSelectedResultItemBinding import com.all.pdfreader.pro.app.model.PdfSplitResultItem import com.all.pdfreader.pro.app.util.AppUtils.dpToPx import com.bumptech.glide.Glide @@ -14,15 +14,15 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.RoundedCorners import java.io.File -class SplitPdfResultAdapter( +class PdfResultAdapter( private val list: MutableList -) : RecyclerView.Adapter() { +) : RecyclerView.Adapter() { - inner class PdfViewHolder(val binding: AdapterSplitSelectedResultItemBinding) : + inner class PdfViewHolder(val binding: AdapterSelectedResultItemBinding) : RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PdfViewHolder( - AdapterSplitSelectedResultItemBinding.inflate( + AdapterSelectedResultItemBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) @@ -35,6 +35,11 @@ class SplitPdfResultAdapter( .transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context))) .into(holder.binding.image) } + if (item.isPassword) { + holder.binding.lockLayout.visibility = View.VISIBLE + } else { + holder.binding.lockLayout.visibility = View.GONE + } holder.binding.nameTv.text = File(item.filePath).name holder.binding.pathTv.text = holder.binding.root.context.getString(R.string.path_details, item.filePath) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SearchPdfAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SearchPdfAdapter.kt index 0217c44..4ba99d4 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SearchPdfAdapter.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SearchPdfAdapter.kt @@ -1,6 +1,7 @@ package com.all.pdfreader.pro.app.ui.adapter import android.annotation.SuppressLint +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -24,7 +25,7 @@ class SearchPdfAdapter( private var showMoreButton: Boolean = true, // 是否显示更多按钮 private var showCheckButton: Boolean = false // 是否显示选择框按钮 ) : ListAdapter(PdfDiffCallback()) { - + private var searchKeyword: String? = null // 保存当前搜索关键字 inner class PdfViewHolder(val binding: AdapterPdfItemBinding) : RecyclerView.ViewHolder(binding.root) @@ -54,12 +55,13 @@ class SearchPdfAdapter( holder.binding.checkBtn.visibility = if (showCheckButton) View.VISIBLE else View.GONE holder.binding.moreBtn.visibility = if (showMoreButton) View.VISIBLE else View.GONE + holder.binding.deleteBtn.visibility = View.GONE - // 文件名高亮,如果 highlightKeyword 为 null 或空字符串,就显示普通文本 - holder.binding.tvFileName.text = if (highlightKeyword.isNullOrBlank()) { + val keyword = highlightKeyword ?: searchKeyword + holder.binding.tvFileName.text = if (keyword.isNullOrBlank()) { item.fileName } else { - item.fileName.toHighlightedSpannable(highlightKeyword, holder.binding.root.context.getColor(R.color.icon_sel_on_color)) + item.fileName.toHighlightedSpannable(keyword, holder.binding.root.context.getColor(R.color.icon_sel_on_color)) } holder.binding.tvFileSize.text = item.fileSize.toFormatFileSize() @@ -85,14 +87,17 @@ class SearchPdfAdapter( holder.binding.moreBtn.setOnClickListener { onMoreClick(item) } } - fun updateData(newList: List) { - submitList(newList.toList()) + fun updateData(newList: List, onComplete: (() -> Unit)? = null) { + submitList(newList.toList()) { + onComplete?.invoke() + } } /** * 搜索场景使用:只刷新高亮,不刷新整个列表 */ fun highlightItems(keyword: String) { + searchKeyword = keyword for (i in 0 until itemCount) { notifyItemChanged(i, keyword) } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/FileDetailsDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/FileDetailsDialogFragment.kt index 991fa7a..0aa3b24 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/FileDetailsDialogFragment.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/FileDetailsDialogFragment.kt @@ -31,7 +31,7 @@ class FileDetailsDialogFragment() : BottomSheetDialogFragment() { override fun onStart() { super.onStart() dialog?.window?.findViewById(com.google.android.material.R.id.design_bottom_sheet) - ?.setBackgroundResource(R.drawable.dr_rounded_corner_12_bg_white) + ?.setBackgroundResource(R.drawable.dr_rc_top_12_bg_white) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 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 a859680..b247b3a 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 @@ -197,7 +197,9 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment() if (pdfDocument.isPassword) { PdfRemovePasswordDialog().show(parentFragmentManager, "PdfRemovePasswordDialog") } else { - PdfSetPasswordDialog().show(parentFragmentManager, "PdfSetPasswordDialog") + PdfSetPasswordDialog(onOkClick = { password -> + viewModel.setPassword(pdfDocument.filePath, password) + }).show(parentFragmentManager, "PdfSetPasswordDialog") } dismiss() } 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 ed53bb2..c6c5dba 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 @@ -7,24 +7,17 @@ import android.text.TextWatcher import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast 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.DialogPdfSetPasswordBinding -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.ToastUtils -import com.all.pdfreader.pro.app.viewmodel.PdfViewModel -import kotlin.getValue -class PdfSetPasswordDialog() : DialogFragment( - -) { +class PdfSetPasswordDialog( + private val onOkClick: (password: String) -> Unit = {} +) : DialogFragment() { private lateinit var binding: DialogPdfSetPasswordBinding - private val viewModel: PdfViewModel by activityViewModels() - private lateinit var pdfDocument: PdfDocumentEntity override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -48,15 +41,9 @@ class PdfSetPasswordDialog() : DialogFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.pdfDocument.value?.let { - pdfDocument = it - binding.etPassword.showKeyboard() - setupListeners() - setupTextWatchers() - } ?: run { - showToast(getString(R.string.file_not)) - dismiss() - } + binding.etPassword.showKeyboard() + setupListeners() + setupTextWatchers() } private fun setupListeners() { @@ -68,7 +55,7 @@ class PdfSetPasswordDialog() : DialogFragment( val password = binding.etPassword.text.toString() val confirmPassword = binding.etConfirmPassword.text.toString() if (validatePassword(password, confirmPassword)) { - viewModel.setPassword(pdfDocument.filePath, password) + onOkClick(password) dismiss() } } 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 79cfdbd..200f792 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 @@ -33,5 +33,9 @@ class ToolsFrag : Fragment() { val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.SPLIT) startActivity(intent) } + binding.lockBtn.setOnClickListener { + val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.LOCK) + 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 9c8b4a6..a1667d8 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,23 +1,27 @@ package com.all.pdfreader.pro.app.util +import android.graphics.pdf.PdfDocument import com.tom_roush.pdfbox.pdmodel.PDDocument import com.tom_roush.pdfbox.pdmodel.encryption.AccessPermission import com.tom_roush.pdfbox.pdmodel.encryption.StandardProtectionPolicy +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File object PdfSecurityUtils { fun setPasswordToPdf( - inputFilePath: String, - userPassword: String, - ownerPassword: String + inputFilePath: String, userPassword: String, ownerPassword: String ): Boolean { return try { PDDocument.load(File(inputFilePath)).use { document -> val accessPermission = AccessPermission() val protectionPolicy = StandardProtectionPolicy(ownerPassword, userPassword, accessPermission) - protectionPolicy.encryptionKeyLength = 256 + protectionPolicy.encryptionKeyLength = 128 protectionPolicy.permissions = accessPermission document.protect(protectionPolicy) document.save(inputFilePath) @@ -29,6 +33,44 @@ object PdfSecurityUtils { } } + suspend fun setPasswordToPdfWithProgress( + inputFilePath: String, + userPassword: String, + ownerPassword: String, + onProgress: (Int) -> Unit + ): 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 + else -> 3 // 90~98% 每次 +3 + } + if (progress > 98) progress = 98 + withContext(Dispatchers.Main) { + onProgress(progress) + } + } + } + + success = setPasswordToPdf(inputFilePath, userPassword, ownerPassword) + + // 停止假进度 + job.cancel() + + // 最终完成直接 100% + withContext(Dispatchers.Main) { + onProgress(100) + } + success + } + + fun removePasswordFromPdf(inputFilePath: String, password: String): Boolean { return try { PDDocument.load(File(inputFilePath), password).use { document -> diff --git a/app/src/main/res/layout/activity_pdf_split_result.xml b/app/src/main/res/layout/activity_pdf_split_result.xml index b083ca5..0626a24 100644 --- a/app/src/main/res/layout/activity_pdf_split_result.xml +++ b/app/src/main/res/layout/activity_pdf_split_result.xml @@ -109,6 +109,7 @@ android:textSize="20sp" /> + tools:listitem="@layout/adapter_selected_result_item" /> - - - + + + + + + + + +