diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 29ec8d2..afd2cb2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -73,6 +73,12 @@
android:label="@string/app_name"
android:screenOrientation="portrait" />
+
+
@@ -143,7 +160,7 @@ class SplitPdfActivity : BaseActivity() {
splitList[pageItem.pageIndex] = pageItem
adapter.updateItem(pageItem.pageIndex)
}
- if (!firstPageLoaded) {//有数据回来则隐藏loading,显示全选按钮
+ if (!firstPageLoaded) {
binding.loadingRoot.root.visibility = View.GONE
binding.selectAllBtn.visibility = View.VISIBLE
firstPageLoaded = true
@@ -176,10 +193,12 @@ class SplitPdfActivity : BaseActivity() {
private fun updateViewState(b: Boolean) {
if (b) {
+ currentViewState = ViewState.SPLIT_SELECTED
binding.selectAllBtn.visibility = View.GONE//隐藏全选按钮
binding.addBtn.visibility = View.VISIBLE//显示add按钮
binding.title.text = getString(R.string.split_pdf)//设置标题
} else {
+ currentViewState = ViewState.SPLIT_LIST
binding.selectAllBtn.visibility = View.VISIBLE
binding.addBtn.visibility = View.GONE
binding.title.text =
@@ -221,26 +240,37 @@ class SplitPdfActivity : BaseActivity() {
override fun onDestroy() {
super.onDestroy()
- splitList.clear()
- PdfUtils.clearPdfThumbsCache(this)
}
private fun setupBackPressedCallback() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
- if (isSelectedViewShow) {
- //使用提示对话框
- PromptDialogFragment(
- getString(R.string.exit_split),
- getString(R.string.confirm_discard_changes),
- getString(R.string.discard),
- onOkClick = {
- isEnabled = false
- onBackPressedDispatcher.onBackPressed()
- }).show(supportFragmentManager, getString(R.string.exit_split))
- } else {
+ if (!isSelectedViewShow) {//点击继续后,说明已经操过过了,不直接finish
isEnabled = false
onBackPressedDispatcher.onBackPressed()
+ return
+ }
+ when (currentViewState) {
+ ViewState.SPLIT_SELECTED -> {
+ // 已选页面列表,提示是否退出
+ PromptDialogFragment(
+ getString(R.string.exit_split),
+ getString(R.string.confirm_discard_changes),
+ getString(R.string.discard),
+ onOkClick = {
+ isEnabled = false
+ onBackPressedDispatcher.onBackPressed()
+ }).show(supportFragmentManager, getString(R.string.exit_split))
+ }
+
+ ViewState.SPLIT_LIST -> {
+ // 拆分页列表,返回到已选页面列表
+ adapter.setAllSelected(false)//重置所有选中状态为false
+ updateSelectAllState(false)//重置选中按钮
+ binding.continueNowBtn.isEnabled = false//继续按钮不可点击
+ updateContinueNowBtnState(false)//重置继续按钮背景
+ updateViewState(true)
+ }
}
}
})
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/SplitPdfResultActivity.kt
index 21bcbd0..6da8d6b 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/SplitPdfResultActivity.kt
@@ -2,11 +2,17 @@ package com.all.pdfreader.pro.app.ui.act
import android.content.Context
import android.content.Intent
+import android.os.Build
import android.os.Bundle
+import android.os.Environment
+import android.os.Parcelable
+import android.view.View
+import androidx.activity.OnBackPressedCallback
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
+import com.all.pdfreader.pro.app.PRApp
import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.ActivityPdfSplitResultBinding
import com.all.pdfreader.pro.app.model.PdfPageItem
@@ -14,9 +20,14 @@ import com.all.pdfreader.pro.app.model.PdfSelectedPagesItem
import com.all.pdfreader.pro.app.model.PdfSplitResultItem
import com.all.pdfreader.pro.app.ui.act.SplitPdfActivity
import com.all.pdfreader.pro.app.ui.adapter.SplitPdfResultAdapter
+import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment
+import com.all.pdfreader.pro.app.util.AppUtils
+import com.all.pdfreader.pro.app.util.PdfScanner
import com.all.pdfreader.pro.app.util.PdfUtils
import com.gyf.immersionbar.ImmersionBar
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.io.File
class SplitPdfResultActivity : BaseActivity() {
@@ -26,11 +37,10 @@ class SplitPdfResultActivity : BaseActivity() {
private const val EXTRA_SELECTED_LIST = "extra_selected_list"
fun createIntent(
- context: Context,
- selectedPageIndices: List
+ context: Context, list: ArrayList
): Intent {
return Intent(context, SplitPdfResultActivity::class.java).apply {
-
+ putParcelableArrayListExtra(EXTRA_SELECTED_LIST, list)
}
}
}
@@ -38,15 +48,24 @@ class SplitPdfResultActivity : BaseActivity() {
private lateinit var binding: ActivityPdfSplitResultBinding
private lateinit var adapter: SplitPdfResultAdapter
private var splitResultList: MutableList = mutableListOf()
+ private lateinit var selectedList: ArrayList
+ private var isSplitting = false
+ private var exitDialog: PromptDialogFragment? = null
+ private val pdfRepository = getRepository()
+ private lateinit var pdfScanner: PdfScanner
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPdfSplitResultBinding.inflate(layoutInflater)
setContentView(binding.root)
+ setupBackPressedCallback()
ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
.navigationBarColor(R.color.bg_color).init()
+ selectedList = requireParcelableArrayList(EXTRA_SELECTED_LIST)
+ pdfScanner = PdfScanner(this, pdfRepository)
initView()
setupClick()
+ initData()
}
private fun initView() {
@@ -55,25 +74,80 @@ class SplitPdfResultActivity : BaseActivity() {
binding.recyclerView.adapter = adapter
}
- private fun startSplittingPDF(
- inputFile: File, selectedPages: List, outputDir: File, outputFileName: String
- ) {
- lifecycleScope.launch {
- lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
- val resultFile = PdfUtils.exportSelectedPages(
+ private fun initData() {
+ lifecycleScope.launch(Dispatchers.IO) {
+ val totalPages = selectedList.sumOf { it.pages.count { it.isSelected } }
+ var processedPages = 0
+
+ withContext(Dispatchers.Main) {
+ isSplitting = true
+ binding.splittingLayout.visibility = View.VISIBLE
+ binding.progressBar.isIndeterminate = false
+ binding.progressBar.progress = 0
+ binding.progressBar.max = 100
+ }
+
+ for (item in selectedList) {
+ val selectedPages = item.pages.filter { it.isSelected }
+ if (selectedPages.isEmpty()) continue
+
+ val inputFile = File(item.filePath)
+ val outputDir = File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+ "PDFReaderPro/split"
+ ).apply { if (!exists()) mkdirs() }
+
+ PdfUtils.exportSelectedPages(
inputFile = inputFile,
selectedPages = selectedPages,
outputDir = outputDir,
- outputFileName = outputFileName,
- onProgress = { current, total ->
-
- })
+ outputFileName = "${item.fileName}.pdf",
+ onProgress = { _, _ -> // 不需要单文件百分比
+ processedPages++// 每页处理完成就加一,多个 PDF 顺序处理时,总进度线性递增
+ val percent = (processedPages.toFloat() / totalPages * 100).toInt()
+ lifecycleScope.launch(Dispatchers.Main) {
+ binding.progressTv.text = "$percent"
+ binding.progressBar.progress = percent
+ }
+ })?.let { resultFile ->
+ val thumbnails =
+ AppUtils.generateFastThumbnail(this@SplitPdfResultActivity, resultFile)
+ val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false)
+ pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) {
+ splitResultList.add(result)
+ }
+ }
+ }
+ withContext(Dispatchers.Main) {
+ binding.splittingLayout.visibility = View.GONE
+ // 默认选中第一个
+ if (splitResultList.isNotEmpty() && splitResultList.none { it.isSelected }) {
+ splitResultList[0].isSelected = true
+ }
+ adapter.updateAdapter()
+ isSplitting = false//拆分结束
+ exitDialog?.dismissAllowingStateLoss()
+ exitDialog = null
}
}
}
private fun setupClick() {
binding.backBtn.setOnClickListener {
+ onBackPressedDispatcher.onBackPressed()
+ }
+ binding.shareBtn.setOnClickListener {
+ val selectedItem = adapter.getSelectedItem()
+ selectedItem?.let {
+ AppUtils.shareFile(this, File(selectedItem.filePath))
+ }
+ }
+ binding.okBtn.setOnClickListener {
+ val selectedItem = adapter.getSelectedItem()
+ selectedItem?.let {
+ val intent = PdfViewActivity.createIntent(this, selectedItem.filePath)
+ startActivity(intent)
+ }
finish()
}
}
@@ -82,4 +156,42 @@ class SplitPdfResultActivity : BaseActivity() {
super.onDestroy()
}
+ private fun setupBackPressedCallback() {
+ onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ if (isSplitting) {
+ exitDialog = PromptDialogFragment(
+ getString(R.string.exit_split),
+ getString(R.string.confirm_discard_changes),
+ getString(R.string.discard),
+ onOkClick = {
+ isEnabled = false
+ onBackPressedDispatcher.onBackPressed()
+ })
+ exitDialog?.show(supportFragmentManager, getString(R.string.exit_split))
+ } else {
+ isEnabled = false
+ onBackPressedDispatcher.onBackPressed()
+ }
+ }
+ })
+ }
+
+ /**
+ * 通用方法:读取必传参数,如果为 null 直接 finish
+ */
+ @Suppress("DEPRECATION")
+ private inline fun requireParcelableArrayList(key: String): ArrayList {
+ val result: ArrayList? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ intent.getParcelableArrayListExtra(key, T::class.java)
+ } else {
+ intent.getParcelableArrayListExtra(key)
+ }
+
+ if (result.isNullOrEmpty()) {
+ showToast(getString(R.string.pdf_loading_failed))
+ finish()
+ }
+ return result ?: arrayListOf()
+ }
}
\ No newline at end of file
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/SplitPdfResultAdapter.kt
index 08a9bac..701f572 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/SplitPdfResultAdapter.kt
@@ -43,6 +43,20 @@ class SplitPdfResultAdapter(
} else {
holder.binding.selectIv.visibility = View.INVISIBLE
}
+ holder.binding.root.setOnClickListener {
+ val oldSelectedIndex = list.indexOfFirst { it.isSelected }
+ val newSelectedIndex = holder.bindingAdapterPosition
+ if (oldSelectedIndex != newSelectedIndex && newSelectedIndex != RecyclerView.NO_POSITION) {
+ // 取消之前选中
+ if (oldSelectedIndex >= 0) {
+ list[oldSelectedIndex].isSelected = false
+ notifyItemChanged(oldSelectedIndex)
+ }
+ // 选中当前
+ list[newSelectedIndex].isSelected = true
+ notifyItemChanged(newSelectedIndex)
+ }
+ }
}
override fun getItemCount(): Int = list.size
@@ -52,7 +66,7 @@ class SplitPdfResultAdapter(
notifyDataSetChanged()
}
- fun updateItem(position: Int) = notifyItemChanged(position)
-
- fun removeItem(position: Int) = notifyItemRemoved(position)
+ fun getSelectedItem(): PdfSplitResultItem? {
+ return list.firstOrNull { it.isSelected }
+ }
}
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt
index 5996ca0..cc535ff 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt
@@ -70,8 +70,7 @@ class PdfScanner(
val newThumbnail = generateFastThumbnail(context, file)
if (newThumbnail != null && doc.thumbnailPath != newThumbnail) {
pdfRepository.updateThumbnailPath(
- doc.filePath,
- newThumbnail
+ doc.filePath, newThumbnail
)
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
}
@@ -136,8 +135,7 @@ class PdfScanner(
val newThumbnail = generateFastThumbnail(context, file)
if (newThumbnail != null && document.thumbnailPath != newThumbnail) {
pdfRepository.updateThumbnailPath(
- document.filePath,
- newThumbnail
+ document.filePath, newThumbnail
)
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
}
@@ -162,8 +160,7 @@ class PdfScanner(
val currentIsPassword = isPdfEncrypted(file)
if (existingDoc.isPassword != currentIsPassword) {
LogUtil.logDebug(TAG, "✅ 密码状态需要更新")
- updatedDoc =
- updatedDoc.copy(isPassword = currentIsPassword)
+ updatedDoc = updatedDoc.copy(isPassword = currentIsPassword)
needUpdate = true
}
@@ -180,8 +177,7 @@ class PdfScanner(
val newThumbnail = generateFastThumbnail(context, file)
if (newThumbnail != null && existingDoc.thumbnailPath != newThumbnail) {
pdfRepository.updateThumbnailPath(
- existingDoc.filePath,
- newThumbnail
+ existingDoc.filePath, newThumbnail
)
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
}
@@ -194,7 +190,9 @@ class PdfScanner(
val file = File(doc.filePath)
if (!file.exists()) {
// 文件不存在 → 删除数据库记录
- LogUtil.logDebug(TAG, "最终过滤:文件不存在 -> ${doc.fileName}, 删除记录")
+ LogUtil.logDebug(
+ TAG, "最终过滤:文件不存在 -> ${doc.fileName}, 删除记录"
+ )
pdfRepository.deleteDocument(doc.filePath)
} else {
LogUtil.logDebug(
@@ -240,16 +238,62 @@ class PdfScanner(
}
}
- fun shouldScan(): Boolean {
- return ScanManager.shouldScan(context)
+ /**
+ * 添加单个pdf文件到数据库
+ */
+ suspend fun addNewPdfToDatabase(
+ pathFile: String,
+ thumbnailPath: String?,
+ callback: (Boolean) -> Unit = {}
+ ) {
+ val file = File(pathFile)
+ if (!file.exists() || !FileUtils.isPdfFile(file)) {
+ LogUtil.logDebug(TAG, "文件不存在或不是PDF: $pathFile")
+ withContext(Dispatchers.Main) {
+ callback.invoke(false)
+ }
+ return
+ }
+
+ try {
+ withContext(Dispatchers.IO) {
+ val existingDoc = pdfRepository.getDocumentByPath(file.absolutePath)
+ if (existingDoc == null) {
+ val isPassword = isPdfEncrypted(file)
+ val metadata = PdfMetadataExtractor.extractMetadata(file.absolutePath)
+
+ val document = PdfDocumentEntity(
+ filePath = file.absolutePath,
+ fileName = file.name,
+ fileSize = file.length(),
+ lastModified = file.lastModified(),
+ pageCount = metadata?.pageCount ?: 0,
+ thumbnailPath = thumbnailPath,
+ metadataTitle = metadata?.title,
+ metadataAuthor = metadata?.author,
+ metadataSubject = metadata?.subject,
+ metadataKeywords = metadata?.keywords,
+ metadataCreationDate = metadata?.creationDate?.time,
+ metadataModificationDate = metadata?.modificationDate?.time,
+ isPassword = isPassword
+ )
+
+ pdfRepository.insertOrUpdateDocument(document)
+ LogUtil.logDebug(TAG, "✅ 新PDF已保存到数据库: ${file.name}")
+
+ withContext(Dispatchers.Main) {
+ callback.invoke(true)
+ }
+ } else {
+ Log.e(TAG, "数据库有已有相同的文件")
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "❌ 添加新PDF出错: ${e.message}", e)
+ withContext(Dispatchers.Main) {
+ callback.invoke(false)
+ }
+ }
}
- fun getLastScanTime(): Long {
- return ScanManager.getLastScanTime(context)
- }
-
- fun getHoursSinceLastScan(): Long {
- val lastScan = getLastScanTime()
- return TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis() - lastScan)
- }
}
\ No newline at end of file
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 3abdbfc..53bdc8e 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
@@ -133,5 +133,4 @@ object PdfUtils {
null
}
}
-
}
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 bb11106..90f4f80 100644
--- a/app/src/main/res/layout/activity_pdf_split_result.xml
+++ b/app/src/main/res/layout/activity_pdf_split_result.xml
@@ -11,6 +11,57 @@
android:layout_width="match_parent"
android:layout_height="0dp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5990f19..4ffd7fa 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -15,10 +15,11 @@
Permission is required to access files
Cancel
OK
+ Open
Sort by
Created Date
Path
- Path %1$s
+ Path: %1$s
File Name
File Size
Ascending
@@ -133,4 +134,5 @@
Congratulations
Your file has been successfully created
Please select at least one page
+ Splitting…
\ No newline at end of file