添加修改文件名功能。

This commit is contained in:
ocean 2025-09-10 18:58:22 +08:00
parent ded6be90bf
commit 32374b0961
12 changed files with 126 additions and 44 deletions

View File

@ -42,6 +42,7 @@ android {
}
dependencies {
implementation(libs.androidx.fragment.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)

View File

@ -0,0 +1,5 @@
package com.all.pdfreader.pro.app.model
sealed class FileActionEvent {
data class Rename(val renameResult: RenameResult) : FileActionEvent()
}

View File

@ -0,0 +1,11 @@
package com.all.pdfreader.pro.app.model
data class RenameResult(
val success: Boolean,
val errorMessage: String? = null
) {
companion object {
fun success() = RenameResult(true)
fun failure(message: String) = RenameResult(false, message)
}
}

View File

@ -36,4 +36,8 @@ interface PdfDocumentDao {
@Query("DELETE FROM pdf_documents WHERE filePath = :filePath")
suspend fun deleteByPath(filePath: String)
@Query("UPDATE pdf_documents SET filePath = :newFilePath, fileName = :newName WHERE filePath = :oldFilePath")
suspend fun updateFilePathAndFileName(oldFilePath: String, newFilePath: String, newName: String)
}

View File

@ -11,9 +11,7 @@ import java.security.MessageDigest
class PdfRepository private constructor(context: Context) {
private val database = Room.databaseBuilder(
context,
PdfDatabase::class.java,
PdfDatabase.DATABASE_NAME
context, PdfDatabase::class.java, PdfDatabase.DATABASE_NAME
).build()
private val pdfDao = database.pdfDocumentDao()
@ -41,6 +39,17 @@ class PdfRepository private constructor(context: Context) {
fun searchDocuments(query: String): Flow<List<PdfDocumentEntity>> =
pdfDao.searchDocuments(query)
suspend fun updateFilePathAndFileName(oldFilePath: String, newFilePath: String, newName: String) {
pdfDao.updateFilePathAndFileName(oldFilePath, newFilePath, newName)
}
// suspend fun updateFilePathAndFileName(filePath: String, newFilePath: String, newName: String) {
// val document = pdfDao.getByPath(filePath)?.copy(
// filePath = newFilePath, fileName = newName
// )
// document?.let { pdfDao.update(it) }
// }
suspend fun updateFavoriteStatus(filePath: String, isFavorite: Boolean) {
val document = pdfDao.getByPath(filePath)?.copy(
isFavorite = isFavorite,
@ -79,7 +88,6 @@ class PdfRepository private constructor(context: Context) {
document?.let { pdfDao.update(it) }
}
// 最近阅读相关操作
suspend fun addToRecent(filePath: String, page: Int = 0) {
val existing = recentDao.getByPdfHash(filePath)
@ -88,8 +96,7 @@ class PdfRepository private constructor(context: Context) {
} else {
recentDao.insertOrUpdate(
RecentReadEntity(
filePath = filePath,
lastOpenedTime = System.currentTimeMillis()
filePath = filePath, lastOpenedTime = System.currentTimeMillis()
)
)
}
@ -120,12 +127,10 @@ class PdfRepository private constructor(context: Context) {
// 组合查询
suspend fun getPdfWithDetails(pdfHash: String): Flow<PdfDetails> {
return combine(
pdfDao.getByHash(pdfHash)?.let { kotlinx.coroutines.flow.flowOf(it) }
return combine(pdfDao.getByHash(pdfHash)?.let { kotlinx.coroutines.flow.flowOf(it) }
?: kotlinx.coroutines.flow.flowOf(null),
bookmarkDao.getBookmarksByPdf(pdfHash),
noteDao.getNotesByPdf(pdfHash)
) { document, bookmarks, notes ->
noteDao.getNotesByPdf(pdfHash)) { document, bookmarks, notes ->
PdfDetails(document, bookmarks, notes)
}
}

View File

@ -5,10 +5,12 @@ import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.all.pdfreader.pro.app.PRApp
import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.ActivityMainBinding
import com.all.pdfreader.pro.app.model.FileActionEvent
import com.all.pdfreader.pro.app.ui.dialog.PermissionDialogFragment
import com.all.pdfreader.pro.app.ui.dialog.SortDialogFragment
import com.all.pdfreader.pro.app.ui.fragment.FavoriteFrag
@ -18,6 +20,7 @@ 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.PdfScanner
import com.all.pdfreader.pro.app.util.StoragePermissionHelper
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.launch
@ -38,10 +41,13 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
private var activeFragment: Fragment = homeFragment
private val fragmentTag = "ACTIVE_FRAGMENT"
private val viewModel by lazy { ViewModelProvider(this)[PdfViewModel::class.java] }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initObserve()
ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
.navigationBarColor(R.color.black).init()
@ -60,6 +66,21 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
updateSelectedNav(activeFragment)
}
private fun initObserve(){
//观察其余操作
viewModel.fileActionEvent.observe(this) { event ->
when (event) {
is FileActionEvent.Rename -> {
if (event.renameResult.success) {
showToast(getString(R.string.rename_successfully))
} else {
showToast(event.renameResult.errorMessage.toString())
}
}
}
}
}
private fun setupFragments() {
supportFragmentManager.beginTransaction().add(R.id.fragment_fl, toolsFragment, "TOOLS")
.hide(toolsFragment).add(R.id.fragment_fl, favoriteFragment, "FAVORITE")

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.ActivityPdfViewBinding
import com.all.pdfreader.pro.app.model.FileActionEvent
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
import com.all.pdfreader.pro.app.ui.dialog.PdfPasswordProtectionDialogFragment
import com.all.pdfreader.pro.app.ui.view.CustomScrollHandle
@ -40,10 +41,14 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
super.onCreate(savedInstanceState)
binding = ActivityPdfViewBinding.inflate(layoutInflater)
setContentView(binding.root)
initObserve()
val filePath = intent.getStringExtra(EXTRA_PDF_HASH)
?: throw IllegalArgumentException("PDF file hash is required")
// 加载PDF数据
viewModel.getPDFDocument(filePath)
}
private fun initObserve(){
// 观察PDF文档数据
viewModel.pdfDocument.observe(this) { document ->
document?.let {
@ -54,9 +59,19 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
finish()
}
}
// 加载PDF数据
viewModel.getPDFDocument(filePath)
//观察其余操作
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() {

View File

@ -5,6 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.all.pdfreader.pro.app.R
@ -28,7 +29,7 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment()
private lateinit var binding: DialogListMoreBinding
private val pdfRepository = PdfRepository.getInstance()
private val viewModel by lazy { ViewModelProvider(this)[PdfViewModel::class.java] }
private val viewModel: PdfViewModel by activityViewModels()
private lateinit var pdfDocument: PdfDocumentEntity
private var isFavorite: Boolean = false

View File

@ -3,7 +3,6 @@ package com.all.pdfreader.pro.app.ui.dialog
import android.graphics.Color
import android.os.Bundle
import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
@ -11,23 +10,21 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.core.graphics.drawable.toDrawable
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.activityViewModels
import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.DialogPdfPasswordProtectionBinding
import com.all.pdfreader.pro.app.databinding.DialogRenameFileBinding
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.FileUtils
import com.all.pdfreader.pro.app.util.FileUtils.isPdfPasswordCorrect
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
import java.io.File
class RenameDialogFragment(
private val filePath: String,
private val filePath: String
) : DialogFragment() {
private lateinit var binding: DialogRenameFileBinding
private val viewModel by lazy { ViewModelProvider(this)[PdfViewModel::class.java] }
private val viewModel: PdfViewModel by activityViewModels()
private lateinit var pdfDocument: PdfDocumentEntity
override fun onCreateView(
@ -81,13 +78,7 @@ class RenameDialogFragment(
binding.tvConfirm.setOnClickListener {
val text = binding.etName.text.toString()
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())
}
viewModel.renamePdf(pdfDocument.filePath, text)
dismiss()
}
}
@ -96,6 +87,7 @@ class RenameDialogFragment(
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
binding.tilName.error = null
}
override fun afterTextChanged(s: Editable?) {}
})
}

View File

@ -20,19 +20,7 @@ import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
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)
}
}
import com.all.pdfreader.pro.app.model.RenameResult
object FileUtils {

View File

@ -5,9 +5,13 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
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.FileUtils
import com.all.pdfreader.pro.app.util.LogUtil
import kotlinx.coroutines.launch
import java.io.File
class PdfViewModel : ViewModel() {
private val pdfRepository = PdfRepository.getInstance()
@ -15,6 +19,11 @@ class PdfViewModel : ViewModel() {
private val _pdfDocument = MutableLiveData<PdfDocumentEntity?>()
val pdfDocument: LiveData<PdfDocumentEntity?> = _pdfDocument
// 用于操作结果或通知UI
private val _fileActionEvent = MutableLiveData<FileActionEvent>()
val fileActionEvent: LiveData<FileActionEvent> = _fileActionEvent
fun getPDFDocument(filePath: String) {
viewModelScope.launch {
val document = pdfRepository.getDocumentByPath(filePath)
@ -22,4 +31,32 @@ class PdfViewModel : ViewModel() {
_pdfDocument.postValue(document)
}
}
//触发修改文件名操作
fun renamePdf(filePath: String, newName: String) {
viewModelScope.launch {
val oldFile = File(filePath)
val renameResult = FileUtils.renameFile(oldFile, newName)
Log.d("ocean", "renamePdf->oldFile: $oldFile, newName=$newName, renameResult=$renameResult")
if (renameResult.success) {//修改成功更新数据库
val finalName = if (newName.contains('.')) {
// 用户提供了后缀,使用用户的
newName
} else {
// 用户没有提供后缀,自动添加原文件后缀
val originalExtension = File(filePath).extension
if (originalExtension.isNotEmpty()) {
"$newName.$originalExtension"
} else {
newName
}
}
val parentDir = oldFile.parentFile
val newFilePath = File(parentDir, finalName).absolutePath.toString()
Log.d("ocean", "renamePdf->newFilePath: $newFilePath, finalName=$finalName")
pdfRepository.updateFilePathAndFileName(filePath, newFilePath, finalName)
}
_fileActionEvent.postValue(FileActionEvent.Rename(renameResult))
}
}
}

View File

@ -2,6 +2,7 @@
androidpdfviewer = "3.2.8"
appcompat = "1.7.1"
agp = "8.10.1"
fragmentKtx = "1.8.9"
glide = "5.0.4"
kotlin = "2.0.21"
ksp = "2.0.21-1.0.27"
@ -23,6 +24,7 @@ material = "1.12.0"
androidpdfviewer = { module = "com.github.marain87:AndroidPdfViewer", version.ref = "androidpdfviewer" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room_version" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room_version" }
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room_version" }