From 8504e7cbea70d4fdadb6ef037a50eaedce9f386a Mon Sep 17 00:00:00 2001 From: ocean <503259349@qq.com> Date: Thu, 16 Oct 2025 18:08:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=88=E5=B9=B6pdf?= =?UTF-8?q?=EF=BC=8C=E9=81=87=E5=88=B0=E6=9C=89=E5=AF=86=E7=A0=81=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E3=80=82=E6=AD=A3=E5=B8=B8=E5=A4=84=E7=90=86?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pro/app/room/entity/PdfDocumentEntity.kt | 3 +- .../pro/app/ui/act/MergePdfActivity.kt | 94 +++++++++-- .../pro/app/ui/act/PdfPickerActivity.kt | 11 +- .../pro/app/ui/act/PdfResultActivity.kt | 148 ++++++++++-------- .../pro/app/ui/act/SplitPdfActivity.kt | 2 +- .../PdfPasswordProtectionDialogFragment.kt | 9 +- .../all/pdfreader/pro/app/util/PdfUtils.kt | 34 +++- .../layout/dialog_pdf_password_protection.xml | 25 ++- app/src/main/res/values/strings.xml | 1 + 9 files changed, 227 insertions(+), 100 deletions(-) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/room/entity/PdfDocumentEntity.kt b/app/src/main/java/com/all/pdfreader/pro/app/room/entity/PdfDocumentEntity.kt index 0df8061..772ea74 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/room/entity/PdfDocumentEntity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/room/entity/PdfDocumentEntity.kt @@ -32,7 +32,8 @@ data class PdfDocumentEntity( val metadataCreationDate: Long? = null, // PDF创建时间 val metadataModificationDate: Long? = null, // PDF修改时间 - val password: String? = null,// PDF密码(加密存储) val isPassword: Boolean = false,//是否存在密码 + + var password: String? = null,// PDF密码(加密存储) var isSelected: Boolean = false ) : Parcelable \ 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 2bdabb6..57e22cf 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 @@ -1,5 +1,6 @@ package com.all.pdfreader.pro.app.ui.act +import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.os.Build @@ -7,21 +8,29 @@ import android.os.Bundle import android.os.Parcelable import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContract +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.databinding.ActivityPdfMergeBinding import com.all.pdfreader.pro.app.model.PdfPickerSource import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter +import com.all.pdfreader.pro.app.ui.dialog.PdfPasswordProtectionDialogFragment import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment 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.FileUtils.isPdfEncrypted import com.gyf.immersionbar.ImmersionBar +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import java.io.File class MergePdfActivity : BaseActivity() { override val TAG: String = "MergePdfActivity" private lateinit var binding: ActivityPdfMergeBinding - private lateinit var selectedList: ArrayList + private var selectedList: ArrayList = arrayListOf() private lateinit var adapter: PdfAdapter override fun onCreate(savedInstanceState: Bundle?) { @@ -31,12 +40,61 @@ class MergePdfActivity : BaseActivity() { setupBackPressedCallback() ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true) .navigationBarColor(R.color.bg_color).init() - selectedList = requireParcelableArrayList(EXTRA_PDF_LIST) - updateContinueNowBtnState(selectedList.size >= 2) - initView() - setupClick() + val list: ArrayList = requireParcelableArrayList(EXTRA_PDF_LIST) + updateContinueNowBtnState(list.size >= 2) + lifecycleScope.launch { + handlePasswordProtectedPdfs(list) { + list.forEach { + logDebug("onCreate password->${it.password}") + logDebug("onCreate isPassword->${it.isPassword}") + } + selectedList.clear() + selectedList.addAll(list) + initView() + setupClick() + } + } } + /** + * 遍历集合,弹出密码对话框并更新 password 字段 + */ + private suspend fun handlePasswordProtectedPdfs( + pdfList: ArrayList, + onAllPasswordsReady: (ArrayList) -> Unit + ) { + for (pdf in pdfList) { + if (pdf.isPassword && pdf.password.isNullOrEmpty()) { + val pwd = showPasswordDialogSuspend(File(pdf.filePath)) ?: run { + finish() + return + } + pdf.password = pwd + } + } + onAllPasswordsReady(pdfList) + } + + /** + * 将 DialogFragment 封装成 suspend 函数 + */ + private suspend fun showPasswordDialogSuspend(file: File): String? = + suspendCancellableCoroutine { cont -> + PdfPasswordProtectionDialogFragment( + file, + onOkClick = { password -> + if (cont.isActive) { + cont.resumeWith(Result.success(password)) + } + }, + onCancelClick = { + if (cont.isActive) { + cont.resumeWith(Result.success(null)) + } + }, isPrompt = true + ).show(supportFragmentManager, TAG) + } + private fun initView() { adapter = PdfAdapter( pdfList = selectedList, @@ -61,9 +119,8 @@ class MergePdfActivity : BaseActivity() { openPicker() } binding.continueNowBtn.setOnSingleClickListener { - val list = selectedList.map { it.filePath } - val intent = PdfResultActivity.createIntentInputFile( - this, ArrayList(list), PdfPickerSource.MERGE + val intent = PdfResultActivity.createIntentMergePdfActivityToResult( + this, selectedList, PdfPickerSource.MERGE ) startActivity(intent) finish() @@ -73,7 +130,7 @@ class MergePdfActivity : BaseActivity() { private val pickPdfLauncher = registerForActivityResult(PickPdfContract(Companion.TAG)) { list -> if (list.isNotEmpty()) { - handleSelectedPdfs(list) + handleSelectedPdfs(list as ArrayList) } } @@ -81,11 +138,20 @@ class MergePdfActivity : BaseActivity() { pickPdfLauncher.launch(PdfPickerSource.MERGE to selectedList) } - private fun handleSelectedPdfs(list: List) { - selectedList.clear() - selectedList.addAll(list) - adapter.notifyDataSetChanged() - updateContinueNowBtnState(selectedList.size >= 2) + @SuppressLint("NotifyDataSetChanged") + private fun handleSelectedPdfs(list: ArrayList) { + lifecycleScope.launch { + handlePasswordProtectedPdfs(list) { + list.forEach { + logDebug("handleSelectedPdfs password->${it.password}") + logDebug("handleSelectedPdfs isPassword->${it.isPassword}") + } + selectedList.clear() + selectedList.addAll(list) + adapter.notifyDataSetChanged() + updateContinueNowBtnState(selectedList.size >= 2) + } + } } private fun updateContinueNowBtnState(b: Boolean) { 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 7dba9e4..d1a83c8 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 @@ -155,9 +155,16 @@ class PdfPickerActivity : BaseActivity() { private fun markSelectedItems( sortedList: List, historyList: List ) { - val historySet = historyList.map { it.filePath }.toSet() + val historyMap = historyList.associateBy { it.filePath } sortedList.forEach { item -> - item.isSelected = item.filePath in historySet + val historyItem = historyMap[item.filePath] + if (historyItem != null) { + item.isSelected = true + // 同步历史密码 + item.password = historyItem.password + } else { + item.isSelected = false + } } } 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 3c38f45..998a991 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 @@ -16,6 +16,7 @@ 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.entity.PdfDocumentEntity 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 @@ -37,12 +38,13 @@ class PdfResultActivity : BaseActivity() { companion object { private const val EXTRA_SELECTED_LIST = "extra_selected_list" private const val EXTRA_FILE_LIST = "extra_file_list" + private const val EXTRA_MERGE_LIST = "extra_merge_list"//合并传输使用的key private const val EXTRA_SOURCE = "extra_source" private const val EXTRA_LOCK_UNLOCK_PASSWORD = "extra_lock_unlock_password" private const val EXTRA_FILE_PATH = "extra_file_path" private const val EXTRA_SPLIT_PASSWORD = "extra_split_password" - fun createIntentPdfSelectedPagesItem( + fun createIntentSplitPdfActivityToResult( context: Context, list: ArrayList, source: PdfPickerSource, @@ -64,6 +66,15 @@ class PdfResultActivity : BaseActivity() { } } + fun createIntentMergePdfActivityToResult( + context: Context, list: ArrayList, source: PdfPickerSource + ): Intent { + return Intent(context, PdfResultActivity::class.java).apply { + putParcelableArrayListExtra(EXTRA_MERGE_LIST, list) + putExtra(EXTRA_SOURCE, source) + } + } + fun createIntentLock( context: Context, filepath: String, password: String, source: PdfPickerSource ): Intent { @@ -78,15 +89,11 @@ class PdfResultActivity : BaseActivity() { private lateinit var binding: ActivityPdfSplitResultBinding 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 isProcessing = false private var exitDialog: PromptDialogFragment? = null private val pdfRepository = getRepository() private lateinit var pdfScanner: PdfScanner - private lateinit var filepath: String - private lateinit var lockAndUnlockPassword: String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -95,35 +102,11 @@ class PdfResultActivity : BaseActivity() { setupBackPressedCallback() ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true) .navigationBarColor(R.color.bg_color).init() - selectedList = requireParcelableArrayList(EXTRA_SELECTED_LIST) - inputFile = requireStringArrayList(EXTRA_FILE_LIST) - filepath = intent.getStringExtra(EXTRA_FILE_PATH) ?: "" - lockAndUnlockPassword = intent.getStringExtra(EXTRA_LOCK_UNLOCK_PASSWORD) ?: "" source = getSerializableOrDefault(EXTRA_SOURCE, PdfPickerSource.NONE) if (source == PdfPickerSource.NONE) { showToast(getString(R.string.pdf_loading_failed)) finish() return - } else { - if (source == PdfPickerSource.SPLIT) { - if (selectedList.isEmpty()) { - showToast(getString(R.string.pdf_loading_failed)) - finish() - return - } - } else if (source == PdfPickerSource.MERGE || source == PdfPickerSource.TO_IMAGES) { - if (inputFile.isEmpty()) { - showToast(getString(R.string.pdf_loading_failed)) - finish() - return - } - } else if (source == PdfPickerSource.LOCK || source == PdfPickerSource.UNLOCK) { - if (filepath.isEmpty() || lockAndUnlockPassword.isEmpty()) { - showToast(getString(R.string.pdf_loading_failed)) - finish() - return - } - } } pdfScanner = PdfScanner(this, pdfRepository) initView() @@ -148,20 +131,26 @@ class PdfResultActivity : BaseActivity() { binding.progressBar.isIndeterminate = false binding.progressBar.progress = 0 binding.progressBar.max = 100 - + val selectedList: ArrayList = + requireParcelableArrayList(EXTRA_SELECTED_LIST) + if (selectedList.isEmpty()) { + showToast(getString(R.string.pdf_loading_failed)) + finish() + return@launch + } val totalPages = selectedList.sumOf { it.pages.count { it.isSelected } } var processedPages = 0 for (item in selectedList) { val selectedPages = item.pages.filter { it.isSelected } if (selectedPages.isEmpty()) continue - val inputFile = File(item.filePath) + val splitInputFile = File(item.filePath) val outputDir = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "PDFReaderPro/split" ).apply { if (!exists()) mkdirs() } PdfUtils.exportSelectedPages( - inputFile = inputFile, + inputFile = splitInputFile, selectedPages = selectedPages, outputDir = outputDir, outputFileName = "${item.fileName}.pdf", @@ -172,7 +161,8 @@ class PdfResultActivity : BaseActivity() { binding.progressTv.text = "$percent" binding.progressBar.progress = percent } - }, splitPassword + }, + splitPassword )?.let { resultFile -> val thumbnails = generateFastThumbnail(this@PdfResultActivity, resultFile) val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) @@ -185,37 +175,51 @@ class PdfResultActivity : BaseActivity() { binding.progressBar.isIndeterminate = false binding.progressBar.progress = 0 binding.progressBar.max = 100 - if (inputFile.isNotEmpty()) { - val inputFiles: List = inputFile.map { path -> File(path) } - val outputDir = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), - "PDFReaderPro/merge" - ).apply { if (!exists()) mkdirs() } - val outputFileName = - getString(R.string.merge) + "_" + System.currentTimeMillis() - .toUnderscoreDateTime() + ".pdf" - PdfUtils.mergePdfFilesSafe( - inputFiles = inputFiles, - outputDir = outputDir, - outputFileName = outputFileName, - onProgress = { current, total -> - val progressPercent = current * 100 / total - lifecycleScope.launch(Dispatchers.Main) { - binding.progressBar.progress = progressPercent - binding.progressTv.text = "$progressPercent" - } - })?.let { resultFile -> - val thumbnails = generateFastThumbnail(this@PdfResultActivity, resultFile) - val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false) - pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) { - resultList.add(result) + val mergeInputFile: ArrayList = + requireParcelableArrayList(EXTRA_MERGE_LIST) + if (mergeInputFile.isEmpty()) { + showToast(getString(R.string.pdf_loading_failed)) + finish() + return@launch + } + val inputWithPasswords = mergeInputFile.map { pdf -> + File(pdf.filePath) to pdf.password + } + val outputDir = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), + "PDFReaderPro/merge" + ).apply { if (!exists()) mkdirs() } + val outputFileName = + getString(R.string.merge) + "_" + System.currentTimeMillis() + .toUnderscoreDateTime() + ".pdf" + PdfUtils.mergePdfFilesWithPassword( + inputFiles = inputWithPasswords, + outputDir = outputDir, + outputFileName = outputFileName, + onProgress = { current, total -> + val progressPercent = current * 100 / total + lifecycleScope.launch(Dispatchers.Main) { + binding.progressBar.progress = progressPercent + binding.progressTv.text = "$progressPercent" } + })?.let { resultFile -> + val thumbnails = 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) { + val filepath = intent.getStringExtra(EXTRA_FILE_PATH) ?: "" + if (filepath.isEmpty()) { + showToast(getString(R.string.pdf_loading_failed)) + finish() + return@launch + } + val lockPassword = intent.getStringExtra(EXTRA_LOCK_UNLOCK_PASSWORD) ?: "" binding.congratulationsDesc.text = getString(R.string.set_password_successfully) PdfSecurityUtils.setPasswordToPdfWithProgress( - filepath, lockAndUnlockPassword, lockAndUnlockPassword + filepath, lockPassword, lockPassword ) { progress -> binding.progressTv.text = "$progress" }.let { it -> @@ -226,26 +230,38 @@ class PdfResultActivity : BaseActivity() { resultList.add(result) } } else if (source == PdfPickerSource.UNLOCK) { + val filepath = intent.getStringExtra(EXTRA_FILE_PATH) ?: "" + if (filepath.isEmpty()) { + showToast(getString(R.string.pdf_loading_failed)) + finish() + return@launch + } + val unlockPassword = intent.getStringExtra(EXTRA_LOCK_UNLOCK_PASSWORD) ?: "" binding.congratulationsDesc.text = getString(R.string.remove_password_successfully) - PdfSecurityUtils.removePasswordFromPdfWithProgress(filepath, lockAndUnlockPassword) { progress -> + PdfSecurityUtils.removePasswordFromPdfWithProgress( + filepath, unlockPassword + ) { progress -> binding.progressTv.text = "$progress" }?.let { it -> val result = PdfSplitResultItem( - filePath = filepath, - thumbnailPath = it, - isPassword = false + filePath = filepath, thumbnailPath = it, isPassword = false ) resultList.add(result) } } else if (source == PdfPickerSource.TO_IMAGES) { - val inputFiles: List = inputFile.map { path -> File(path) } + val files = requireStringArrayList(EXTRA_FILE_LIST) + if (files.isEmpty()) { + showToast(getString(R.string.pdf_loading_failed)) + finish() + return@launch + } + val inputFiles: List = files.map { path -> File(path) } val outputDir = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "PDFReaderPro/img2Pdf" ).apply { if (!exists()) mkdirs() } - val outputFileName = - getString(R.string.app_name) + "_" + System.currentTimeMillis() - .toUnderscoreDateTime() + ".pdf" + val outputFileName = getString(R.string.app_name) + "_" + System.currentTimeMillis() + .toUnderscoreDateTime() + ".pdf" PdfUtils.imgToPdfFilesSafe( inputFiles = inputFiles, outputDir = outputDir, 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 4abd84d..55409d6 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 @@ -141,7 +141,7 @@ class SplitPdfActivity : BaseActivity() { binding.splitBtn.setOnSingleClickListener { logDebug("${selectedList.size}") //因为图片做的路径缓存方式,所以这里直接传入整个集合到result页处理 - val intent = PdfResultActivity.createIntentPdfSelectedPagesItem( + val intent = PdfResultActivity.createIntentSplitPdfActivityToResult( this, ArrayList(selectedList), PdfPickerSource.SPLIT, 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 c644936..a6c09b1 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 @@ -16,7 +16,8 @@ import java.io.File class PdfPasswordProtectionDialogFragment( private val file: File, private val onOkClick: (String) -> Unit, - private val onCancelClick: () -> Unit + private val onCancelClick: () -> Unit, + private val isPrompt: Boolean = false ) : DialogFragment() { private lateinit var binding: DialogPdfPasswordProtectionBinding @@ -47,6 +48,12 @@ class PdfPasswordProtectionDialogFragment( binding.etPassword.showKeyboard() + if (isPrompt) { + binding.promptTv.visibility = View.VISIBLE + binding.promptTv.text = getString(R.string.file_is_password_protected, file.name) + } else { + binding.promptTv.visibility = View.GONE + } binding.tvCancel.setOnClickListener { onCancelClick() diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt index 664d481..7e8db3a 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt @@ -193,8 +193,8 @@ object PdfUtils { * @param onProgress 可选回调,当前处理进度 (current 文件, total 文件) * @return 新生成的 PDF 文件,失败返回 null */ - suspend fun mergePdfFilesSafe( - inputFiles: List, + suspend fun mergePdfFilesWithPassword( + inputFiles: List>, // Pair<文件, 密码> outputDir: File, outputFileName: String, onProgress: ((current: Int, total: Int) -> Unit)? = null @@ -209,9 +209,26 @@ object PdfUtils { val merger = PDFMergerUtility() val total = 100 - // 加载文件 0~30% - inputFiles.forEachIndexed { index, file -> - merger.addSource(file) + // 0~30%: 加载 PDF + inputFiles.forEachIndexed { index, (file, password) -> + val doc = if (!password.isNullOrEmpty()) { + PDDocument.load(file, password) + } else { + PDDocument.load(file) + } + + // 移除加密保护 + if (doc.isEncrypted) { + doc.isAllSecurityToBeRemoved = true + } + + // 将文档临时保存到内存文件,然后添加给 merger + val tempFile = File.createTempFile("merge_", ".pdf") + doc.save(tempFile) + doc.close() + + merger.addSource(tempFile) + val progress = ((index + 1).toFloat() / inputFiles.size * 30).toInt() onProgress?.invoke(progress, total) delay(50) @@ -219,17 +236,18 @@ object PdfUtils { merger.destinationFileName = outputFile.absolutePath - // 伪进度(30%~95%) + // 伪进度 30~95% var fakeProgress = 30 val fakeJob = launch { while (fakeProgress < 95) { - fakeProgress += (1..3).random() // 每次前进一点点 + fakeProgress += (1..3).random() if (fakeProgress > 95) fakeProgress = 95 onProgress?.invoke(fakeProgress, total) - delay((100L..300L).random()) // 模拟不均匀的进度 + delay((100L..300L).random()) } } + // 真正合并 merger.mergeDocuments(MemoryUsageSetting.setupTempFileOnly()) fakeJob.cancel() 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 ec90984..cc91244 100644 --- a/app/src/main/res/layout/dialog_pdf_password_protection.xml +++ b/app/src/main/res/layout/dialog_pdf_password_protection.xml @@ -9,20 +9,31 @@ + + @@ -37,14 +48,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/enter_password" - app:endIconMode="password_toggle" - app:endIconDrawable="@drawable/dr_password_state"> + app:endIconDrawable="@drawable/dr_password_state" + app:endIconMode="password_toggle"> @@ -61,19 +72,19 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 90fa28f..d9e94b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,6 +21,7 @@ Created Date Path Path: %1$s + %1$s is password protected File Name File Size Ascending