diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfToImageActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfToImageActivity.kt index efab259..311e286 100644 --- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfToImageActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfToImageActivity.kt @@ -16,6 +16,7 @@ import com.all.pdfreader.pdf.reader.ui.dialog.PdfPasswordProtectionDialogFragmen import com.all.pdfreader.pdf.reader.util.AppUtils.setOnSingleClickListener import com.all.pdfreader.pdf.reader.util.FileUtils.isPdfEncrypted import com.all.pdfreader.pdf.reader.util.PdfUtils +import com.all.pdfreader.pdf.reader.util.ToastUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -55,8 +56,8 @@ class PdfToImageActivity : BaseActivity() { statusBarDarkFont(true) navigationBarColor(R.color.bg_color) } - filePath = intent.getStringExtra(EXTRA_PDF_PATH) - ?: throw IllegalArgumentException("PDF file hash is required") + filePath = intent.getStringExtra(EXTRA_PDF_PATH) ?: "" + source = getSerializableOrDefault(EXTRA_SOURCE, PdfPickerSource.NONE) if (filePath.isEmpty()) { showToast(getString(R.string.file_not)) @@ -153,8 +154,18 @@ class PdfToImageActivity : BaseActivity() { PdfUtils.splitPdfToPageItemsFlow( context = this@PdfToImageActivity, inputFile = file, - password = password + password = password, + onError = { e -> + runOnUiThread { + ToastUtils.show( + this@PdfToImageActivity, + getString(R.string.file_not_pdf_or_corrupted) + ) + finish() + } + } ).collect { pageItem -> + logDebug("splitPdfToPageItemsFlow pageItem->$pageItem") if (pdfPageList.size <= pageItem.pageIndex) { pdfPageList.add(pageItem) diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt index bc55389..97db171 100644 --- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/PdfViewActivity.kt @@ -49,12 +49,12 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList get() = binding.rootBottomLayout companion object { const val FRAG_TAG = "PdfViewActivity" - private const val EXTRA_PDF_HASH = "extra_pdf_hash" + private const val EXTRA_PDF_FILE_PATH = "extra_pdf_file_path" // 创建启动Intent的便捷方法 fun createIntent(context: Context, filePath: String): Intent { return Intent(context, PdfViewActivity::class.java).apply { - putExtra(EXTRA_PDF_HASH, filePath) + putExtra(EXTRA_PDF_FILE_PATH, filePath) } } } @@ -81,9 +81,12 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList navigationBarColor(R.color.white) } setupDoubleBackExit() + val filePath = intent.getStringExtra(EXTRA_PDF_FILE_PATH) ?: "" + if (filePath.isEmpty()) { + showToast(getString(R.string.file_not)) + finish() + } initObserve() - val filePath = intent.getStringExtra(EXTRA_PDF_HASH) - ?: throw IllegalArgumentException("PDF file hash is required") // 加载PDF数据 viewModel.getPDFDocument(filePath) //加载书签数据 diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt index 59b9b4a..3b66bed 100644 --- a/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pdf/reader/ui/act/SplitPdfActivity.kt @@ -23,6 +23,7 @@ import com.all.pdfreader.pdf.reader.util.AppUtils.setOnSingleClickListener import com.all.pdfreader.pdf.reader.util.FileUtils.isPdfEncrypted import com.all.pdfreader.pdf.reader.util.FileUtils.toUnderscoreDateTime import com.all.pdfreader.pdf.reader.util.PdfUtils +import com.all.pdfreader.pdf.reader.util.ToastUtils import com.gyf.immersionbar.ImmersionBar import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -35,7 +36,7 @@ class SplitPdfActivity : BaseActivity() { get() = binding.rootBottomLayout private var currentViewState: ViewState = ViewState.SPLIT_LIST - var currentPassword : String? = null//拆分只会选择一个文件。直接进行密码传递 + var currentPassword: String? = null//拆分只会选择一个文件。直接进行密码传递 private enum class ViewState { SPLIT_LIST, // 拆分页列表 @@ -69,8 +70,7 @@ class SplitPdfActivity : BaseActivity() { statusBarDarkFont(true) navigationBarColor(R.color.bg_color) } - filePath = intent.getStringExtra(EXTRA_PDF_PATH) - ?: throw IllegalArgumentException("PDF file hash is required") + filePath = intent.getStringExtra(EXTRA_PDF_PATH) ?: "" if (filePath.isEmpty()) { showToast(getString(R.string.file_not)) finish() @@ -174,7 +174,7 @@ class SplitPdfActivity : BaseActivity() { } private fun initSplitDataWithPassword(file: File, password: String? = null) { - if(!password.isNullOrEmpty()){ + if (!password.isNullOrEmpty()) { currentPassword = password } lifecycleScope.launch { @@ -188,7 +188,16 @@ class SplitPdfActivity : BaseActivity() { PdfUtils.splitPdfToPageItemsFlow( context = this@SplitPdfActivity, inputFile = file, - password = password + password = password, + onError = { e -> + runOnUiThread { + ToastUtils.show( + this@SplitPdfActivity, + getString(R.string.file_not_pdf_or_corrupted) + ) + finish() + } + } ).collect { pageItem -> logDebug("splitPdfToPageItemsFlow pageItem->$pageItem") if (splitList.size <= pageItem.pageIndex) { diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/util/FileUtils.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/util/FileUtils.kt index a2a9438..0d025d8 100644 --- a/app/src/main/java/com/all/pdfreader/pdf/reader/util/FileUtils.kt +++ b/app/src/main/java/com/all/pdfreader/pdf/reader/util/FileUtils.kt @@ -518,24 +518,10 @@ object FileUtils { } fun getFileFromUri(context: Context, uri: Uri): File? { - // 先尝试通过 DATA 字段获取 - val projection = arrayOf(MediaStore.Files.FileColumns.DATA) - context.contentResolver.query(uri, projection, null, null, null)?.use { cursor -> - if (cursor.moveToFirst()) { - val path = - cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)) - if (!path.isNullOrEmpty()) { - val file = File(path) - if (file.exists()) { - return file - } - } - } - } - - // Android 10+ 或 DATA 字段为空时,使用 ContentResolver 流访问 return try { - val tempFile = File(context.cacheDir, "temp_pdf_${System.currentTimeMillis()}.pdf") + val fileName = getFileName(context, uri) ?: "temp_${System.currentTimeMillis()}" + val tempFile = File(context.cacheDir, fileName) + context.contentResolver.openInputStream(uri)?.use { input -> tempFile.outputStream().use { output -> input.copyTo(output) @@ -543,11 +529,28 @@ object FileUtils { } tempFile } catch (e: Exception) { -// Log.e("ocean", "无法获取文件: ${e.message}", e) + e.printStackTrace() null } } + private fun getFileName(context: Context, uri: Uri): String? { + return try { + val cursor = context.contentResolver.query(uri, + arrayOf(MediaStore.Files.FileColumns.DISPLAY_NAME), + null, null, null) + cursor?.use { + if (it.moveToFirst()) { + return it.getString(0) + } + } + null + } catch (e: Exception) { + null + } + } + + fun duplicateFile(originalFile: File): File? { if (!originalFile.exists()) return null diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/util/PDFSearchManager.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/util/PDFSearchManager.kt index 3c2c3fd..fab849a 100644 --- a/app/src/main/java/com/all/pdfreader/pdf/reader/util/PDFSearchManager.kt +++ b/app/src/main/java/com/all/pdfreader/pdf/reader/util/PDFSearchManager.kt @@ -86,7 +86,8 @@ class PDFSearchManager(private val pdfView: PDFView) { onLoaded?.invoke(cachedDoc!!) } catch (e: Exception) { onError?.invoke(e) - throw e + cachedDoc = null + cachedPath = null } } else { // 已缓存文档,仍可调用回调 diff --git a/app/src/main/java/com/all/pdfreader/pdf/reader/util/PdfUtils.kt b/app/src/main/java/com/all/pdfreader/pdf/reader/util/PdfUtils.kt index 8d16f97..7b7da57 100644 --- a/app/src/main/java/com/all/pdfreader/pdf/reader/util/PdfUtils.kt +++ b/app/src/main/java/com/all/pdfreader/pdf/reader/util/PdfUtils.kt @@ -52,53 +52,68 @@ object PdfUtils { dpi: Float = 72f, chunkSize: Int = 5, thumbWidth: Int = 200, - password: String? = null + password: String? = null, + onError: ((Exception) -> Unit)? = null ): Flow = flow { val pdfiumCore = PdfiumCore(context) - ParcelFileDescriptor.open(inputFile, ParcelFileDescriptor.MODE_READ_ONLY).use { fd -> - val pdfDocument = if (password.isNullOrEmpty()) { - pdfiumCore.newDocument(fd) - } else { - pdfiumCore.newDocument(fd, password) - } - val pageCount = pdfiumCore.getPageCount(pdfDocument) - - val pages = List(pageCount) { - PdfPageItem(pageIndex = it, previewFilePath = null, isSelected = false) - } - - // 先 emit 页对象,让 UI 知道总页数 - pages.forEach { emit(it) } - - val cacheDir = File(context.cacheDir, child).apply { mkdirs() } - - for (i in 0 until pageCount) { - pdfiumCore.openPage(pdfDocument, i) - - val width = (pdfiumCore.getPageWidthPoint(pdfDocument, i) * dpi / 72).toInt() - val height = (pdfiumCore.getPageHeightPoint(pdfDocument, i) * dpi / 72).toInt() - - val scale = thumbWidth.toFloat() / width - val targetHeight = (height * scale).toInt() - - val bitmap = createBitmap(thumbWidth, targetHeight, Bitmap.Config.RGB_565) - pdfiumCore.renderPageBitmap(pdfDocument, bitmap, i, 0, 0, thumbWidth, targetHeight) - - // 保存为压缩 JPEG - val outFile = File(cacheDir, inputFile.name + "_page_$i.jpg") - FileOutputStream(outFile).use { fos -> - bitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos) + try { + ParcelFileDescriptor.open(inputFile, ParcelFileDescriptor.MODE_READ_ONLY).use { fd -> + val pdfDocument = try { + if (password.isNullOrEmpty()) { + pdfiumCore.newDocument(fd) + } else { + pdfiumCore.newDocument(fd, password) + } + } catch (e: Exception) { + onError?.invoke(e) + return@flow // 直接退出 flow,不崩溃 } - bitmap.recycle() - pages[i].previewFilePath = outFile.absolutePath - emit(pages[i]) + val pageCount = pdfiumCore.getPageCount(pdfDocument) + val pages = List(pageCount) { + PdfPageItem(pageIndex = it, previewFilePath = null, isSelected = false) + } - if ((i + 1) % chunkSize == 0) delay(50) // 分批渲染,保证 UI 流畅 + // 先 emit 页对象,让 UI 知道总页数 + pages.forEach { emit(it) } + + val cacheDir = File(context.cacheDir, child).apply { mkdirs() } + + for (i in 0 until pageCount) { + try { + pdfiumCore.openPage(pdfDocument, i) + + val width = (pdfiumCore.getPageWidthPoint(pdfDocument, i) * dpi / 72).toInt() + val height = (pdfiumCore.getPageHeightPoint(pdfDocument, i) * dpi / 72).toInt() + + val scale = thumbWidth.toFloat() / width + val targetHeight = (height * scale).toInt() + + val bitmap = createBitmap(thumbWidth, targetHeight, Bitmap.Config.RGB_565) + pdfiumCore.renderPageBitmap(pdfDocument, bitmap, i, 0, 0, thumbWidth, targetHeight) + + val outFile = File(cacheDir, inputFile.name + "_page_$i.jpg") + FileOutputStream(outFile).use { fos -> + bitmap.compress(Bitmap.CompressFormat.JPEG, 70, fos) + } + bitmap.recycle() + + pages[i].previewFilePath = outFile.absolutePath + emit(pages[i]) + + if ((i + 1) % chunkSize == 0) delay(50) + } catch (e: Exception) { + // 单页渲染失败也不崩溃,只提示 + onError?.invoke(e) + } + } + + pdfiumCore.closeDocument(pdfDocument) } - - pdfiumCore.closeDocument(pdfDocument) + } catch (e: Exception) { + // 打开文件失败或 PFDium 异常 + onError?.invoke(e) } }.flowOn(Dispatchers.IO) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dbd2bf7..3774ae1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -193,4 +193,5 @@ Save to Album 1 image converted %1$d imagesF converted + File not in PDF format or corrupted \ No newline at end of file