添加批量操作等功能。

This commit is contained in:
ocean 2025-09-29 14:50:04 +08:00
parent c7af68a8ee
commit e20a8ab016
14 changed files with 238 additions and 30 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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)
}
}
/** /**
* 添加点击动画点击立即执行逻辑动画期间禁用点击 * 添加点击动画点击立即执行逻辑动画期间禁用点击
* *

View File

@ -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
}
}
} }

View File

@ -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))
@ -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)
} }
} }

View File

@ -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>

View File

@ -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>

View File

@ -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>