添加拆分pdf展示界面。
This commit is contained in:
parent
2089866c61
commit
06a2720547
@ -61,4 +61,5 @@ dependencies {
|
||||
implementation(libs.glide)
|
||||
implementation(libs.androidpdfviewer)
|
||||
implementation(libs.pdfbox.android)
|
||||
implementation(libs.jp2forandroid)
|
||||
}
|
||||
@ -67,6 +67,12 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.act.SplitPdfActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
||||
@ -5,6 +5,7 @@ import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||
import com.all.pdfreader.pro.app.util.FileChangeObserver
|
||||
import com.tom_roush.pdfbox.android.PDFBoxResourceLoader
|
||||
|
||||
class PRApp : Application() {
|
||||
|
||||
@ -34,6 +35,8 @@ class PRApp : Application() {
|
||||
|
||||
// 初始化数据库
|
||||
PdfRepository.initialize(this)
|
||||
// 初始化pdfbox
|
||||
PDFBoxResourceLoader.init(this)
|
||||
}
|
||||
|
||||
// 在权限授权后调用
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
package com.all.pdfreader.pro.app.model
|
||||
|
||||
/**
|
||||
* 拆分pdf,展示用的每页信息
|
||||
*/
|
||||
data class PdfPageItem(
|
||||
val pageIndex: Int,
|
||||
var previewFilePath: String?, // 缓存文件路径
|
||||
var isSelected: Boolean
|
||||
)
|
||||
@ -0,0 +1,112 @@
|
||||
package com.all.pdfreader.pro.app.ui.act
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.all.pdfreader.pro.app.R
|
||||
import com.all.pdfreader.pro.app.databinding.ActivityPdfSplitBinding
|
||||
import com.all.pdfreader.pro.app.model.PdfPageItem
|
||||
import com.all.pdfreader.pro.app.ui.adapter.SplitPdfAdapter
|
||||
import com.all.pdfreader.pro.app.util.PdfUtils
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class SplitPdfActivity : BaseActivity() {
|
||||
override val TAG: String = "SplitPdfActivity"
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_PDF_PATH = "extra_pdf_path"
|
||||
|
||||
fun createIntent(context: Context, filePath: String): Intent {
|
||||
return Intent(context, SplitPdfActivity::class.java).apply {
|
||||
putExtra(EXTRA_PDF_PATH, filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityPdfSplitBinding
|
||||
private lateinit var adapter: SplitPdfAdapter
|
||||
private var splitList: MutableList<PdfPageItem> = 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)
|
||||
}
|
||||
}
|
||||
@ -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<PdfPageItem>,
|
||||
private val onItemClick: (PdfPageItem, Int) -> Unit
|
||||
) : RecyclerView.Adapter<SplitPdfAdapter.PdfViewHolder>() {
|
||||
|
||||
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<Any>
|
||||
) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
129
app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt
Normal file
129
app/src/main/java/com/all/pdfreader/pro/app/util/PdfUtils.kt
Normal file
@ -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<PdfPageItem>
|
||||
* 先 emit 页对象(preview = null),UI 可立即显示总页数
|
||||
* 后续异步渲染 Bitmap 并更新对象
|
||||
*/
|
||||
fun splitPdfToPageItemsFlow(
|
||||
context: Context,
|
||||
inputFile: File,
|
||||
dpi: Float = 72f,
|
||||
chunkSize: Int = 5,
|
||||
thumbWidth: Int = 200
|
||||
): Flow<PdfPageItem> = 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<PdfPageItem>, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,5 +5,5 @@
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M927.9,478.1 L168.9,478.1l308.7,-308.9c11.7,-11.7 11.4,-30.9 -0.6,-42.9 -12,-12 -31.2,-12.2 -42.9,-0.5L75.2,484.9c-2,1.7 -3.8,3.6 -5.3,5.7 -4,5.4 -6,11.8 -5.9,18.3 -0.1,7.8 2.7,15.6 8.6,21.4l361.6,361.7c11.7,11.7 30.9,11.4 42.9,-0.5 12,-12 12.2,-31.2 0.6,-42.9L168.4,539.5l759.4,0c16.5,0 29.9,-13.7 29.9,-30.7S944.4,478.1 927.9,478.1z"
|
||||
android:fillColor="#2c2c2c"/>
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
|
||||
5
app/src/main/res/drawable/bg_loading.xml
Normal file
5
app/src/main/res/drawable/bg_loading.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<solid android:color="#CC000000"/>
|
||||
<corners android:radius="12dp"/>
|
||||
</shape>
|
||||
5
app/src/main/res/drawable/dr_circular_sel_off_bg.xml
Normal file
5
app/src/main/res/drawable/dr_circular_sel_off_bg.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/icon_sel_off_color"/>
|
||||
</shape>
|
||||
5
app/src/main/res/drawable/dr_circular_sel_on_bg.xml
Normal file
5
app/src/main/res/drawable/dr_circular_sel_on_bg.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/icon_sel_on_color"/>
|
||||
</shape>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners
|
||||
android:topLeftRadius="8dp"
|
||||
android:bottomRightRadius="8dp" />
|
||||
<solid android:color="@color/icon_sel_off_color" />
|
||||
</shape>
|
||||
8
app/src/main/res/drawable/dr_item_page_img_sel_on_bg.xml
Normal file
8
app/src/main/res/drawable/dr_item_page_img_sel_on_bg.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners
|
||||
android:topLeftRadius="8dp"
|
||||
android:bottomRightRadius="8dp" />
|
||||
<solid android:color="@color/icon_sel_on_color" />
|
||||
</shape>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners
|
||||
android:bottomLeftRadius="8dp"
|
||||
android:topRightRadius="8dp" />
|
||||
<solid android:color="@color/icon_sel_off_color" />
|
||||
</shape>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners
|
||||
android:bottomLeftRadius="8dp"
|
||||
android:topRightRadius="8dp" />
|
||||
<solid android:color="@color/icon_sel_on_color" />
|
||||
</shape>
|
||||
6
app/src/main/res/drawable/dr_placeholder_bg.xml
Normal file
6
app/src/main/res/drawable/dr_placeholder_bg.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/placeholder_bg_color"/>
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
7
app/src/main/res/drawable/dr_sel_off_frame.xml
Normal file
7
app/src/main/res/drawable/dr_sel_off_frame.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke android:color="@color/placeholder_bg_color"
|
||||
android:width="1dp"/>
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
7
app/src/main/res/drawable/dr_sel_on_frame.xml
Normal file
7
app/src/main/res/drawable/dr_sel_on_frame.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke android:color="@color/icon_sel_on_color"
|
||||
android:width="1dp"/>
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/file_document.xml
Normal file
9
app/src/main/res/drawable/file_document.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M842.7,285.9l-187.7,-187.7c-14.9,-14.9 -32,-21.3 -53.3,-21.3L234.7,76.8C194.1,74.7 160,108.8 160,149.3v725.3c0,40.5 34.1,74.7 74.7,74.7h554.7c40.5,0 74.7,-34.1 74.7,-74.7L864,337.1c0,-19.2 -8.5,-38.4 -21.3,-51.2zM797.9,330.7c-2.1,2.1 -4.3,0 -8.5,0h-170.7c-6.4,0 -10.7,-4.3 -10.7,-10.7L608,149.3c0,-2.1 0,-6.4 -2.1,-8.5 0,0 2.1,0 2.1,2.1l189.9,187.7zM789.3,885.3L234.7,885.3c-6.4,0 -10.7,-4.3 -10.7,-10.7L224,149.3c0,-6.4 4.3,-10.7 10.7,-10.7h311.5c-2.1,4.3 -2.1,6.4 -2.1,10.7v170.7c0,40.5 34.1,74.7 74.7,74.7h170.7c4.3,0 6.4,0 10.7,-2.1L800,874.7c0,6.4 -4.3,10.7 -10.7,10.7z"
|
||||
android:fillColor="#cccccc"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/gou_white.xml
Normal file
9
app/src/main/res/drawable/gou_white.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M384,768c-12.8,0 -21.3,-4.3 -29.9,-12.8l-213.3,-213.3c-17.1,-17.1 -17.1,-42.7 0,-59.7s42.7,-17.1 59.7,0L384,665.6 823.5,226.1c17.1,-17.1 42.7,-17.1 59.7,0s17.1,42.7 0,59.7l-469.3,469.3c-8.5,8.5 -17.1,12.8 -29.9,12.8z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/icon_split.xml
Normal file
9
app/src/main/res/drawable/icon_split.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="256dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M255.9,255.9v511.7h511.7v-511.7h-511.7zM703.6,703.6h-383.8v-383.8h383.8v383.8zM895.5,575.7v319.8h-319.8v64h383.8v-383.8zM127.9,447.8v-319.8h319.8v-64h-383.8v383.8z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
@ -3,10 +3,10 @@
|
||||
android:height="256dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M785.1,416h-61.9v-121.6c0,-108.8 -91.7,-198.4 -202.7,-198.4s-202.7,89.6 -202.7,198.4v121.6h-78.9c-55.5,0 -100.3,44.8 -100.3,100.3v311.5c0,55.5 44.8,100.3 100.3,100.3h546.1c55.5,0 100.3,-44.8 100.3,-100.3L885.3,516.3c0,-55.5 -44.8,-100.3 -100.3,-100.3zM381.9,294.4c0,-74.7 61.9,-134.4 138.7,-134.4s138.7,59.7 138.7,134.4v121.6h-277.3v-121.6zM821.3,827.7c0,19.2 -17.1,36.3 -36.3,36.3L238.9,864c-19.2,0 -36.3,-17.1 -36.3,-36.3L202.7,516.3c0,-19.2 17.1,-36.3 36.3,-36.3h546.1c19.2,0 36.3,17.1 36.3,36.3v311.5z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:pathData="M512,544c-17.1,0 -32,14.9 -32,32v106.7c0,17.1 14.9,32 32,32s32,-14.9 32,-32v-106.7c0,-17.1 -14.9,-32 -32,-32z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:fillColor="#666666"
|
||||
android:pathData="M785.1,416h-61.9v-121.6c0,-108.8 -91.7,-198.4 -202.7,-198.4s-202.7,89.6 -202.7,198.4v121.6h-78.9c-55.5,0 -100.3,44.8 -100.3,100.3v311.5c0,55.5 44.8,100.3 100.3,100.3h546.1c55.5,0 100.3,-44.8 100.3,-100.3L885.3,516.3c0,-55.5 -44.8,-100.3 -100.3,-100.3zM381.9,294.4c0,-74.7 61.9,-134.4 138.7,-134.4s138.7,59.7 138.7,134.4v121.6h-277.3v-121.6zM821.3,827.7c0,19.2 -17.1,36.3 -36.3,36.3L238.9,864c-19.2,0 -36.3,-17.1 -36.3,-36.3L202.7,516.3c0,-19.2 17.1,-36.3 36.3,-36.3h546.1c19.2,0 36.3,17.1 36.3,36.3v311.5z" />
|
||||
<path
|
||||
android:fillColor="#666666"
|
||||
android:pathData="M512,544c-17.1,0 -32,14.9 -32,32v106.7c0,17.1 14.9,32 32,32s32,-14.9 32,-32v-106.7c0,-17.1 -14.9,-32 -32,-32z" />
|
||||
</vector>
|
||||
|
||||
106
app/src/main/res/layout/activity_pdf_split.xml
Normal file
106
app/src/main/res/layout/activity_pdf_split.xml
Normal file
@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/bg_color"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/statusLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/backBtn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/back_black" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextViewFont_PopMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/selected_page"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/selectAllBtn"
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/selectAll"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:background="@drawable/dr_circular_sel_off_bg"
|
||||
android:padding="2dp"
|
||||
android:src="@drawable/gou_white" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/loadingRoot"
|
||||
layout="@layout/layout_loading"
|
||||
android:visibility="gone" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/splitRv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="8dp"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="6"
|
||||
tools:listitem="@layout/adapter_split_page_item" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/allow_access_btn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/dr_btn_not_clickable_bg"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
style="@style/TextViewFont_PopSemiBold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/continue_now"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
58
app/src/main/res/layout/adapter_split_page_item.xml
Normal file
58
app/src/main/res/layout/adapter_split_page_item.xml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/itemBgLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="230dp"
|
||||
android:layout_margin="8dp"
|
||||
android:background="@drawable/dr_sel_off_frame"
|
||||
android:gravity="center"
|
||||
android:padding="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/dr_placeholder_bg"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:src="@drawable/file_document" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pageNumberTv"
|
||||
style="@style/TextViewFont_PopMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@drawable/dr_item_page_number_sel_off_bg"
|
||||
android:gravity="center"
|
||||
android:minWidth="24dp"
|
||||
android:padding="2dp"
|
||||
android:text="1"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/selIv"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@drawable/dr_item_page_img_sel_off_bg"
|
||||
android:padding="2dp"
|
||||
android:src="@drawable/gou_white" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
@ -251,6 +251,28 @@
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/splitBtn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/icon_split" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="@string/split_pdf"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/setPasswordBtn"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
38
app/src/main/res/layout/layout_loading.xml
Normal file
38
app/src/main/res/layout/layout_loading.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/loading_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#50000000"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="48dp"
|
||||
android:indeterminateTint="@color/icon_sel_on_color"
|
||||
android:layout_height="48dp"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/loading"
|
||||
style="@style/TextViewFont_PopMedium"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@ -23,4 +23,5 @@
|
||||
<color name="text_red_lv2">#BB6D64</color>
|
||||
<color name="btn_sel_on_color">#E43521</color>
|
||||
<color name="btn_sel_off_color">#33E43521</color>
|
||||
<color name="placeholder_bg_color">#E8EAEE</color>
|
||||
</resources>
|
||||
@ -119,4 +119,9 @@
|
||||
<string name="delete_bookmarks_desc">Are you sure you want to delete all Bookmarks?</string>
|
||||
<string name="bookmark_loading">Loading bookmarks, please try again later</string>
|
||||
<string name="no_files_yet">no files yet</string>
|
||||
<string name="split_pdf">Split PDF</string>
|
||||
<string name="merge_pdf">Merge PDF</string>
|
||||
<string name="selected_page">%1$d Selected</string>
|
||||
<string name="loading">Loading</string>
|
||||
<string name="continue_now">Continue Now</string>
|
||||
</resources>
|
||||
@ -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" }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user