From 82270adb0733c7e51545fdec585a0399793108b9 Mon Sep 17 00:00:00 2001 From: ocean <503259349@qq.com> Date: Thu, 11 Sep 2025 11:46:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=A0=E9=99=A4=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=93=8D=E4=BD=9C=EF=BC=8C=E4=BC=98=E5=8C=96=E6=94=B6?= =?UTF-8?q?=E8=97=8F=E6=96=87=E4=BB=B6=E6=93=8D=E4=BD=9C=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96viewmodel=E6=95=B0=E6=8D=AE=E5=85=B1=E4=BA=AB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pdfreader/pro/app/model/DeleteResult.kt | 17 ++ .../pro/app/model/FileActionEvent.kt | 2 + .../pdfreader/pro/app/ui/act/MainActivity.kt | 18 +- .../pro/app/ui/act/PdfViewActivity.kt | 13 - .../pro/app/ui/dialog/DeleteDialogFragment.kt | 67 +++++ .../app/ui/dialog/ListMoreDialogFragment.kt | 28 +- .../pro/app/ui/dialog/RenameDialogFragment.kt | 22 +- .../pdfreader/pro/app/util/FileDeleteUtil.kt | 264 ++++++++++++++++++ .../all/pdfreader/pro/app/util/FileUtils.kt | 11 +- .../pro/app/viewmodel/PdfViewModel.kt | 38 ++- .../main/res/drawable/dr_click_btn_red_bg.xml | 20 ++ app/src/main/res/layout/dialog_delete.xml | 72 +++++ app/src/main/res/layout/dialog_list_more.xml | 1 + app/src/main/res/values/strings.xml | 13 + 14 files changed, 530 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/model/DeleteResult.kt create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/DeleteDialogFragment.kt create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/util/FileDeleteUtil.kt create mode 100644 app/src/main/res/drawable/dr_click_btn_red_bg.xml create mode 100644 app/src/main/res/layout/dialog_delete.xml diff --git a/app/src/main/java/com/all/pdfreader/pro/app/model/DeleteResult.kt b/app/src/main/java/com/all/pdfreader/pro/app/model/DeleteResult.kt new file mode 100644 index 0000000..0bb994f --- /dev/null +++ b/app/src/main/java/com/all/pdfreader/pro/app/model/DeleteResult.kt @@ -0,0 +1,17 @@ +package com.all.pdfreader.pro.app.model + +/** + * 文件删除结果封装类 + */ +data class DeleteResult( + val success: Boolean, + val errorMessage: String? = null, + val deletedFiles: Int = 0, + val deletedSize: Long = 0 +) { + companion object { + fun success(deletedFiles: Int = 1, deletedSize: Long = 0) = + DeleteResult(true, null, deletedFiles, deletedSize) + fun failure(message: String) = DeleteResult(false, message, 0, 0) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/model/FileActionEvent.kt b/app/src/main/java/com/all/pdfreader/pro/app/model/FileActionEvent.kt index c20334e..9f32b10 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/model/FileActionEvent.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/model/FileActionEvent.kt @@ -2,4 +2,6 @@ package com.all.pdfreader.pro.app.model sealed class FileActionEvent { data class Rename(val renameResult: RenameResult) : FileActionEvent() + data class Delete(val deleteResult: DeleteResult) : FileActionEvent() + data class Favorite(val isFavorite: Boolean) : FileActionEvent() } \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt index 34a8ba9..954aee0 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt @@ -66,7 +66,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback updateSelectedNav(activeFragment) } - private fun initObserve(){ + private fun initObserve() { //观察其余操作 viewModel.fileActionEvent.observe(this) { event -> when (event) { @@ -77,6 +77,22 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback showToast(event.renameResult.errorMessage.toString()) } } + + is FileActionEvent.Delete -> { + if (event.deleteResult.success) { + showToast(getString(R.string.delete_successfully)) + } else { + showToast(event.deleteResult.errorMessage.toString()) + } + } + + is FileActionEvent.Favorite -> { + if (event.isFavorite) { + showToast(getString(R.string.added_to_favorites)) + } else { + showToast(getString(R.string.removed_from_favorites)) + } + } } } } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt index 390d446..fc52292 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt @@ -59,19 +59,6 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList finish() } } - //观察其余操作 - viewModel.fileActionEvent.observe(this) { event -> - logDebug("fileActionEvent响应 $event") - when (event) { - is FileActionEvent.Rename -> { - if (event.renameResult.success) { - showToast(getString(R.string.rename_successfully)) - } else { - showToast(event.renameResult.errorMessage.toString()) - } - } - } - } } private fun loadPdf() { diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/DeleteDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/DeleteDialogFragment.kt new file mode 100644 index 0000000..86228a9 --- /dev/null +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/DeleteDialogFragment.kt @@ -0,0 +1,67 @@ +package com.all.pdfreader.pro.app.ui.dialog + +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.graphics.drawable.toDrawable +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.activityViewModels +import com.all.pdfreader.pro.app.R +import com.all.pdfreader.pro.app.databinding.DialogDeleteBinding +import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity +import com.all.pdfreader.pro.app.viewmodel.PdfViewModel + +class DeleteDialogFragment() : DialogFragment() { + + private lateinit var binding: DialogDeleteBinding + private val viewModel: PdfViewModel by activityViewModels() + private lateinit var pdfDocument: PdfDocumentEntity + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View? { + binding = DialogDeleteBinding.inflate(layoutInflater) + return binding.root + } + + override fun onStart() { + super.onStart() + dialog?.window?.apply { + // 去掉系统默认的背景 padding + setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + // 设置宽度为全屏减去 16dp + val margin = resources.getDimensionPixelSize(R.dimen.dialog_margin) // 16dp + val width = resources.displayMetrics.widthPixels - margin * 2 + setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT) + } + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.pdfDocument.value?.let { + pdfDocument = it + setupOnClick() + } ?: run { + showToast(getString(R.string.file_not)) + dismiss() + } + } + + private fun setupOnClick() { + binding.okBtn.setOnClickListener { + viewModel.deleteFile(pdfDocument.filePath) + dismiss() + } + binding.cancelBtn.setOnClickListener { + dismiss() + } + } + + private fun showToast(message: String) { + Toast.makeText(requireActivity(), message, Toast.LENGTH_SHORT).show() + } +} \ 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 0c3482f..9549f0d 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 @@ -28,7 +28,6 @@ import java.io.File class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment() { private lateinit var binding: DialogListMoreBinding - private val pdfRepository = PdfRepository.getInstance() private val viewModel: PdfViewModel by activityViewModels() private lateinit var pdfDocument: PdfDocumentEntity private var isFavorite: Boolean = false @@ -54,7 +53,6 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment() isFavorite = pdfDocument.isFavorite initUi() setupOnClick() - } ?: run { showToast(getString(R.string.file_not)) dismiss() @@ -84,28 +82,16 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment() binding.collectBtn.setClickWithAnimation(duration = 250) { isFavorite = !isFavorite updateCollectUi(isFavorite) - saveCollectState(isFavorite) - } - binding.renameFileBtn.setOnClickListener { - RenameDialogFragment(pdfDocument.filePath).show( - parentFragmentManager, - "ListMoreDialogFragment" - ) + viewModel.saveCollectState(pdfDocument.filePath, isFavorite) dismiss() } - } - - private fun saveCollectState(b: Boolean) { - pdfDocument = pdfDocument.copy( - isFavorite = b - ) - lifecycleScope.launch { - pdfRepository.updateFavoriteStatus(pdfDocument.filePath, pdfDocument.isFavorite) + binding.renameFileBtn.setOnClickListener { + RenameDialogFragment().show(parentFragmentManager, "ListMoreDialogFragment") + dismiss() } - if (b) { - showToast(getString(R.string.added_to_favorites)) - } else { - showToast(getString(R.string.removed_from_favorites)) + binding.deleteFileBtn.setOnClickListener { + DeleteDialogFragment().show(parentFragmentManager, "DeleteDialogFragment") + dismiss() } } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/RenameDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/RenameDialogFragment.kt index d44ea75..8bb3f1a 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/RenameDialogFragment.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/RenameDialogFragment.kt @@ -19,9 +19,7 @@ import com.all.pdfreader.pro.app.util.FileUtils import com.all.pdfreader.pro.app.viewmodel.PdfViewModel import java.io.File -class RenameDialogFragment( - private val filePath: String -) : DialogFragment() { +class RenameDialogFragment() : DialogFragment() { private lateinit var binding: DialogRenameFileBinding private val viewModel: PdfViewModel by activityViewModels() @@ -49,18 +47,14 @@ class RenameDialogFragment( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - viewModel.pdfDocument.observe(this) { document -> - document?.let { - pdfDocument = it - initView() - setupOnClick() - } ?: run { - showToast(getString(R.string.file_not)) - dismiss() - } + viewModel.pdfDocument.value?.let { + pdfDocument = it + initView() + setupOnClick() + } ?: run { + showToast(getString(R.string.file_not)) + dismiss() } - viewModel.getPDFDocument(filePath) } private fun initView() { diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/FileDeleteUtil.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/FileDeleteUtil.kt new file mode 100644 index 0000000..f5477a8 --- /dev/null +++ b/app/src/main/java/com/all/pdfreader/pro/app/util/FileDeleteUtil.kt @@ -0,0 +1,264 @@ +package com.all.pdfreader.pro.app.util + +import android.content.Context +import android.net.Uri +import android.provider.MediaStore +import android.util.Log +import com.all.pdfreader.pro.app.PRApp +import com.all.pdfreader.pro.app.R +import com.all.pdfreader.pro.app.model.DeleteResult +import com.all.pdfreader.pro.app.util.FileUtils.formatFileSize +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import kotlin.collections.forEach + +object FileDeleteUtil { + /** + * 删除文件(同步版本)- 功能完善的删除方法 + * + * @param file 要删除的文件 + * @param deleteEmptyParentDirs 是否删除空的父目录(默认false) + * @param dryRun 是否只模拟删除不实际执行(默认false) + * @return DeleteResult对象,包含删除结果和详细信息 + */ + fun deleteFile( + file: File, + deleteEmptyParentDirs: Boolean = false, + dryRun: Boolean = false + ): DeleteResult { + if (!file.exists()) { + Log.e("ocean", "❌ File does not exist: ${file.path}") + return DeleteResult.failure(PRApp.getStringRes(R.string.error_file_not_exist)) + } + + if (!file.canWrite()) { + Log.e("ocean", "❌ No write permission for file: ${file.path}") + return DeleteResult.failure(PRApp.getStringRes(R.string.error_no_write_permission)) + } + + // 安全检查:防止删除重要系统目录 + if (isProtectedPath(file)) { + Log.e("ocean", "❌ Cannot delete protected path: ${file.path}") + return DeleteResult.failure(PRApp.getStringRes(R.string.error_cannot_delete_protected_path)) + } + + // 如果是目录,检查是否为空(避免误删非空目录) + if (file.isDirectory) { + val files = file.listFiles() + if (!files.isNullOrEmpty()) { + Log.e("ocean", "❌ Directory is not empty: ${file.path} (${files.size} items)") + return DeleteResult.failure(PRApp.getStringRes(R.string.error_directory_not_empty)) + } + } + + // 计算要删除的文件大小 + val fileSize = file.length() + + if (dryRun) { + Log.d("ocean", "🔍 Dry run - would delete: ${file.path} (${formatFileSize(fileSize)})") + return DeleteResult.success(1, fileSize) + } + + return try { + val success = file.delete() + if (success) { + Log.d( + "ocean", + "✅ File deleted successfully: ${file.path} (${formatFileSize(fileSize)})" + ) + + // 如果需要,删除空的父目录 + if (deleteEmptyParentDirs) { + deleteEmptyParentDirectories(file.parentFile) + } + + DeleteResult.success(1, fileSize) + } else { + Log.e("ocean", "❌ Failed to delete file: ${file.path}") + DeleteResult.failure(PRApp.getStringRes(R.string.error_failed_delete_file)) + } + } catch (e: SecurityException) { + Log.e("ocean", "❌ Security exception while deleting: ${e.message}") + DeleteResult.failure(PRApp.getStringRes(R.string.error_permission_delete_file)) + } catch (e: Exception) { + Log.e("ocean", "❌ Exception while deleting file: ${e.message}") + DeleteResult.failure( + PRApp.getStringRes( + R.string.error_file_delete_failed, + e.message.toString() + ) + ) + } + } + + /** + * 删除文件(异步版本)- 功能完善的删除方法 + * + * @param file 要删除的文件 + * @param deleteEmptyParentDirs 是否删除空的父目录(默认false) + * @param dryRun 是否只模拟删除不实际执行(默认false) + * @return DeleteResult对象,包含删除结果和详细信息 + */ + suspend fun deleteFileAsync( + file: File, + deleteEmptyParentDirs: Boolean = false, + dryRun: Boolean = false + ): DeleteResult = + withContext(Dispatchers.IO) { + deleteFile(file, deleteEmptyParentDirs, dryRun) + } + + /** + * 批量删除文件 + * + * @param files 要删除的文件列表 + * @param deleteEmptyParentDirs 是否删除空的父目录(默认false) + * @param dryRun 是否只模拟删除不实际执行(默认false) + * @return DeleteResult对象,包含总体删除结果 + */ + fun deleteFiles( + files: List, + deleteEmptyParentDirs: Boolean = false, + dryRun: Boolean = false + ): DeleteResult { + if (files.isEmpty()) { + return DeleteResult.success(0, 0) + } + + var totalDeleted = 0 + var totalSize = 0L + val errors = mutableListOf() + + files.forEach { file -> + val result = deleteFile(file, deleteEmptyParentDirs, dryRun) + if (result.success) { + totalDeleted += result.deletedFiles + totalSize += result.deletedSize + } else { + errors.add("${file.name}: ${result.errorMessage}") + } + } + + return if (errors.isEmpty()) { + DeleteResult.success(totalDeleted, totalSize) + } else { + val error = "\n${errors.joinToString("\n")}" + DeleteResult.failure(PRApp.getStringRes(R.string.error_some_files_not_deleted, error)) + } + } + + /** + * 删除目录及其所有内容(递归删除) + * + * @param directory 要删除的目录 + * @param dryRun 是否只模拟删除不实际执行(默认false) + * @return DeleteResult对象,包含删除结果 + */ + fun deleteDirectoryRecursive(directory: File, dryRun: Boolean = false): DeleteResult { + if (!directory.exists()) { + return DeleteResult.failure(PRApp.getStringRes(R.string.error_directory_does_not_exist)) + } + + if (!directory.isDirectory) { + return DeleteResult.failure(PRApp.getStringRes(R.string.error_path_not_directory)) + } + + if (isProtectedPath(directory)) { + return DeleteResult.failure(PRApp.getStringRes(R.string.error_cannot_delete_protected_directory)) + } + + var deletedCount = 0 + var deletedSize = 0L + + // 先删除目录下的所有文件和子目录 + directory.listFiles()?.forEach { file -> + val result = if (file.isDirectory) { + deleteDirectoryRecursive(file, dryRun) + } else { + deleteFile(file, false, dryRun) + } + + if (result.success) { + deletedCount += result.deletedFiles + deletedSize += result.deletedSize + } + } + + // 最后删除空目录本身 + val dirResult = deleteFile(directory, false, dryRun) + if (dirResult.success) { + deletedCount += 1 + } + + return DeleteResult.success(deletedCount, deletedSize) + } + + /** + * 检查是否为受保护的路径(防止误删系统文件) + */ + private fun isProtectedPath(file: File): Boolean { + val protectedPaths = listOf( + "/system", + "/data", + "/vendor", + "/sbin", + "/etc", + "/dev", + "/proc", + "/sys", + "/root", + "/acct", + "/mnt", + "/storage/emulated/0/Android", + "/storage/emulated/0/.android_secure" + ) + + val absolutePath = file.absolutePath + return protectedPaths.any { protectedPath -> + absolutePath.startsWith(protectedPath) + } + } + + /** + * 删除空的父目录(向上递归) + */ + private fun deleteEmptyParentDirectories(directory: File?) { + directory?.let { dir -> + if (dir.exists() && dir.isDirectory && dir.listFiles()?.isEmpty() == true) { + val parent = dir.parentFile + if (dir.delete()) { + Log.d("ocean", "✅ Deleted empty parent directory: ${dir.path}") + deleteEmptyParentDirectories(parent) + } + } + } + } + + /** + * 安全删除文件到回收站(如果系统支持) + * + * @param context 上下文 + * @param file 要删除的文件 + * @return DeleteResult对象 + */ + fun moveToTrash(context: Context, file: File): DeleteResult { + return try { + // Android 10+ 支持系统回收站 + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + MediaStore.createTrashRequest( + context.contentResolver, + listOf(Uri.fromFile(file)), + true + ) + // 注意:这里需要Activity来启动IntentSender + DeleteResult.success(1, file.length()) + } else { + // 低版本直接删除 + deleteFile(file) + } + } catch (e: Exception) { + DeleteResult.failure("Failed to move file to trash: ${e.message}") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/FileUtils.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/FileUtils.kt index ed42f9b..7411d6f 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/util/FileUtils.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/util/FileUtils.kt @@ -6,7 +6,7 @@ import android.graphics.pdf.PdfRenderer import android.net.Uri import android.os.ParcelFileDescriptor import android.provider.MediaStore -import android.provider.OpenableColumns +import android.provider.OpenableColumns import android.util.Log import com.all.pdfreader.pro.app.PRApp import com.all.pdfreader.pro.app.R @@ -35,7 +35,6 @@ object FileUtils { // 外部存储根目录(需要权限) if (StoragePermissionHelper.hasBasicStoragePermission(context)) { Log.d("ocean", "📂 扫描外部存储目录...") - // 扫描常见的PDF存储目录 val externalStorage = android.os.Environment.getExternalStorageDirectory() scanCommonDirectories(externalStorage, pdfFiles) @@ -431,7 +430,11 @@ object FileUtils { RenameResult.failure(PRApp.getStringRes(R.string.error_insufficient_permission)) } catch (e: Exception) { Log.e("ocean", "❌ File rename exception: ${e.message}") - RenameResult.failure(PRApp.getStringRes(R.string.error_file_rename_exception, e.message.toString())) + RenameResult.failure( + PRApp.getStringRes( + R.string.error_file_rename_exception, e.message.toString() + ) + ) } } @@ -516,7 +519,7 @@ object FileUtils { } tempFile } catch (e: Exception) { - Log.e("ocean", "无法获取文件: ${e.message}", e) +// Log.e("ocean", "无法获取文件: ${e.message}", e) null } } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/viewmodel/PdfViewModel.kt b/app/src/main/java/com/all/pdfreader/pro/app/viewmodel/PdfViewModel.kt index e45cc52..d82dba9 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/viewmodel/PdfViewModel.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/viewmodel/PdfViewModel.kt @@ -8,9 +8,12 @@ import androidx.lifecycle.viewModelScope import com.all.pdfreader.pro.app.model.FileActionEvent import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.room.repository.PdfRepository +import com.all.pdfreader.pro.app.util.FileDeleteUtil import com.all.pdfreader.pro.app.util.FileUtils import com.all.pdfreader.pro.app.util.LogUtil +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File class PdfViewModel : ViewModel() { @@ -23,7 +26,6 @@ class PdfViewModel : ViewModel() { private val _fileActionEvent = MutableLiveData() val fileActionEvent: LiveData = _fileActionEvent - fun getPDFDocument(filePath: String) { viewModelScope.launch { val document = pdfRepository.getDocumentByPath(filePath) @@ -37,7 +39,10 @@ class PdfViewModel : ViewModel() { viewModelScope.launch { val oldFile = File(filePath) val renameResult = FileUtils.renameFile(oldFile, newName) - Log.d("ocean", "renamePdf->oldFile: $oldFile, newName=$newName, renameResult=$renameResult") + Log.d( + "ocean", + "renamePdf->oldFile: $oldFile, newName=$newName, renameResult=$renameResult" + ) if (renameResult.success) {//修改成功更新数据库 val finalName = if (newName.contains('.')) { // 用户提供了后缀,使用用户的 @@ -56,7 +61,34 @@ class PdfViewModel : ViewModel() { Log.d("ocean", "renamePdf->newFilePath: $newFilePath, finalName=$finalName") pdfRepository.updateFilePathAndFileName(filePath, newFilePath, finalName) } - _fileActionEvent.postValue(FileActionEvent.Rename(renameResult)) + + withContext(Dispatchers.Main) { + _fileActionEvent.postValue(FileActionEvent.Rename(renameResult)) + } + } + } + + fun deleteFile(filePath: String) { + viewModelScope.launch { + val file = File(filePath) + val deleteResult = FileDeleteUtil.deleteFile(file) + Log.d("ocean", "deleteFile->file: $file, deleteResult=$deleteResult") + if (deleteResult.success) { + Log.d("ocean", "文件已删除,清除数据库数据") + pdfRepository.deleteDocument(filePath) + } + withContext(Dispatchers.Main) { + _fileActionEvent.postValue(FileActionEvent.Delete(deleteResult)) + } + } + } + + fun saveCollectState(filePath: String, isFavorite: Boolean) { + viewModelScope.launch { + pdfRepository.updateFavoriteStatus(filePath, isFavorite) + withContext(Dispatchers.Main) { + _fileActionEvent.postValue(FileActionEvent.Favorite(isFavorite)) + } } } } \ No newline at end of file diff --git a/app/src/main/res/drawable/dr_click_btn_red_bg.xml b/app/src/main/res/drawable/dr_click_btn_red_bg.xml new file mode 100644 index 0000000..d0d2f29 --- /dev/null +++ b/app/src/main/res/drawable/dr_click_btn_red_bg.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_delete.xml b/app/src/main/res/layout/dialog_delete.xml new file mode 100644 index 0000000..0a2c2aa --- /dev/null +++ b/app/src/main/res/layout/dialog_delete.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + \ 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 83ad345..09f3ebc 100644 --- a/app/src/main/res/layout/dialog_list_more.xml +++ b/app/src/main/res/layout/dialog_list_more.xml @@ -269,6 +269,7 @@ Added to Favorite Removed from Favorites Delete File + Delete Set Password Remove Password Duplicate File @@ -57,6 +58,7 @@ A file with the same name already exists File name cannot start or end with space Rename successfully + Delete successfully File does not exist No write permission for file Invalid file name @@ -65,4 +67,15 @@ File rename failed Insufficient permission to rename file File rename exception: %1$s + Cannot delete protected system path + Directory is not empty + Failed to delete file + Insufficient permission to delete file + Delete operation failed: %1$s + Some files could not be deleted: %1$s + Directory does not exist + Path is not a directory + Cannot delete protected system directory + Delete this file permanently? + Deleting this file will remove it permanently from your device. \ No newline at end of file