添加批量操作等功能。
This commit is contained in:
parent
c7af68a8ee
commit
e20a8ab016
@ -5,6 +5,9 @@ import java.io.File
|
|||||||
sealed class FileActionEvent {
|
sealed class FileActionEvent {
|
||||||
data class Rename(val renameResult: RenameResult) : FileActionEvent()
|
data class Rename(val renameResult: RenameResult) : FileActionEvent()
|
||||||
data class Delete(val deleteResult: DeleteResult) : FileActionEvent()
|
data class Delete(val deleteResult: DeleteResult) : FileActionEvent()
|
||||||
|
data class DeleteAll(val status: Status, val deleteResult: DeleteResult? = null) : FileActionEvent() {
|
||||||
|
enum class Status { START, COMPLETE }
|
||||||
|
}
|
||||||
data class Favorite(val isFavorite: Boolean) : FileActionEvent()
|
data class Favorite(val isFavorite: Boolean) : FileActionEvent()
|
||||||
data class Duplicate(val file: File?) : FileActionEvent()
|
data class Duplicate(val file: File?) : FileActionEvent()
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,9 @@ interface BookmarkDao {
|
|||||||
@Query("DELETE FROM bookmarks WHERE filePath = :filePath")
|
@Query("DELETE FROM bookmarks WHERE filePath = :filePath")
|
||||||
suspend fun deleteAllByPdf(filePath: String): Int
|
suspend fun deleteAllByPdf(filePath: String): Int
|
||||||
|
|
||||||
|
@Query("DELETE FROM bookmarks WHERE filePath IN (:filePaths)")
|
||||||
|
suspend fun deleteAllByPdfs(filePaths: List<String>): Int
|
||||||
|
|
||||||
@Query("DELETE FROM bookmarks WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
@Query("DELETE FROM bookmarks WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
||||||
suspend fun deleteByPage(filePath: String, pageNumber: Int)
|
suspend fun deleteByPage(filePath: String, pageNumber: Int)
|
||||||
|
|
||||||
|
|||||||
@ -34,6 +34,9 @@ interface NoteDao {
|
|||||||
@Query("DELETE FROM notes WHERE filePath = :filePath")
|
@Query("DELETE FROM notes WHERE filePath = :filePath")
|
||||||
suspend fun deleteAllByPdf(filePath: String)
|
suspend fun deleteAllByPdf(filePath: String)
|
||||||
|
|
||||||
|
@Query("DELETE FROM notes WHERE filePath IN (:filePaths)")
|
||||||
|
suspend fun deleteAllByPdfs(filePaths: List<String>)
|
||||||
|
|
||||||
@Query("DELETE FROM notes WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
@Query("DELETE FROM notes WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
||||||
suspend fun deleteByPage(filePath: String, pageNumber: Int)
|
suspend fun deleteByPage(filePath: String, pageNumber: Int)
|
||||||
}
|
}
|
||||||
@ -41,6 +41,14 @@ interface PdfDocumentDao {
|
|||||||
@Query("DELETE FROM pdf_documents WHERE filePath = :filePath")
|
@Query("DELETE FROM pdf_documents WHERE filePath = :filePath")
|
||||||
suspend fun deleteByPath(filePath: String)
|
suspend fun deleteByPath(filePath: String)
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
@Query("DELETE FROM pdf_documents WHERE filePath IN (:filePaths)")
|
||||||
|
suspend fun deleteByPaths(filePaths: List<String>)
|
||||||
|
|
||||||
|
// 批量取消收藏,设置isFavorite = 0
|
||||||
|
@Query("UPDATE pdf_documents SET isFavorite = 0 WHERE filePath IN (:filePaths)")
|
||||||
|
suspend fun cancelFavoriteStatus(filePaths: List<String>)
|
||||||
|
|
||||||
//@Query 会响应flow
|
//@Query 会响应flow
|
||||||
@Query("UPDATE pdf_documents SET filePath = :newFilePath, fileName = :newName WHERE filePath = :oldFilePath")
|
@Query("UPDATE pdf_documents SET filePath = :newFilePath, fileName = :newName WHERE filePath = :oldFilePath")
|
||||||
suspend fun updateFilePathAndFileName(oldFilePath: String, newFilePath: String, newName: String)
|
suspend fun updateFilePathAndFileName(oldFilePath: String, newFilePath: String, newName: String)
|
||||||
@ -51,4 +59,7 @@ interface PdfDocumentDao {
|
|||||||
@Query("UPDATE pdf_documents SET lastOpenedTime = :time WHERE filePath = :filePath")
|
@Query("UPDATE pdf_documents SET lastOpenedTime = :time WHERE filePath = :filePath")
|
||||||
suspend fun updateLastOpenTime(filePath: String, time: Long)
|
suspend fun updateLastOpenTime(filePath: String, time: Long)
|
||||||
|
|
||||||
|
@Query("UPDATE pdf_documents SET lastOpenedTime = :time WHERE filePath IN (:filePaths)")
|
||||||
|
suspend fun updateLastOpenTimes(filePaths: List<String>, time: Long)
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -31,6 +31,10 @@ interface RecentReadDao {
|
|||||||
@Query("DELETE FROM recently_read WHERE filePath = :filePath")
|
@Query("DELETE FROM recently_read WHERE filePath = :filePath")
|
||||||
suspend fun deleteByPdfPath(filePath: String)
|
suspend fun deleteByPdfPath(filePath: String)
|
||||||
|
|
||||||
|
@Query("DELETE FROM recently_read WHERE filePath IN (:filePaths)")
|
||||||
|
suspend fun deleteByPdfPaths(filePaths: List<String>)
|
||||||
|
|
||||||
|
|
||||||
@Query("DELETE FROM recently_read WHERE lastOpenedTime < :cutoffTime")
|
@Query("DELETE FROM recently_read WHERE lastOpenedTime < :cutoffTime")
|
||||||
suspend fun deleteOldRecents(cutoffTime: Long)
|
suspend fun deleteOldRecents(cutoffTime: Long)
|
||||||
}
|
}
|
||||||
@ -62,10 +62,14 @@ class PdfRepository private constructor(context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//更新最后打开时间,可以设置为0L,相当于更新成未打开过。
|
//更新最后打开时间,可以设置为0L,相当于更新成未打开过。
|
||||||
suspend fun updateLastOpenTime(filePath: String, time: Long) {
|
suspend fun updateLastOpenTime(filePath: String, time: Long = 0L) {
|
||||||
pdfDao.updateLastOpenTime(filePath, time)
|
pdfDao.updateLastOpenTime(filePath, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateLastOpenTimes(filePaths: List<String>, time: Long = 0L) {
|
||||||
|
pdfDao.updateLastOpenTimes(filePaths, time)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun updateFavoriteStatus(filePath: String, isFavorite: Boolean) {
|
suspend fun updateFavoriteStatus(filePath: String, isFavorite: Boolean) {
|
||||||
val document = pdfDao.getByPath(filePath)?.copy(
|
val document = pdfDao.getByPath(filePath)?.copy(
|
||||||
isFavorite = isFavorite,
|
isFavorite = isFavorite,
|
||||||
@ -74,6 +78,11 @@ class PdfRepository private constructor(context: Context) {
|
|||||||
document?.let { pdfDao.update(it) }
|
document?.let { pdfDao.update(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//批量取消收藏
|
||||||
|
suspend fun cancelFavorites(filePaths: List<String>) {
|
||||||
|
pdfDao.cancelFavoriteStatus(filePaths)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun updateReadingProgress(filePath: String, page: Int, progress: Float) {
|
suspend fun updateReadingProgress(filePath: String, page: Int, progress: Float) {
|
||||||
val document = pdfDao.getByPath(filePath)?.copy(
|
val document = pdfDao.getByPath(filePath)?.copy(
|
||||||
lastOpenedTime = System.currentTimeMillis(),
|
lastOpenedTime = System.currentTimeMillis(),
|
||||||
@ -188,6 +197,14 @@ class PdfRepository private constructor(context: Context) {
|
|||||||
noteDao.deleteAllByPdf(filePath)
|
noteDao.deleteAllByPdf(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 数据清理集合
|
||||||
|
suspend fun deleteDocuments(filePaths: List<String>) {
|
||||||
|
pdfDao.deleteByPaths(filePaths)
|
||||||
|
recentDao.deleteByPdfPaths(filePaths)
|
||||||
|
bookmarkDao.deleteAllByPdfs(filePaths)
|
||||||
|
noteDao.deleteAllByPdfs(filePaths)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Volatile
|
@Volatile
|
||||||
private var INSTANCE: PdfRepository? = null
|
private var INSTANCE: PdfRepository? = null
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.all.pdfreader.pro.app.ui.act
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@ -15,18 +16,22 @@ import com.all.pdfreader.pro.app.model.FragmentType
|
|||||||
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||||
import com.all.pdfreader.pro.app.ui.dialog.PermissionDialogFragment
|
import com.all.pdfreader.pro.app.ui.dialog.PermissionDialogFragment
|
||||||
import com.all.pdfreader.pro.app.ui.dialog.ProgressDialogFragment
|
import com.all.pdfreader.pro.app.ui.dialog.ProgressDialogFragment
|
||||||
|
import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment
|
||||||
import com.all.pdfreader.pro.app.ui.dialog.SortDialogFragment
|
import com.all.pdfreader.pro.app.ui.dialog.SortDialogFragment
|
||||||
import com.all.pdfreader.pro.app.ui.fragment.FavoriteFrag
|
import com.all.pdfreader.pro.app.ui.fragment.FavoriteFrag
|
||||||
import com.all.pdfreader.pro.app.ui.fragment.HomeFrag
|
import com.all.pdfreader.pro.app.ui.fragment.HomeFrag
|
||||||
import com.all.pdfreader.pro.app.ui.fragment.RecentlyFrag
|
import com.all.pdfreader.pro.app.ui.fragment.RecentlyFrag
|
||||||
import com.all.pdfreader.pro.app.ui.fragment.ToolsFrag
|
import com.all.pdfreader.pro.app.ui.fragment.ToolsFrag
|
||||||
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
|
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.PdfScanner
|
import com.all.pdfreader.pro.app.util.PdfScanner
|
||||||
|
import com.all.pdfreader.pro.app.util.PdfUtils
|
||||||
import com.all.pdfreader.pro.app.util.StoragePermissionHelper
|
import com.all.pdfreader.pro.app.util.StoragePermissionHelper
|
||||||
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
||||||
import com.all.pdfreader.pro.app.viewmodel.observeEvent
|
import com.all.pdfreader.pro.app.viewmodel.observeEvent
|
||||||
import com.gyf.immersionbar.ImmersionBar
|
import com.gyf.immersionbar.ImmersionBar
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback,
|
class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback,
|
||||||
PermissionDialogFragment.CloseCallback, HomeFrag.OnItemLongClickListener,
|
PermissionDialogFragment.CloseCallback, HomeFrag.OnItemLongClickListener,
|
||||||
@ -136,6 +141,26 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
viewModel.fileActionEvent.observeEvent<FileActionEvent.DeleteAll>(this) { event ->
|
||||||
|
when (event.status) {
|
||||||
|
FileActionEvent.DeleteAll.Status.START -> {
|
||||||
|
progressDialog = ProgressDialogFragment()
|
||||||
|
progressDialog?.show(supportFragmentManager, "progressDialog")
|
||||||
|
}
|
||||||
|
|
||||||
|
FileActionEvent.DeleteAll.Status.COMPLETE -> {
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
progressDialog = null
|
||||||
|
event.deleteResult?.let {
|
||||||
|
if (event.deleteResult.success) {
|
||||||
|
showToast(getString(R.string.delete_successfully))
|
||||||
|
} else {
|
||||||
|
showToast(event.deleteResult.errorMessage.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupFragments() {
|
private fun setupFragments() {
|
||||||
@ -147,12 +172,12 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
|
|
||||||
//按钮点击事件
|
//按钮点击事件
|
||||||
private fun setupNavigation() {
|
private fun setupNavigation() {
|
||||||
binding.homeLlBtn.setOnClickListener { switchFragment(homeFragment) }
|
binding.homeLlBtn.setOnSingleClickListener { switchFragment(homeFragment) }
|
||||||
binding.recentlyLlBtn.setOnClickListener { switchFragment(recentlyFragment) }
|
binding.recentlyLlBtn.setOnSingleClickListener { switchFragment(recentlyFragment) }
|
||||||
binding.favoriteLlBtn.setOnClickListener { switchFragment(favoriteFragment) }
|
binding.favoriteLlBtn.setOnSingleClickListener { switchFragment(favoriteFragment) }
|
||||||
binding.toolsLayoutBtn.setOnClickListener { switchFragment(toolsFragment) }
|
binding.toolsLayoutBtn.setOnSingleClickListener { switchFragment(toolsFragment) }
|
||||||
|
|
||||||
binding.pnGoBtn.setOnClickListener {
|
binding.pnGoBtn.setOnSingleClickListener {
|
||||||
//直接跳转到权限设置页面
|
//直接跳转到权限设置页面
|
||||||
requestPermissions()
|
requestPermissions()
|
||||||
}
|
}
|
||||||
@ -171,13 +196,10 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
(activeFragment as? SortableFragment)?.onSortTypeChanged(it)
|
(activeFragment as? SortableFragment)?.onSortTypeChanged(it)
|
||||||
}).show(supportFragmentManager, TAG)
|
}).show(supportFragmentManager, TAG)
|
||||||
}
|
}
|
||||||
binding.multiSelectBackBtn.setOnClickListener {
|
binding.multiSelectBackBtn.setOnSingleClickListener {
|
||||||
homeFragment.exitMultiSelectMode()
|
exitAllMultiSelect()
|
||||||
favoriteFragment.exitMultiSelectMode()
|
|
||||||
recentlyFragment.exitMultiSelectMode()
|
|
||||||
updateMultiSelectUi(false, fragmentType)
|
|
||||||
}
|
}
|
||||||
binding.multiSelectAllBtn.setOnClickListener {
|
binding.multiSelectAllBtn.setOnSingleClickListener {
|
||||||
homeFragment.adapter.toggleSelectAll()
|
homeFragment.adapter.toggleSelectAll()
|
||||||
val isAllSelected = homeFragment.adapter.isAllSelected()
|
val isAllSelected = homeFragment.adapter.isAllSelected()
|
||||||
if (isAllSelected) {
|
if (isAllSelected) {
|
||||||
@ -187,18 +209,56 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
}
|
}
|
||||||
updateSelectNumber(homeFragment.adapter.getSelectedItems().size)
|
updateSelectNumber(homeFragment.adapter.getSelectedItems().size)
|
||||||
}
|
}
|
||||||
binding.multiSelectDeleteBtn.setOnClickListener {
|
binding.multiSelectDeleteBtn.setOnSingleClickListener {
|
||||||
val selectedItems = homeFragment.adapter.getSelectedItems()
|
val selectedItems = homeFragment.adapter.getSelectedItems()
|
||||||
logDebug("selectedItems->${selectedItems.size}")
|
if (selectedItems.isNotEmpty()) {
|
||||||
|
val filesToDelete = selectedItems.map { File(it.filePath) }
|
||||||
|
var title = getString(R.string.delete_file_title)
|
||||||
|
var desc = getString(R.string.delete_file_desc)
|
||||||
|
if (selectedItems.size > 1) {
|
||||||
|
title = getString(R.string.delete_all_file_title)
|
||||||
|
desc = getString(R.string.delete_all_file_desc)
|
||||||
}
|
}
|
||||||
binding.multiSelectMergeBtn.setOnClickListener {
|
PromptDialogFragment(
|
||||||
|
title, desc, onOkClick = {
|
||||||
|
viewModel.deleteFiles(filesToDelete)
|
||||||
|
exitAllMultiSelect()
|
||||||
|
}).show(supportFragmentManager, "deleteFiles")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.multiSelectMergeBtn.setOnSingleClickListener {
|
||||||
logDebug("合并")
|
logDebug("合并")
|
||||||
|
val selectedItems = recentlyFragment.adapter.getSelectedItems()
|
||||||
|
// if (selectedItems.isNotEmpty()) {
|
||||||
|
// val inputFile = selectedItems.map { File(it.filePath) }
|
||||||
|
// val outputDir = File(
|
||||||
|
// Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
|
||||||
|
// "PDFReaderPro/merge"
|
||||||
|
// ).apply { if (!exists()) mkdirs() }
|
||||||
|
// PdfUtils.mergePdfFiles(inputFiles = inputFile, outputDir =outputDir, onProgress = {} )
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
binding.multiSelectRemoveBtn.setOnClickListener {
|
binding.multiSelectRemoveBtn.setOnSingleClickListener {
|
||||||
logDebug("移除最新阅读")
|
val selectedItems = recentlyFragment.adapter.getSelectedItems()
|
||||||
|
if (selectedItems.isNotEmpty()) {
|
||||||
|
val filePaths = selectedItems.map { it.filePath }
|
||||||
|
PromptDialogFragment(
|
||||||
|
getString(R.string.remove_dialog_title),
|
||||||
|
getString(R.string.remove_dialog_desc),
|
||||||
|
getString(R.string.remove),
|
||||||
|
onOkClick = {
|
||||||
|
viewModel.removeRecentAll(filePaths)
|
||||||
|
exitAllMultiSelect()
|
||||||
|
}).show(supportFragmentManager, "removeRecent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.multiSelectUnFavoriteBtn.setOnSingleClickListener {
|
||||||
|
val selectedItems = favoriteFragment.adapter.getSelectedItems()
|
||||||
|
if (selectedItems.isNotEmpty()) {
|
||||||
|
val filePaths = selectedItems.map { it.filePath }
|
||||||
|
viewModel.cancelCollectState(filePaths)
|
||||||
|
exitAllMultiSelect()
|
||||||
}
|
}
|
||||||
binding.multiSelectUnFavoriteBtn.setOnClickListener {
|
|
||||||
logDebug("移除收藏")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +356,13 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun exitAllMultiSelect() {
|
||||||
|
homeFragment.exitMultiSelectMode()
|
||||||
|
favoriteFragment.exitMultiSelectMode()
|
||||||
|
recentlyFragment.exitMultiSelectMode()
|
||||||
|
updateMultiSelectUi(false, fragmentType)
|
||||||
|
}
|
||||||
|
|
||||||
// 授权后续操作
|
// 授权后续操作
|
||||||
override fun onPermissionGranted() {
|
override fun onPermissionGranted() {
|
||||||
logDebug("main onPermissionGranted")
|
logDebug("main onPermissionGranted")
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import com.all.pdfreader.pro.app.ui.adapter.SplitPdfAdapter
|
|||||||
import com.all.pdfreader.pro.app.ui.adapter.SplitSelectedPdfAdapter
|
import com.all.pdfreader.pro.app.ui.adapter.SplitSelectedPdfAdapter
|
||||||
import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment
|
import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment
|
||||||
import com.all.pdfreader.pro.app.ui.dialog.RenameDialogFragment
|
import com.all.pdfreader.pro.app.ui.dialog.RenameDialogFragment
|
||||||
|
import com.all.pdfreader.pro.app.util.AppUtils.setOnSingleClickListener
|
||||||
import com.all.pdfreader.pro.app.util.FileUtils.toUnderscoreDateTime
|
import com.all.pdfreader.pro.app.util.FileUtils.toUnderscoreDateTime
|
||||||
import com.all.pdfreader.pro.app.util.PdfUtils
|
import com.all.pdfreader.pro.app.util.PdfUtils
|
||||||
import com.gyf.immersionbar.ImmersionBar
|
import com.gyf.immersionbar.ImmersionBar
|
||||||
@ -109,8 +110,8 @@ class SplitPdfActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupClick() {
|
private fun setupClick() {
|
||||||
binding.backBtn.setOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
binding.backBtn.setOnSingleClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||||
binding.selectAllBtn.setOnClickListener {
|
binding.selectAllBtn.setOnSingleClickListener {
|
||||||
val selectAll = splitList.any { !it.isSelected }//如果列表里有一页没选中 → 返回 true
|
val selectAll = splitList.any { !it.isSelected }//如果列表里有一页没选中 → 返回 true
|
||||||
adapter.setAllSelected(selectAll)
|
adapter.setAllSelected(selectAll)
|
||||||
binding.title.text = getString(R.string.selected_page, adapter.getSelPages())
|
binding.title.text = getString(R.string.selected_page, adapter.getSelPages())
|
||||||
@ -118,7 +119,7 @@ class SplitPdfActivity : BaseActivity() {
|
|||||||
updateSelectAllState(selectAll)
|
updateSelectAllState(selectAll)
|
||||||
updateContinueNowBtnState(selectAll)
|
updateContinueNowBtnState(selectAll)
|
||||||
}
|
}
|
||||||
binding.continueNowBtn.setOnClickListener {
|
binding.continueNowBtn.setOnSingleClickListener {
|
||||||
val selectedPages = splitList.filter { it.isSelected }.map { it.copy() }
|
val selectedPages = splitList.filter { it.isSelected }.map { it.copy() }
|
||||||
val name = getString(R.string.split) + "_" + System.currentTimeMillis().toUnderscoreDateTime()
|
val name = getString(R.string.split) + "_" + System.currentTimeMillis().toUnderscoreDateTime()
|
||||||
val item = PdfSelectedPagesItem(filePath, name, selectedPages)
|
val item = PdfSelectedPagesItem(filePath, name, selectedPages)
|
||||||
@ -127,14 +128,14 @@ class SplitPdfActivity : BaseActivity() {
|
|||||||
isSelectedViewShow = true
|
isSelectedViewShow = true
|
||||||
updateViewState(true)
|
updateViewState(true)
|
||||||
}
|
}
|
||||||
binding.addBtn.setOnClickListener {
|
binding.addBtn.setOnSingleClickListener {
|
||||||
binding.continueNowBtn.isEnabled = false//继续按钮不可点击
|
binding.continueNowBtn.isEnabled = false//继续按钮不可点击
|
||||||
updateContinueNowBtnState(false)//重置继续按钮背景
|
updateContinueNowBtnState(false)//重置继续按钮背景
|
||||||
adapter.setAllSelected(false)
|
adapter.setAllSelected(false)
|
||||||
updateSelectAllState(false)
|
updateSelectAllState(false)
|
||||||
updateViewState(false)
|
updateViewState(false)
|
||||||
}
|
}
|
||||||
binding.splitBtn.setOnClickListener {
|
binding.splitBtn.setOnSingleClickListener {
|
||||||
//因为图片做的路径缓存方式,所以这里直接传入整个集合到result页处理
|
//因为图片做的路径缓存方式,所以这里直接传入整个集合到result页处理
|
||||||
val intent = SplitPdfResultActivity.createIntent(this, ArrayList(selectedList))
|
val intent = SplitPdfResultActivity.createIntent(this, ArrayList(selectedList))
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
|||||||
@ -35,6 +35,16 @@ import java.util.regex.Pattern
|
|||||||
|
|
||||||
object AppUtils {
|
object AppUtils {
|
||||||
|
|
||||||
|
fun View.setOnSingleClickListener(interval: Long = 1000, onClick: (View) -> Unit) {
|
||||||
|
var lastClickTime = 0L
|
||||||
|
setOnClickListener {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
if (now - lastClickTime < interval) return@setOnClickListener
|
||||||
|
lastClickTime = now
|
||||||
|
onClick(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加点击动画(点击立即执行逻辑,动画期间禁用点击)
|
* 添加点击动画(点击立即执行逻辑,动画期间禁用点击)
|
||||||
*
|
*
|
||||||
|
|||||||
@ -122,6 +122,7 @@ object PdfUtils {
|
|||||||
newDocument.importPage(document.getPage(pageItem.pageIndex))
|
newDocument.importPage(document.getPage(pageItem.pageIndex))
|
||||||
// 回调进度
|
// 回调进度
|
||||||
onProgress?.invoke(index + 1, total)
|
onProgress?.invoke(index + 1, total)
|
||||||
|
delay(1)
|
||||||
}
|
}
|
||||||
// 保存新 PDF 文件
|
// 保存新 PDF 文件
|
||||||
newDocument.save(outputFile)
|
newDocument.save(outputFile)
|
||||||
@ -133,4 +134,50 @@ object PdfUtils {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并多个 PDF 文件到一个新的 PDF 文件
|
||||||
|
*
|
||||||
|
* 使用 PDFBox 的 PDDocument 合并多个 PDF,支持进度回调。
|
||||||
|
* 避免直接生成缩略图,以保持原 PDF 的矢量质量。
|
||||||
|
*
|
||||||
|
* @param inputFiles 要合并的 PDF 文件列表
|
||||||
|
* @param outputDir 输出目录,如果不存在会自动创建
|
||||||
|
* @param outputFileName 输出文件名,例如 "merged.pdf"
|
||||||
|
* @param onProgress 可选回调,当前处理进度 (current 文件, total 文件)
|
||||||
|
* @return 新生成的 PDF 文件,失败返回 null
|
||||||
|
*/
|
||||||
|
suspend fun mergePdfFiles(
|
||||||
|
inputFiles: List<File>,
|
||||||
|
outputDir: File,
|
||||||
|
outputFileName: String,
|
||||||
|
onProgress: ((current: Int, total: Int) -> Unit)? = null
|
||||||
|
): File? = withContext(Dispatchers.IO) {
|
||||||
|
|
||||||
|
if (inputFiles.isEmpty()) return@withContext null
|
||||||
|
if (!outputDir.exists()) outputDir.mkdirs()
|
||||||
|
val outputFile = File(outputDir, outputFileName)
|
||||||
|
|
||||||
|
try {
|
||||||
|
PDDocument().use { mergedDocument ->
|
||||||
|
val totalFiles = inputFiles.size
|
||||||
|
inputFiles.forEachIndexed { index, file ->
|
||||||
|
PDDocument.load(file).use { doc ->
|
||||||
|
for (page in doc.pages) {
|
||||||
|
mergedDocument.addPage(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 回调进度:按文件计数,也可以按总页数进一步精细化
|
||||||
|
onProgress?.invoke(index + 1, totalFiles)
|
||||||
|
delay(1) // 给 UI 更新留点时间
|
||||||
|
}
|
||||||
|
mergedDocument.save(outputFile)
|
||||||
|
}
|
||||||
|
outputFile
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.all.pdfreader.pro.app.PRApp
|
import com.all.pdfreader.pro.app.PRApp
|
||||||
|
import com.all.pdfreader.pro.app.model.DeleteResult
|
||||||
import com.all.pdfreader.pro.app.model.FileActionEvent
|
import com.all.pdfreader.pro.app.model.FileActionEvent
|
||||||
import com.all.pdfreader.pro.app.room.entity.BookmarkEntity
|
import com.all.pdfreader.pro.app.room.entity.BookmarkEntity
|
||||||
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||||
@ -88,6 +89,30 @@ class PdfViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteFiles(files: List<File>) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_fileActionEvent.postValue(FileActionEvent.DeleteAll(FileActionEvent.DeleteAll.Status.START))
|
||||||
|
val deleteResult = withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val result = FileDeleteUtil.deleteFiles(files)
|
||||||
|
if (result.success) {
|
||||||
|
// 批量清理数据库
|
||||||
|
val paths = files.map { it.absolutePath }
|
||||||
|
pdfRepository.deleteDocuments(paths)
|
||||||
|
}
|
||||||
|
result
|
||||||
|
} catch (e: Exception) {
|
||||||
|
DeleteResult.failure(e.message ?: "Unknown error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_fileActionEvent.postValue(
|
||||||
|
FileActionEvent.DeleteAll(
|
||||||
|
FileActionEvent.DeleteAll.Status.COMPLETE, deleteResult
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun saveCollectState(filePath: String, isFavorite: Boolean) {
|
fun saveCollectState(filePath: String, isFavorite: Boolean) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
pdfRepository.updateFavoriteStatus(filePath, isFavorite)
|
pdfRepository.updateFavoriteStatus(filePath, isFavorite)
|
||||||
@ -95,6 +120,13 @@ class PdfViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelCollectState(filePaths: List<String>) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
pdfRepository.cancelFavorites(filePaths)
|
||||||
|
_fileActionEvent.postValue(FileActionEvent.Favorite(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun duplicateFile(context: Context, filePath: String) {
|
fun duplicateFile(context: Context, filePath: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val file = FileUtils.duplicateFile(File(filePath))
|
val file = FileUtils.duplicateFile(File(filePath))
|
||||||
@ -214,7 +246,7 @@ class PdfViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun gotoPage(number: Int){
|
fun gotoPage(number: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_fileActionEvent.postValue(FileActionEvent.GotoPage(number))
|
_fileActionEvent.postValue(FileActionEvent.GotoPage(number))
|
||||||
}
|
}
|
||||||
@ -222,7 +254,13 @@ class PdfViewModel : ViewModel() {
|
|||||||
|
|
||||||
fun removeRecent(filePath: String) {
|
fun removeRecent(filePath: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
pdfRepository.updateLastOpenTime(filePath, 0L)
|
pdfRepository.updateLastOpenTime(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeRecentAll(filePaths: List<String>) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
pdfRepository.updateLastOpenTimes(filePaths)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
style="@style/TextViewFont_PopMedium"
|
style="@style/TextViewFont_PopMedium"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/no_files_yet"
|
android:text="@string/no_favorites_yet"
|
||||||
android:textColor="#B6BFCC"
|
android:textColor="#B6BFCC"
|
||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
style="@style/TextViewFont_PopMedium"
|
style="@style/TextViewFont_PopMedium"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/no_files_yet"
|
android:text="@string/no_recent_reads_yet"
|
||||||
android:textColor="#B6BFCC"
|
android:textColor="#B6BFCC"
|
||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -97,6 +97,8 @@
|
|||||||
<string name="error_cannot_delete_protected_directory">Cannot delete protected system directory</string>
|
<string name="error_cannot_delete_protected_directory">Cannot delete protected system directory</string>
|
||||||
<string name="delete_file_title">Delete this file permanently?</string>
|
<string name="delete_file_title">Delete this file permanently?</string>
|
||||||
<string name="delete_file_desc">Deleting this file will remove it permanently from your device.</string>
|
<string name="delete_file_desc">Deleting this file will remove it permanently from your device.</string>
|
||||||
|
<string name="delete_all_file_title">Delete selected files permanently?</string>
|
||||||
|
<string name="delete_all_file_desc">Deleting the selected files will remove them permanently from your device.</string>
|
||||||
<string name="file_information">File Information</string>
|
<string name="file_information">File Information</string>
|
||||||
<string name="file_information_desc">Everything you need to know about the file.</string>
|
<string name="file_information_desc">Everything you need to know about the file.</string>
|
||||||
<string name="last_modified">Last Modified</string>
|
<string name="last_modified">Last Modified</string>
|
||||||
@ -122,6 +124,8 @@
|
|||||||
<string name="delete_bookmarks_desc">Are you sure you want to delete all Bookmarks?</string>
|
<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="bookmark_loading">Loading bookmarks, please try again later</string>
|
||||||
<string name="no_files_yet">no files yet</string>
|
<string name="no_files_yet">no files yet</string>
|
||||||
|
<string name="no_favorites_yet">No favorites yet</string>
|
||||||
|
<string name="no_recent_reads_yet">No recent reads yet</string>
|
||||||
<string name="split_pdf">Split PDF</string>
|
<string name="split_pdf">Split PDF</string>
|
||||||
<string name="split">Split</string>
|
<string name="split">Split</string>
|
||||||
<string name="merge_pdf">Merge PDF</string>
|
<string name="merge_pdf">Merge PDF</string>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user