From 06a2720547ccbb3af304a9ba96177b24cd2957e1 Mon Sep 17 00:00:00 2001
From: ocean <503259349@qq.com>
Date: Tue, 23 Sep 2025 10:21:17 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8B=86=E5=88=86pdf?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA=E7=95=8C=E9=9D=A2=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/build.gradle.kts | 1 +
app/src/main/AndroidManifest.xml | 6 +
.../java/com/all/pdfreader/pro/app/PRApp.kt | 3 +
.../pdfreader/pro/app/model/PdfPageItem.kt | 10 ++
.../pro/app/ui/act/SplitPdfActivity.kt | 112 +++++++++++++++
.../pro/app/ui/adapter/SplitPdfAdapter.kt | 106 ++++++++++++++
.../app/ui/dialog/ListMoreDialogFragment.kt | 18 ++-
.../all/pdfreader/pro/app/util/PdfUtils.kt | 129 ++++++++++++++++++
app/src/main/res/drawable/back_black.xml | 2 +-
app/src/main/res/drawable/bg_loading.xml | 5 +
.../res/drawable/dr_circular_sel_off_bg.xml | 5 +
.../res/drawable/dr_circular_sel_on_bg.xml | 5 +
.../drawable/dr_item_page_img_sel_off_bg.xml | 8 ++
.../drawable/dr_item_page_img_sel_on_bg.xml | 8 ++
.../dr_item_page_number_sel_off_bg.xml | 8 ++
.../dr_item_page_number_sel_on_bg.xml | 8 ++
.../main/res/drawable/dr_placeholder_bg.xml | 6 +
.../main/res/drawable/dr_sel_off_frame.xml | 7 +
app/src/main/res/drawable/dr_sel_on_frame.xml | 7 +
app/src/main/res/drawable/file_document.xml | 9 ++
app/src/main/res/drawable/gou_white.xml | 9 ++
app/src/main/res/drawable/icon_split.xml | 9 ++
app/src/main/res/drawable/lock.xml | 12 +-
.../main/res/layout/activity_pdf_split.xml | 106 ++++++++++++++
.../res/layout/adapter_split_page_item.xml | 58 ++++++++
app/src/main/res/layout/dialog_list_more.xml | 22 +++
app/src/main/res/layout/layout_loading.xml | 38 ++++++
app/src/main/res/values/colors.xml | 1 +
app/src/main/res/values/strings.xml | 5 +
gradle/libs.versions.toml | 2 +
30 files changed, 715 insertions(+), 10 deletions(-)
create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/model/PdfPageItem.kt
create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/ui/act/SplitPdfActivity.kt
create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitPdfAdapter.kt
create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt
create mode 100644 app/src/main/res/drawable/bg_loading.xml
create mode 100644 app/src/main/res/drawable/dr_circular_sel_off_bg.xml
create mode 100644 app/src/main/res/drawable/dr_circular_sel_on_bg.xml
create mode 100644 app/src/main/res/drawable/dr_item_page_img_sel_off_bg.xml
create mode 100644 app/src/main/res/drawable/dr_item_page_img_sel_on_bg.xml
create mode 100644 app/src/main/res/drawable/dr_item_page_number_sel_off_bg.xml
create mode 100644 app/src/main/res/drawable/dr_item_page_number_sel_on_bg.xml
create mode 100644 app/src/main/res/drawable/dr_placeholder_bg.xml
create mode 100644 app/src/main/res/drawable/dr_sel_off_frame.xml
create mode 100644 app/src/main/res/drawable/dr_sel_on_frame.xml
create mode 100644 app/src/main/res/drawable/file_document.xml
create mode 100644 app/src/main/res/drawable/gou_white.xml
create mode 100644 app/src/main/res/drawable/icon_split.xml
create mode 100644 app/src/main/res/layout/activity_pdf_split.xml
create mode 100644 app/src/main/res/layout/adapter_split_page_item.xml
create mode 100644 app/src/main/res/layout/layout_loading.xml
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 0549fdd..c89c917 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -61,4 +61,5 @@ dependencies {
implementation(libs.glide)
implementation(libs.androidpdfviewer)
implementation(libs.pdfbox.android)
+ implementation(libs.jp2forandroid)
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 069e4a1..29ec8d2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -67,6 +67,12 @@
+
+
= mutableListOf()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityPdfSplitBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
+ .navigationBarColor(R.color.white).init()
+ val filePath = intent.getStringExtra(EXTRA_PDF_PATH)
+ ?: throw IllegalArgumentException("PDF file hash is required")
+ if (filePath.isEmpty()) {
+ showToast(getString(R.string.file_not))
+ finish()
+ }
+ initView()
+ setupClick()
+ initSplitData(File(filePath))
+ }
+
+ private fun initView() {
+ binding.title.text = getString(R.string.selected_page, 0)
+ binding.selectAllBtn.visibility = View.GONE
+ binding.loadingRoot.root.visibility = View.VISIBLE
+ binding.splitRv.layoutManager = GridLayoutManager(this, 2)
+ adapter = SplitPdfAdapter(splitList) { item, pos ->
+ item.isSelected = !item.isSelected
+ adapter.setItemSelected(pos, item.isSelected)
+ binding.title.text = getString(R.string.selected_page, adapter.getSelPages())
+ }
+ binding.splitRv.adapter = adapter
+ }
+
+ private fun setupClick() {
+ binding.backBtn.setOnClickListener { finish() }
+ binding.selectAllBtn.setOnClickListener {
+ val selectAll = splitList.any { !it.isSelected }
+ adapter.setAllSelected(selectAll)
+ binding.title.text = getString(R.string.selected_page, adapter.getSelPages())
+
+ if (selectAll) {
+ binding.selectAll.setBackgroundResource(R.drawable.dr_circular_sel_on_bg)
+ } else {
+ binding.selectAll.setBackgroundResource(R.drawable.dr_circular_sel_off_bg)
+ }
+ }
+ }
+
+ private fun initSplitData(file: File) {
+ lifecycleScope.launch {
+ PdfUtils.clearPdfThumbsCache(this@SplitPdfActivity) // 先清理旧缓存
+ lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ var firstPageLoaded = false
+ PdfUtils.splitPdfToPageItemsFlow(this@SplitPdfActivity, file).collect { pageItem ->
+ logDebug("pageItem flow ->$pageItem")
+ if (splitList.size <= pageItem.pageIndex) {
+ splitList.add(pageItem)
+ adapter.notifyItemInserted(splitList.size - 1)
+ } else {
+ splitList[pageItem.pageIndex] = pageItem
+ adapter.updateItem(pageItem.pageIndex)
+ }
+ if (!firstPageLoaded) {
+ binding.loadingRoot.root.visibility = View.GONE
+ binding.selectAllBtn.visibility = View.VISIBLE
+ firstPageLoaded = true
+ }
+ }
+ binding.loadingRoot.root.visibility = View.GONE
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ splitList.clear()
+ PdfUtils.clearPdfThumbsCache(this)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitPdfAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitPdfAdapter.kt
new file mode 100644
index 0000000..44dc759
--- /dev/null
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitPdfAdapter.kt
@@ -0,0 +1,106 @@
+package com.all.pdfreader.pro.app.ui.adapter
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.all.pdfreader.pro.app.R
+import com.all.pdfreader.pro.app.databinding.AdapterSplitPageItemBinding
+import com.all.pdfreader.pro.app.model.PdfPageItem
+import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import java.io.File
+
+class SplitPdfAdapter(
+ private val pdfList: MutableList,
+ private val onItemClick: (PdfPageItem, Int) -> Unit
+) : RecyclerView.Adapter() {
+
+ inner class PdfViewHolder(val binding: AdapterSplitPageItemBinding) :
+ RecyclerView.ViewHolder(binding.root)
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PdfViewHolder(
+ AdapterSplitPageItemBinding.inflate(
+ LayoutInflater.from(parent.context), parent, false
+ )
+ )
+
+ @SuppressLint("SetTextI18n")
+ override fun onBindViewHolder(holder: PdfViewHolder, position: Int) {
+ bindAll(holder, position)
+ }
+
+ // 支持 payload,局部刷新
+ override fun onBindViewHolder(
+ holder: PdfViewHolder, position: Int, payloads: MutableList
+ ) {
+ if (payloads.isEmpty()) {
+ bindAll(holder, position) // 全量刷新
+ } else {
+ if (payloads.any { it == "selection" }) {
+ bindSelectionOnly(holder, position)
+ }
+ }
+ }
+
+ @SuppressLint("SetTextI18n")
+ private fun bindAll(holder: PdfViewHolder, position: Int) {
+ val item = pdfList[position]
+ holder.binding.apply {
+ //更新文字图片与选中状态
+ pageNumberTv.text = "${item.pageIndex + 1}"
+ Glide.with(holder.binding.root)
+ .load(File(item.previewFilePath ?: ""))
+ .transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context)))
+ .into(image)
+ bindSelection(item, holder)
+ }
+ holder.binding.root.setOnClickListener {
+ val pos = holder.bindingAdapterPosition
+ if (pos != RecyclerView.NO_POSITION) {
+ onItemClick(pdfList[pos], pos)
+ }
+ }
+ }
+
+ private fun bindSelectionOnly(holder: PdfViewHolder, position: Int) {
+ val item = pdfList[position]
+ bindSelection(item, holder)
+ }
+
+ //更新选中状态
+ private fun bindSelection(item: PdfPageItem, holder: PdfViewHolder) {
+ val b = holder.binding
+ val selRes =
+ if (item.isSelected) R.drawable.dr_circular_sel_on_bg else R.drawable.dr_circular_sel_off_bg
+ b.selIv.setBackgroundResource(selRes)
+
+ val bgRes = if (item.isSelected) R.drawable.dr_sel_on_frame else R.drawable.dr_sel_off_frame
+ b.itemBgLayout.setBackgroundResource(bgRes)
+
+ val pageBgRes =
+ if (item.isSelected) R.drawable.dr_item_page_number_sel_on_bg else R.drawable.dr_item_page_number_sel_off_bg
+ b.pageNumberTv.setBackgroundResource(pageBgRes)
+ }
+
+
+ override fun getItemCount() = pdfList.size
+
+ fun updateItem(position: Int) = notifyItemChanged(position)
+
+ // 只刷新选中状态
+ fun setItemSelected(position: Int, selected: Boolean) {
+ pdfList[position].isSelected = selected
+ notifyItemChanged(position, "selection") // 这里的 payload 就是标记
+ }
+
+ fun getSelPages() = pdfList.count { it.isSelected }
+
+ // 批量更新选中状态,用 payload 避免图片重新加载
+ fun setAllSelected(selected: Boolean) {
+ pdfList.forEach { it.isSelected = selected }
+ notifyItemRangeChanged(0, pdfList.size, "selection")
+ }
+}
\ No newline at end of file
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 1309f6e..9cfcea1 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
@@ -1,6 +1,7 @@
package com.all.pdfreader.pro.app.ui.dialog
import android.net.Uri
+import android.nfc.Tag
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -12,6 +13,8 @@ import com.all.pdfreader.pro.app.databinding.DialogListMoreBinding
import com.all.pdfreader.pro.app.model.PrintResult
import com.all.pdfreader.pro.app.model.RenameType
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
+import com.all.pdfreader.pro.app.ui.act.SplitPdfActivity
+import com.all.pdfreader.pro.app.ui.fragment.HomeFrag
import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
import com.all.pdfreader.pro.app.util.AppUtils.printPdfFile
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
@@ -71,10 +74,14 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment()
}
private fun initUi() {
- if (pdfDocument.lastOpenedTime > 0) {
- binding.removeRecentBtn.visibility = View.VISIBLE
- } else {
+ if (tag == HomeFrag().tag) {
binding.removeRecentBtn.visibility = View.GONE
+ } else {
+ if (pdfDocument.lastOpenedTime > 0) {
+ binding.removeRecentBtn.visibility = View.VISIBLE
+ } else {
+ binding.removeRecentBtn.visibility = View.GONE
+ }
}
binding.tvFileName.text = pdfDocument.fileName
binding.tvFileSize.text = pdfDocument.fileSize.toFormatFileSize()
@@ -181,6 +188,11 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment()
}).show(parentFragmentManager, "removeRecent")
dismiss()
}
+ binding.splitBtn.setOnClickListener {
+ val intent = SplitPdfActivity.createIntent(requireActivity(), pdfDocument.filePath)
+ startActivity(intent)
+ dismiss()
+ }
}
private fun updateCollectUi(b: Boolean) {
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
new file mode 100644
index 0000000..9508e60
--- /dev/null
+++ b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt
@@ -0,0 +1,129 @@
+package com.all.pdfreader.pro.app.util
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.os.ParcelFileDescriptor
+import androidx.core.graphics.createBitmap
+import com.all.pdfreader.pro.app.model.PdfPageItem
+import com.shockwave.pdfium.PdfiumCore
+import com.tom_roush.pdfbox.pdmodel.PDDocument
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.withContext
+import java.io.File
+import java.io.FileOutputStream
+
+object PdfUtils {
+
+ //拆分缩略图缓存地址
+ const val child = "pdf_split_thumbs"
+
+ //清除拆分缓存文件夹
+ fun clearPdfThumbsCache(context: Context) {
+ val cacheDir = File(context.cacheDir, child)
+ if (cacheDir.exists()) {
+ cacheDir.listFiles()?.forEach { it.delete() }
+ }
+ }
+
+ /**
+ * 分页渲染 PDF 并返回 Flow
+ * 先 emit 页对象(preview = null),UI 可立即显示总页数
+ * 后续异步渲染 Bitmap 并更新对象
+ */
+ fun splitPdfToPageItemsFlow(
+ context: Context,
+ inputFile: File,
+ dpi: Float = 72f,
+ chunkSize: Int = 5,
+ thumbWidth: Int = 200
+ ): Flow = flow {
+ val pdfiumCore = PdfiumCore(context)
+ ParcelFileDescriptor.open(inputFile, ParcelFileDescriptor.MODE_READ_ONLY).use { fd ->
+ val pdfDocument = pdfiumCore.newDocument(fd)
+ 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)
+ val canvas = Canvas(bitmap)
+ canvas.scale(scale, scale)
+
+ pdfiumCore.renderPageBitmap(pdfDocument, bitmap, i, 0, 0, width, height)
+
+ // 保存为压缩 JPEG
+ val outFile = File(cacheDir, "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) // 分批渲染,保证 UI 流畅
+ }
+
+ pdfiumCore.closeDocument(pdfDocument)
+ }
+ }.flowOn(Dispatchers.IO)
+
+ /**
+ * 获取 PDF 总页数
+ */
+ fun getPdfPageCount(inputFile: File): Int {
+ PDDocument.load(inputFile).use { return it.numberOfPages }
+ }
+
+ /**
+ * 导出用户勾选的页生成新的 PDF
+ * @param inputFile 原 PDF 文件
+ * @param selectedPages 用户选择的页列表
+ * @param outputDir 输出目录(外部存储可访问目录)
+ * @param outputFileName 输出文件名
+ */
+ suspend fun exportSelectedPages(
+ inputFile: File, selectedPages: List, outputDir: File, outputFileName: String
+ ): File? = withContext(Dispatchers.IO) {
+ if (selectedPages.isEmpty()) return@withContext null
+
+ if (!outputDir.exists()) outputDir.mkdirs() // 确保目录存在
+
+ val outputFile = File(outputDir, outputFileName)
+ try {
+ PDDocument.load(inputFile).use { document ->
+ val newDocument = PDDocument()
+ selectedPages.sortedBy { it.pageIndex }.forEach { pageItem ->
+ val page = document.getPage(pageItem.pageIndex)
+ newDocument.addPage(page)
+ }
+ newDocument.save(outputFile)
+ newDocument.close()
+ }
+ outputFile
+ } catch (e: Exception) {
+ e.printStackTrace()
+ null
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/back_black.xml b/app/src/main/res/drawable/back_black.xml
index c02aa11..5e39e91 100644
--- a/app/src/main/res/drawable/back_black.xml
+++ b/app/src/main/res/drawable/back_black.xml
@@ -5,5 +5,5 @@
android:viewportHeight="1024">
+ android:fillColor="#000000"/>
diff --git a/app/src/main/res/drawable/bg_loading.xml b/app/src/main/res/drawable/bg_loading.xml
new file mode 100644
index 0000000..fa39b5c
--- /dev/null
+++ b/app/src/main/res/drawable/bg_loading.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/dr_circular_sel_off_bg.xml b/app/src/main/res/drawable/dr_circular_sel_off_bg.xml
new file mode 100644
index 0000000..59faf37
--- /dev/null
+++ b/app/src/main/res/drawable/dr_circular_sel_off_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_circular_sel_on_bg.xml b/app/src/main/res/drawable/dr_circular_sel_on_bg.xml
new file mode 100644
index 0000000..08fbd13
--- /dev/null
+++ b/app/src/main/res/drawable/dr_circular_sel_on_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_item_page_img_sel_off_bg.xml b/app/src/main/res/drawable/dr_item_page_img_sel_off_bg.xml
new file mode 100644
index 0000000..dd9a43f
--- /dev/null
+++ b/app/src/main/res/drawable/dr_item_page_img_sel_off_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_item_page_img_sel_on_bg.xml b/app/src/main/res/drawable/dr_item_page_img_sel_on_bg.xml
new file mode 100644
index 0000000..5c83ec6
--- /dev/null
+++ b/app/src/main/res/drawable/dr_item_page_img_sel_on_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_item_page_number_sel_off_bg.xml b/app/src/main/res/drawable/dr_item_page_number_sel_off_bg.xml
new file mode 100644
index 0000000..11c6fb0
--- /dev/null
+++ b/app/src/main/res/drawable/dr_item_page_number_sel_off_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_item_page_number_sel_on_bg.xml b/app/src/main/res/drawable/dr_item_page_number_sel_on_bg.xml
new file mode 100644
index 0000000..cfee03f
--- /dev/null
+++ b/app/src/main/res/drawable/dr_item_page_number_sel_on_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_placeholder_bg.xml b/app/src/main/res/drawable/dr_placeholder_bg.xml
new file mode 100644
index 0000000..7498e17
--- /dev/null
+++ b/app/src/main/res/drawable/dr_placeholder_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_sel_off_frame.xml b/app/src/main/res/drawable/dr_sel_off_frame.xml
new file mode 100644
index 0000000..57d977b
--- /dev/null
+++ b/app/src/main/res/drawable/dr_sel_off_frame.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_sel_on_frame.xml b/app/src/main/res/drawable/dr_sel_on_frame.xml
new file mode 100644
index 0000000..dbcef5b
--- /dev/null
+++ b/app/src/main/res/drawable/dr_sel_on_frame.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/file_document.xml b/app/src/main/res/drawable/file_document.xml
new file mode 100644
index 0000000..b295851
--- /dev/null
+++ b/app/src/main/res/drawable/file_document.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/gou_white.xml b/app/src/main/res/drawable/gou_white.xml
new file mode 100644
index 0000000..b00118a
--- /dev/null
+++ b/app/src/main/res/drawable/gou_white.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/icon_split.xml b/app/src/main/res/drawable/icon_split.xml
new file mode 100644
index 0000000..a3cdab0
--- /dev/null
+++ b/app/src/main/res/drawable/icon_split.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/lock.xml b/app/src/main/res/drawable/lock.xml
index 3c17f6f..639d123 100644
--- a/app/src/main/res/drawable/lock.xml
+++ b/app/src/main/res/drawable/lock.xml
@@ -3,10 +3,10 @@
android:height="256dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
-
-
+
+
diff --git a/app/src/main/res/layout/activity_pdf_split.xml b/app/src/main/res/layout/activity_pdf_split.xml
new file mode 100644
index 0000000..38409b9
--- /dev/null
+++ b/app/src/main/res/layout/activity_pdf_split.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/adapter_split_page_item.xml b/app/src/main/res/layout/adapter_split_page_item.xml
new file mode 100644
index 0000000..d30ef8e
--- /dev/null
+++ b/app/src/main/res/layout/adapter_split_page_item.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_list_more.xml b/app/src/main/res/layout/dialog_list_more.xml
index 5a5fa22..4b88f48 100644
--- a/app/src/main/res/layout/dialog_list_more.xml
+++ b/app/src/main/res/layout/dialog_list_more.xml
@@ -251,6 +251,28 @@
android:textSize="16sp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 90a8b14..ed6df19 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -23,4 +23,5 @@
#BB6D64
#E43521
#33E43521
+ #E8EAEE
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fffeaf8..19fc48b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -119,4 +119,9 @@
Are you sure you want to delete all Bookmarks?
Loading bookmarks, please try again later
no files yet
+ Split PDF
+ Merge PDF
+ %1$d Selected
+ Loading
+ Continue Now
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index aea20b8..cb27d74 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -4,6 +4,7 @@ appcompat = "1.7.1"
agp = "8.10.1"
fragmentKtx = "1.8.9"
glide = "5.0.4"
+jp2forandroid = "1.0.4"
kotlin = "2.0.21"
ksp = "2.0.21-1.0.27"
coreKtx = "1.17.0"
@@ -30,6 +31,7 @@ androidx-room-compiler = { group = "androidx.room", name = "room-compiler", vers
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room_version" }
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room_version" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
+jp2forandroid = { module = "com.github.Tgo1014:JP2ForAndroid", version.ref = "jp2forandroid" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }