添加合并pdf,遇到有密码的文件。正常处理。
This commit is contained in:
parent
0bc4a4fab2
commit
8504e7cbea
@ -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
|
||||
@ -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<PdfDocumentEntity>
|
||||
private var selectedList: ArrayList<PdfDocumentEntity> = arrayListOf()
|
||||
private lateinit var adapter: PdfAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -31,11 +40,60 @@ 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)
|
||||
val list: ArrayList<PdfDocumentEntity> = 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<PdfDocumentEntity>,
|
||||
onAllPasswordsReady: (ArrayList<PdfDocumentEntity>) -> 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(
|
||||
@ -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<PdfDocumentEntity>)
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,12 +138,21 @@ class MergePdfActivity : BaseActivity() {
|
||||
pickPdfLauncher.launch(PdfPickerSource.MERGE to selectedList)
|
||||
}
|
||||
|
||||
private fun handleSelectedPdfs(list: List<PdfDocumentEntity>) {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun handleSelectedPdfs(list: ArrayList<PdfDocumentEntity>) {
|
||||
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) {
|
||||
binding.continueNowBtn.setBackgroundResource(
|
||||
|
||||
@ -155,9 +155,16 @@ class PdfPickerActivity : BaseActivity() {
|
||||
private fun markSelectedItems(
|
||||
sortedList: List<PdfDocumentEntity>, historyList: List<PdfDocumentEntity>
|
||||
) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<PdfSelectedPagesItem>,
|
||||
source: PdfPickerSource,
|
||||
@ -64,6 +66,15 @@ class PdfResultActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
fun createIntentMergePdfActivityToResult(
|
||||
context: Context, list: ArrayList<PdfDocumentEntity>, 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<PdfSplitResultItem> = mutableListOf()
|
||||
private lateinit var selectedList: ArrayList<PdfSelectedPagesItem>
|
||||
private lateinit var inputFile: ArrayList<String>
|
||||
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<PdfSelectedPagesItem> =
|
||||
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,8 +175,16 @@ class PdfResultActivity : BaseActivity() {
|
||||
binding.progressBar.isIndeterminate = false
|
||||
binding.progressBar.progress = 0
|
||||
binding.progressBar.max = 100
|
||||
if (inputFile.isNotEmpty()) {
|
||||
val inputFiles: List<File> = inputFile.map { path -> File(path) }
|
||||
val mergeInputFile: ArrayList<PdfDocumentEntity> =
|
||||
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"
|
||||
@ -194,8 +192,8 @@ class PdfResultActivity : BaseActivity() {
|
||||
val outputFileName =
|
||||
getString(R.string.merge) + "_" + System.currentTimeMillis()
|
||||
.toUnderscoreDateTime() + ".pdf"
|
||||
PdfUtils.mergePdfFilesSafe(
|
||||
inputFiles = inputFiles,
|
||||
PdfUtils.mergePdfFilesWithPassword(
|
||||
inputFiles = inputWithPasswords,
|
||||
outputDir = outputDir,
|
||||
outputFileName = outputFileName,
|
||||
onProgress = { current, total ->
|
||||
@ -211,11 +209,17 @@ class PdfResultActivity : BaseActivity() {
|
||||
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,25 +230,37 @@ 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<File> = 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<File> = 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()
|
||||
val outputFileName = getString(R.string.app_name) + "_" + System.currentTimeMillis()
|
||||
.toUnderscoreDateTime() + ".pdf"
|
||||
PdfUtils.imgToPdfFilesSafe(
|
||||
inputFiles = inputFiles,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -193,8 +193,8 @@ object PdfUtils {
|
||||
* @param onProgress 可选回调,当前处理进度 (current 文件, total 文件)
|
||||
* @return 新生成的 PDF 文件,失败返回 null
|
||||
*/
|
||||
suspend fun mergePdfFilesSafe(
|
||||
inputFiles: List<File>,
|
||||
suspend fun mergePdfFilesWithPassword(
|
||||
inputFiles: List<Pair<File, String?>>, // 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()
|
||||
|
||||
@ -9,20 +9,31 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
style="@style/TextViewFont_PopSemiBold"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
style="@style/TextViewFont_PopSemiBold"
|
||||
android:text="@string/password_protection"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMessage"
|
||||
android:id="@+id/promptTv"
|
||||
style="@style/TextViewFont_PopMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/file_is_password_protected"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMessage"
|
||||
style="@style/TextViewFont_PopRegular"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/password_protection_dialog_desc"
|
||||
android:textColor="@color/black_80"
|
||||
android:textSize="14sp" />
|
||||
@ -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">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etPassword"
|
||||
style="@style/TextViewFont_PopRegular"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextViewFont_PopRegular"
|
||||
android:inputType="textPassword"
|
||||
android:textSize="16sp" />
|
||||
|
||||
@ -61,19 +72,19 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCancel"
|
||||
style="@style/TextViewFont_PopRegular"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
style="@style/TextViewFont_PopRegular"
|
||||
android:padding="12dp"
|
||||
android:text="@string/cancel"
|
||||
android:textColor="@color/black_80" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvConfirm"
|
||||
style="@style/TextViewFont_PopMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextViewFont_PopMedium"
|
||||
android:padding="12dp"
|
||||
android:text="@string/ok"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
<string name="created_date">Created Date</string>
|
||||
<string name="path">Path</string>
|
||||
<string name="path_details">Path: %1$s</string>
|
||||
<string name="file_is_password_protected">%1$s is password protected</string>
|
||||
<string name="file_name">File Name</string>
|
||||
<string name="file_size">File Size</string>
|
||||
<string name="ascending">Ascending</string>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user