update
This commit is contained in:
parent
369dc9d129
commit
ded6be90bf
@ -18,7 +18,7 @@
|
|||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".PDFReaderApplication"
|
android:name=".PRApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
|||||||
@ -2,17 +2,25 @@ package com.all.pdfreader.pro.app
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||||
import com.all.pdfreader.pro.app.util.FileChangeObserver
|
import com.all.pdfreader.pro.app.util.FileChangeObserver
|
||||||
|
|
||||||
class PDFReaderApplication : Application() {
|
class PRApp : Application() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private lateinit var instance: PDFReaderApplication
|
private lateinit var instance: PRApp
|
||||||
fun getInstance(): PDFReaderApplication = instance
|
|
||||||
|
fun getInstance(): PRApp = instance
|
||||||
|
|
||||||
fun getContext(): Context = instance.applicationContext
|
fun getContext(): Context = instance.applicationContext
|
||||||
|
|
||||||
//是新创建了界面,则需要全盘扫描,进入onResume判定,扫描方法调用后置为false
|
//是新创建了界面,则需要全盘扫描,进入onResume判定,扫描方法调用后置为false
|
||||||
var isNeedFullScan = false
|
var isNeedFullScan = false
|
||||||
|
|
||||||
|
fun getStringRes(@StringRes resId: Int, vararg formatArgs: Any): String {
|
||||||
|
return instance.getString(resId, *formatArgs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var fileChangeObserver: FileChangeObserver
|
private lateinit var fileChangeObserver: FileChangeObserver
|
||||||
@ -6,7 +6,7 @@ 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
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.all.pdfreader.pro.app.PDFReaderApplication
|
import com.all.pdfreader.pro.app.PRApp
|
||||||
import com.all.pdfreader.pro.app.R
|
import com.all.pdfreader.pro.app.R
|
||||||
import com.all.pdfreader.pro.app.databinding.ActivityMainBinding
|
import com.all.pdfreader.pro.app.databinding.ActivityMainBinding
|
||||||
import com.all.pdfreader.pro.app.ui.dialog.PermissionDialogFragment
|
import com.all.pdfreader.pro.app.ui.dialog.PermissionDialogFragment
|
||||||
@ -140,7 +140,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
logDebug("main onResume")
|
logDebug("main onResume")
|
||||||
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
||||||
// 有授权才初始化文件变化监听器
|
// 有授权才初始化文件变化监听器
|
||||||
PDFReaderApplication.getInstance().startFileChangeObserving()
|
PRApp.getInstance().startFileChangeObserving()
|
||||||
scanningStrategy()
|
scanningStrategy()
|
||||||
binding.pnLayout.visibility = View.GONE
|
binding.pnLayout.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
@ -156,7 +156,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
private fun scanningStrategy() {
|
private fun scanningStrategy() {
|
||||||
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
pdfScanner.scanAndLoadPdfFiles(PDFReaderApplication.isNeedFullScan)
|
pdfScanner.scanAndLoadPdfFiles(PRApp.isNeedFullScan)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logDebug("❌ 权限不足,跳过扫描")
|
logDebug("❌ 权限不足,跳过扫描")
|
||||||
@ -183,7 +183,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
|||||||
logDebug("main onPermissionGranted")
|
logDebug("main onPermissionGranted")
|
||||||
//授权成功后:隐藏授权提示,开始扫描文件
|
//授权成功后:隐藏授权提示,开始扫描文件
|
||||||
binding.pnLayout.visibility = View.GONE
|
binding.pnLayout.visibility = View.GONE
|
||||||
PDFReaderApplication.getInstance().startFileChangeObserving()
|
PRApp.getInstance().startFileChangeObserving()
|
||||||
scanningStrategy()
|
scanningStrategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,17 +9,11 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.all.pdfreader.pro.app.R
|
import com.all.pdfreader.pro.app.R
|
||||||
import com.all.pdfreader.pro.app.databinding.DialogListMoreBinding
|
import com.all.pdfreader.pro.app.databinding.DialogListMoreBinding
|
||||||
import com.all.pdfreader.pro.app.databinding.DialogPermissionBinding
|
|
||||||
import com.all.pdfreader.pro.app.databinding.DialogSortBinding
|
|
||||||
import com.all.pdfreader.pro.app.model.SortConfig
|
|
||||||
import com.all.pdfreader.pro.app.model.SortDirection
|
|
||||||
import com.all.pdfreader.pro.app.model.SortField
|
|
||||||
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||||
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||||
import com.all.pdfreader.pro.app.sp.AppStore
|
|
||||||
import com.all.pdfreader.pro.app.ui.act.MainActivity.SortableFragment
|
|
||||||
import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
|
import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
|
||||||
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.FileUtils
|
||||||
import com.all.pdfreader.pro.app.util.FileUtils.toFormatFileSize
|
import com.all.pdfreader.pro.app.util.FileUtils.toFormatFileSize
|
||||||
import com.all.pdfreader.pro.app.util.FileUtils.toSlashDate
|
import com.all.pdfreader.pro.app.util.FileUtils.toSlashDate
|
||||||
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
||||||
@ -28,6 +22,7 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
|||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment() {
|
class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
@ -91,11 +86,10 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment()
|
|||||||
saveCollectState(isFavorite)
|
saveCollectState(isFavorite)
|
||||||
}
|
}
|
||||||
binding.renameFileBtn.setOnClickListener {
|
binding.renameFileBtn.setOnClickListener {
|
||||||
RenameDialogFragment(pdfDocument.filePath, onOkClick = {
|
RenameDialogFragment(pdfDocument.filePath).show(
|
||||||
|
parentFragmentManager,
|
||||||
}, onCancelClick = {
|
"ListMoreDialogFragment"
|
||||||
|
)
|
||||||
}).show(parentFragmentManager, "ListMoreDialogFragment")
|
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,9 @@ package com.all.pdfreader.pro.app.ui.dialog
|
|||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
|
import android.text.TextWatcher
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -15,14 +17,13 @@ import com.all.pdfreader.pro.app.databinding.DialogPdfPasswordProtectionBinding
|
|||||||
import com.all.pdfreader.pro.app.databinding.DialogRenameFileBinding
|
import com.all.pdfreader.pro.app.databinding.DialogRenameFileBinding
|
||||||
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||||
import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard
|
import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard
|
||||||
|
import com.all.pdfreader.pro.app.util.FileUtils
|
||||||
import com.all.pdfreader.pro.app.util.FileUtils.isPdfPasswordCorrect
|
import com.all.pdfreader.pro.app.util.FileUtils.isPdfPasswordCorrect
|
||||||
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class RenameDialogFragment(
|
class RenameDialogFragment(
|
||||||
private val filePath: String,
|
private val filePath: String,
|
||||||
private val onOkClick: () -> Unit,
|
|
||||||
private val onCancelClick: () -> Unit
|
|
||||||
) : DialogFragment() {
|
) : DialogFragment() {
|
||||||
|
|
||||||
private lateinit var binding: DialogRenameFileBinding
|
private lateinit var binding: DialogRenameFileBinding
|
||||||
@ -67,7 +68,7 @@ class RenameDialogFragment(
|
|||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
binding.etName.showKeyboard()
|
binding.etName.showKeyboard()
|
||||||
binding.etName.setText(pdfDocument.fileName)
|
binding.etName.setText(FileUtils.removeFileExtension(pdfDocument.fileName))
|
||||||
// 保持光标在末尾
|
// 保持光标在末尾
|
||||||
binding.etName.setSelection(binding.etName.text?.length ?: 0)
|
binding.etName.setSelection(binding.etName.text?.length ?: 0)
|
||||||
|
|
||||||
@ -75,15 +76,28 @@ class RenameDialogFragment(
|
|||||||
|
|
||||||
private fun setupOnClick() {
|
private fun setupOnClick() {
|
||||||
binding.tvCancel.setOnClickListener {
|
binding.tvCancel.setOnClickListener {
|
||||||
onCancelClick()
|
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
binding.tvConfirm.setOnClickListener {
|
binding.tvConfirm.setOnClickListener {
|
||||||
val text = binding.etName.text.toString()
|
val text = binding.etName.text.toString()
|
||||||
if (validateEnter(text)) {
|
if (validateEnter(text)) {
|
||||||
|
val renameResult =
|
||||||
|
FileUtils.renameFile(File(pdfDocument.filePath), text)
|
||||||
|
if (renameResult.success) {
|
||||||
|
showToast(getString(R.string.rename_successfully))
|
||||||
|
} else {
|
||||||
|
showToast(renameResult.errorMessage.toString())
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
binding.etName.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
binding.tilName.error = null
|
||||||
|
}
|
||||||
|
override fun afterTextChanged(s: Editable?) {}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateEnter(name: String): Boolean {
|
private fun validateEnter(name: String): Boolean {
|
||||||
@ -93,14 +107,14 @@ class RenameDialogFragment(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 名字未做修改
|
// 名字未做修改(因展示的时候去掉了扩展名,则判断的时候也去掉)
|
||||||
if (name == pdfDocument.fileName) {
|
if (name == FileUtils.removeFileExtension(pdfDocument.fileName)) {
|
||||||
binding.tilName.error = getString(R.string.name_not_changed)
|
binding.tilName.error = getString(R.string.name_not_changed)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 含有非法字符
|
// 含有非法字符
|
||||||
val invalidChars = "[/\\\\:*?\"<>|]".toRegex()
|
val invalidChars = "[/\\\\:*?\"<>|.]".toRegex()
|
||||||
if (invalidChars.containsMatchIn(name)) {
|
if (invalidChars.containsMatchIn(name)) {
|
||||||
binding.tilName.error = getString(R.string.name_invalid_chars)
|
binding.tilName.error = getString(R.string.name_invalid_chars)
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -8,16 +8,34 @@ import android.os.ParcelFileDescriptor
|
|||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.all.pdfreader.pro.app.PRApp
|
||||||
|
import com.all.pdfreader.pro.app.R
|
||||||
import com.shockwave.pdfium.PdfiumCore
|
import com.shockwave.pdfium.PdfiumCore
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件重命名结果封装类
|
||||||
|
*/
|
||||||
|
data class RenameResult(
|
||||||
|
val success: Boolean,
|
||||||
|
val errorMessage: String? = null
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun success() = RenameResult(true)
|
||||||
|
fun failure(message: String) = RenameResult(false, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object FileUtils {
|
object FileUtils {
|
||||||
|
|
||||||
fun scanPdfFiles(context: Context): List<File> {
|
fun scanPdfFiles(context: Context): List<File> {
|
||||||
val pdfFiles = mutableListOf<File>()
|
val pdfFiles = mutableListOf<File>()
|
||||||
|
|
||||||
@ -345,7 +363,8 @@ object FileUtils {
|
|||||||
try {
|
try {
|
||||||
renderer?.close()
|
renderer?.close()
|
||||||
fd?.close()
|
fd?.close()
|
||||||
} catch (_: Exception) {}
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,13 +383,132 @@ object FileUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重命名文件(同步版本)- 智能处理后缀
|
||||||
|
*
|
||||||
|
* @param file 要重命名的文件
|
||||||
|
* @param newName 新文件名(可包含或不包含后缀,会自动处理)
|
||||||
|
* @return RenameResult对象,包含成功状态和错误信息
|
||||||
|
*/
|
||||||
|
fun renameFile(file: File, newName: String): RenameResult {
|
||||||
|
if (!file.exists()) {
|
||||||
|
return RenameResult.failure(PRApp.getStringRes(R.string.error_file_not_exist))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.canWrite()) {
|
||||||
|
return RenameResult.failure(PRApp.getStringRes(R.string.error_no_write_permission))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能处理后缀:如果用户没有提供后缀,保留原文件后缀
|
||||||
|
val finalName = if (newName.contains('.')) {
|
||||||
|
// 用户提供了后缀,使用用户的
|
||||||
|
newName
|
||||||
|
} else {
|
||||||
|
// 用户没有提供后缀,自动添加原文件后缀
|
||||||
|
val originalExtension = file.extension
|
||||||
|
if (originalExtension.isNotEmpty()) {
|
||||||
|
"$newName.$originalExtension"
|
||||||
|
} else {
|
||||||
|
newName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证新文件名
|
||||||
|
val validatedName = validateFileName(finalName) ?: run {
|
||||||
|
return RenameResult.failure(PRApp.getStringRes(R.string.error_invalid_file_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取原文件的父目录
|
||||||
|
val parentDir = file.parentFile ?: run {
|
||||||
|
return RenameResult.failure(PRApp.getStringRes(R.string.error_no_parent_directory))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建新文件对象
|
||||||
|
val newFile = File(parentDir, validatedName)
|
||||||
|
|
||||||
|
// 检查目标文件是否已存在
|
||||||
|
if (newFile.exists()) {
|
||||||
|
return RenameResult.failure(PRApp.getStringRes(R.string.error_target_file_exists))
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
if (file.renameTo(newFile)) {
|
||||||
|
Log.d("ocean", "✅ File renamed successfully: ${file.name} -> $validatedName")
|
||||||
|
RenameResult.success()
|
||||||
|
} else {
|
||||||
|
Log.e("ocean", "❌ File rename failed: ${file.path}")
|
||||||
|
RenameResult.failure(PRApp.getStringRes(R.string.error_file_rename_failed))
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重命名文件(异步版本)
|
||||||
|
*
|
||||||
|
* @param file 要重命名的文件
|
||||||
|
* @param newName 新文件名(可包含或不包含后缀,会自动处理)
|
||||||
|
* @return RenameResult对象,包含成功状态和错误信息
|
||||||
|
*/
|
||||||
|
suspend fun renameFileAsync(file: File, newName: String): RenameResult =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
renameFile(file, newName)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除文件扩展名(后缀)
|
||||||
|
*
|
||||||
|
* @param fileName 原始文件名
|
||||||
|
* @return 不包含扩展名的文件名
|
||||||
|
*/
|
||||||
|
fun removeFileExtension(fileName: String): String {
|
||||||
|
val lastDotIndex = fileName.lastIndexOf('.')
|
||||||
|
return if (lastDotIndex > 0 && lastDotIndex < fileName.length - 1) {
|
||||||
|
// 确保点号不在开头,且后面还有字符
|
||||||
|
fileName.substring(0, lastDotIndex)
|
||||||
|
} else {
|
||||||
|
// 没有扩展名,或者点号在开头(如.hidden文件)
|
||||||
|
fileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证并清理文件名
|
||||||
|
*
|
||||||
|
* @param fileName 原始文件名
|
||||||
|
* @return 验证通过的文件名,如果无效则返回null
|
||||||
|
*/
|
||||||
|
fun validateFileName(fileName: String): String? {
|
||||||
|
if (fileName.isBlank()) return null
|
||||||
|
|
||||||
|
// 移除文件系统中的非法字符
|
||||||
|
val cleanedName = fileName.replace(Regex("[<>:\"|?*\u0000-\u001f]"), "_")
|
||||||
|
.replace(Regex("^\\.+"), "") // 移除开头的点
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
if (cleanedName.isEmpty() || cleanedName == "." || cleanedName == "..") {
|
||||||
|
return "NewFile"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保文件名长度合理(通常文件系统支持255字符)
|
||||||
|
if (cleanedName.length > 255) {
|
||||||
|
return cleanedName.substring(0, 255)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanedName
|
||||||
|
}
|
||||||
|
|
||||||
fun getFileFromUri(context: Context, uri: Uri): File? {
|
fun getFileFromUri(context: Context, uri: Uri): File? {
|
||||||
// 先尝试通过 DATA 字段获取
|
// 先尝试通过 DATA 字段获取
|
||||||
val projection = arrayOf(MediaStore.Files.FileColumns.DATA)
|
val projection = arrayOf(MediaStore.Files.FileColumns.DATA)
|
||||||
context.contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
|
context.contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
|
val path =
|
||||||
|
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
|
||||||
if (!path.isNullOrEmpty()) {
|
if (!path.isNullOrEmpty()) {
|
||||||
val file = File(path)
|
val file = File(path)
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import android.graphics.Color
|
|||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.graphics.createBitmap
|
import androidx.core.graphics.createBitmap
|
||||||
import com.all.pdfreader.pro.app.PDFReaderApplication
|
import com.all.pdfreader.pro.app.PRApp
|
||||||
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||||
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||||
import com.all.pdfreader.pro.app.util.FileUtils.isPdfEncrypted
|
import com.all.pdfreader.pro.app.util.FileUtils.isPdfEncrypted
|
||||||
@ -76,7 +76,10 @@ class PdfScanner(
|
|||||||
LogUtil.logDebug(TAG, "异步获取图片更新数据")
|
LogUtil.logDebug(TAG, "异步获取图片更新数据")
|
||||||
val newThumbnail = generateFastThumbnail(context, file)
|
val newThumbnail = generateFastThumbnail(context, file)
|
||||||
if (newThumbnail != null && doc.thumbnailPath != newThumbnail) {
|
if (newThumbnail != null && doc.thumbnailPath != newThumbnail) {
|
||||||
pdfRepository.updateThumbnailPath(doc.filePath, newThumbnail)
|
pdfRepository.updateThumbnailPath(
|
||||||
|
doc.filePath,
|
||||||
|
newThumbnail
|
||||||
|
)
|
||||||
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,9 +117,7 @@ class PdfScanner(
|
|||||||
"🔄处理文件 ${index + 1}/${allFiles.size}: ${file.name} - ${file.absolutePath}"
|
"🔄处理文件 ${index + 1}/${allFiles.size}: ${file.name} - ${file.absolutePath}"
|
||||||
)
|
)
|
||||||
if (FileUtils.isPdfFile(file)) {
|
if (FileUtils.isPdfFile(file)) {
|
||||||
val existingDoc =
|
val existingDoc = pdfRepository.getDocumentByPath(file.absolutePath)
|
||||||
pdfRepository.getDocumentByPath(file.absolutePath)
|
|
||||||
|
|
||||||
if (existingDoc == null) {
|
if (existingDoc == null) {
|
||||||
LogUtil.logDebug(
|
LogUtil.logDebug(
|
||||||
TAG, "🆕发现新PDF文件: ${file.name}"
|
TAG, "🆕发现新PDF文件: ${file.name}"
|
||||||
@ -145,16 +146,19 @@ class PdfScanner(
|
|||||||
LogUtil.logDebug(TAG, " ✅ 已保存到数据库: ${file.name}")
|
LogUtil.logDebug(TAG, " ✅ 已保存到数据库: ${file.name}")
|
||||||
|
|
||||||
if (!isPassword) {//没有密码的情况下才去获取缩略图
|
if (!isPassword) {//没有密码的情况下才去获取缩略图
|
||||||
launch(Dispatchers.IO){
|
launch(Dispatchers.IO) {
|
||||||
val newThumbnail = generateFastThumbnail(context, file)
|
val newThumbnail = generateFastThumbnail(context, file)
|
||||||
if (newThumbnail != null && document.thumbnailPath != newThumbnail) {
|
if (newThumbnail != null && document.thumbnailPath != newThumbnail) {
|
||||||
pdfRepository.updateThumbnailPath(document.filePath, newThumbnail)
|
pdfRepository.updateThumbnailPath(
|
||||||
|
document.filePath,
|
||||||
|
newThumbnail
|
||||||
|
)
|
||||||
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogUtil.logDebug(TAG, " 📋 文件已存在: ${file.name}")
|
LogUtil.logDebug(TAG, " 📋 文件已存在数据库: ${file.name}")
|
||||||
// 🔹 文件已存在,检查是否需要更新
|
// 🔹 文件已存在,检查是否需要更新
|
||||||
var needUpdate = false
|
var needUpdate = false
|
||||||
var updatedDoc = existingDoc.copy()
|
var updatedDoc = existingDoc.copy()
|
||||||
@ -192,7 +196,10 @@ class PdfScanner(
|
|||||||
LogUtil.logDebug(TAG, "异步获取图片更新数据")
|
LogUtil.logDebug(TAG, "异步获取图片更新数据")
|
||||||
val newThumbnail = generateFastThumbnail(context, file)
|
val newThumbnail = generateFastThumbnail(context, file)
|
||||||
if (newThumbnail != null && existingDoc.thumbnailPath != newThumbnail) {
|
if (newThumbnail != null && existingDoc.thumbnailPath != newThumbnail) {
|
||||||
pdfRepository.updateThumbnailPath(existingDoc.filePath, newThumbnail)
|
pdfRepository.updateThumbnailPath(
|
||||||
|
existingDoc.filePath,
|
||||||
|
newThumbnail
|
||||||
|
)
|
||||||
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,16 +210,23 @@ class PdfScanner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 打印数据库中的总记录数
|
//最后过滤数据库的文件,文件不存在则删除记录,打印数据库中的总记录数
|
||||||
pdfRepository.getAllDocumentsOnce().forEach { doc ->
|
pdfRepository.getAllDocumentsOnce().forEach { doc ->
|
||||||
LogUtil.logDebug(
|
val file = File(doc.filePath)
|
||||||
TAG,
|
if (!file.exists()) {
|
||||||
" 📖 ${doc.fileName} - ${doc.filePath} - ${doc.pageCount}页 - ${
|
// 文件不存在 → 删除数据库记录
|
||||||
FileUtils.formatFileSize(
|
LogUtil.logDebug(TAG, "最终过滤:文件不存在 -> ${doc.fileName}, 删除记录")
|
||||||
doc.fileSize
|
pdfRepository.deleteDocument(doc.filePath)
|
||||||
)
|
} else {
|
||||||
} - ${doc.thumbnailPath}"
|
LogUtil.logDebug(
|
||||||
)
|
TAG,
|
||||||
|
" 📖 ${doc.fileName} - ${doc.filePath} - ${doc.pageCount}页 - ${
|
||||||
|
FileUtils.formatFileSize(
|
||||||
|
doc.fileSize
|
||||||
|
)
|
||||||
|
} - ${doc.thumbnailPath}"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 标记扫描完成
|
// 标记扫描完成
|
||||||
@ -233,7 +247,7 @@ class PdfScanner(
|
|||||||
LogUtil.logDebug(
|
LogUtil.logDebug(
|
||||||
TAG, "$string 本次扫描耗时: $scannerTime ms (${scannerTime / 1000.0} 秒)"
|
TAG, "$string 本次扫描耗时: $scannerTime ms (${scannerTime / 1000.0} 秒)"
|
||||||
)
|
)
|
||||||
PDFReaderApplication.isNeedFullScan = false
|
PRApp.isNeedFullScan = false
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
callback.invoke(true)
|
callback.invoke(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
android:id="@+id/etName"
|
android:id="@+id/etName"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
android:fontFamily="@font/poppins_regular"
|
android:fontFamily="@font/poppins_regular"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
|||||||
@ -56,4 +56,13 @@
|
|||||||
<string name="name_too_long">File name is too long (max 255 characters)</string>
|
<string name="name_too_long">File name is too long (max 255 characters)</string>
|
||||||
<string name="name_already_exists">A file with the same name already exists</string>
|
<string name="name_already_exists">A file with the same name already exists</string>
|
||||||
<string name="name_start_end_space">File name cannot start or end with space</string>
|
<string name="name_start_end_space">File name cannot start or end with space</string>
|
||||||
|
<string name="rename_successfully">Rename successfully</string>
|
||||||
|
<string name="error_file_not_exist">File does not exist</string>
|
||||||
|
<string name="error_no_write_permission">No write permission for file</string>
|
||||||
|
<string name="error_invalid_file_name">Invalid file name</string>
|
||||||
|
<string name="error_no_parent_directory">Cannot get parent directory</string>
|
||||||
|
<string name="error_target_file_exists">Target file already exists</string>
|
||||||
|
<string name="error_file_rename_failed">File rename failed</string>
|
||||||
|
<string name="error_insufficient_permission">Insufficient permission to rename file</string>
|
||||||
|
<string name="error_file_rename_exception">File rename exception: %1$s</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Reference in New Issue
Block a user