封装扫描方法。
This commit is contained in:
parent
c642e3322e
commit
167b5574ec
8
.claude/settings.local.json
Normal file
8
.claude/settings.local.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(./gradlew:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.all.pdfreader.pro.app.R
|
||||
import com.all.pdfreader.pro.app.databinding.ActivityMainBinding
|
||||
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.fragment.FavoriteFrag
|
||||
import com.all.pdfreader.pro.app.ui.fragment.HomeFrag
|
||||
@ -20,15 +19,10 @@ import com.all.pdfreader.pro.app.model.SortDirection
|
||||
import com.all.pdfreader.pro.app.ui.dialog.SortDialogFragment
|
||||
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
|
||||
import com.all.pdfreader.pro.app.util.FileChangeObserver
|
||||
import com.all.pdfreader.pro.app.util.FileUtils
|
||||
import com.all.pdfreader.pro.app.util.PdfMetadataExtractor
|
||||
import com.all.pdfreader.pro.app.util.ScanManager
|
||||
import com.all.pdfreader.pro.app.util.PdfScanner
|
||||
import com.all.pdfreader.pro.app.util.StoragePermissionHelper
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback,
|
||||
PermissionDialogFragment.CloseCallback {
|
||||
@ -38,6 +32,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private val pdfRepository = getRepository()
|
||||
private lateinit var fileChangeObserver: FileChangeObserver
|
||||
private lateinit var pdfScanner: PdfScanner
|
||||
|
||||
private val homeFragment = HomeFrag()
|
||||
private val recentlyFragment = RecentlyFrag()
|
||||
@ -56,6 +51,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
||||
|
||||
setupFragments()
|
||||
setupNavigation()
|
||||
pdfScanner = PdfScanner(this, pdfRepository)
|
||||
if (savedInstanceState != null) {
|
||||
val restoredFragment =
|
||||
supportFragmentManager.getFragment(savedInstanceState, fragmentTag)
|
||||
@ -70,16 +66,17 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
||||
|
||||
private fun scanningStrategy() {
|
||||
// 智能扫描策略
|
||||
if (ScanManager.shouldScan(this)) {
|
||||
if (pdfScanner.shouldScan()) {
|
||||
logDebug("🔄 需要扫描PDF文件 (首次启动或超过24小时)")
|
||||
if (StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
||||
scanAndLoadPdfFiles()
|
||||
lifecycleScope.launch {
|
||||
pdfScanner.scanAndLoadPdfFiles()
|
||||
}
|
||||
} else {
|
||||
logDebug("❌ 权限不足,跳过扫描")
|
||||
}
|
||||
} else {
|
||||
val lastScan = ScanManager.getLastScanTime(this)
|
||||
val hoursAgo = TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis() - lastScan)
|
||||
val hoursAgo = pdfScanner.getHoursSinceLastScan()
|
||||
logDebug("⏭️ 跳过扫描,上次扫描在${hoursAgo}小时前")
|
||||
}
|
||||
}
|
||||
@ -153,108 +150,6 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
|
||||
}
|
||||
}
|
||||
|
||||
fun scanAndLoadPdfFiles(callback: (Boolean) -> Unit = {}) {
|
||||
if (!StoragePermissionHelper.hasBasicStoragePermission(this)) {
|
||||
logDebug("权限不足")
|
||||
callback.invoke(false)
|
||||
return
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
logDebug("🔍 开始扫描PDF文件...")
|
||||
|
||||
// 扫描应用私有目录(无需权限)
|
||||
val privateFiles = FileUtils.scanPdfFiles(this@MainActivity)
|
||||
logDebug("📁 应用私有目录找到: ${privateFiles.size} 个PDF文件")
|
||||
privateFiles.forEach { file ->
|
||||
logDebug(" 📄 ${file.name} (${FileUtils.formatFileSize(file.length())})")
|
||||
}
|
||||
|
||||
// 扫描MediaStore(需要权限)
|
||||
val mediaStoreFiles = FileUtils.scanPdfFilesFromMediaStore(this@MainActivity)
|
||||
logDebug("📱 MediaStore找到: ${mediaStoreFiles.size} 个PDF文件")
|
||||
mediaStoreFiles.forEach { file ->
|
||||
logDebug(" 📱 ${file.name} (${FileUtils.formatFileSize(file.length())})")
|
||||
}
|
||||
|
||||
// 合并并去重
|
||||
val allFiles = (privateFiles + mediaStoreFiles).distinctBy { it.absolutePath }
|
||||
logDebug("📊 总计扫描到: ${allFiles.size} 个PDF文件")
|
||||
|
||||
// 处理每个PDF文件
|
||||
allFiles.forEachIndexed { index, file ->
|
||||
logDebug("🔄 处理文件 ${index + 1}/${allFiles.size}: ${file.name}")
|
||||
|
||||
if (FileUtils.isPdfFile(file)) {
|
||||
val fileHash = FileUtils.calculateFileHash(file.absolutePath)
|
||||
logDebug(" 🔑 文件哈希: $fileHash")
|
||||
|
||||
if (fileHash != null) {
|
||||
val existingDoc = pdfRepository.getDocumentByHash(fileHash)
|
||||
|
||||
if (existingDoc == null) {
|
||||
logDebug(" 🆕 发现新PDF文件: ${file.name}")
|
||||
val metadata =
|
||||
PdfMetadataExtractor.extractMetadata(file.absolutePath)
|
||||
val document = PdfDocumentEntity(
|
||||
fileHash = fileHash,
|
||||
filePath = file.absolutePath,
|
||||
fileName = file.name,
|
||||
fileSize = file.length(),
|
||||
lastModified = file.lastModified(),
|
||||
pageCount = metadata?.pageCount ?: 0,
|
||||
metadataTitle = metadata?.title,
|
||||
metadataAuthor = metadata?.author,
|
||||
metadataSubject = metadata?.subject,
|
||||
metadataKeywords = metadata?.keywords,
|
||||
metadataCreationDate = metadata?.creationDate?.time,
|
||||
metadataModificationDate = metadata?.modificationDate?.time
|
||||
)
|
||||
pdfRepository.insertOrUpdateDocument(document)
|
||||
logDebug(" ✅ 已保存到数据库: ${file.name}")
|
||||
} else {
|
||||
logDebug(" 📋 文件已存在: ${file.name}")
|
||||
if (existingDoc.filePath != file.absolutePath) {
|
||||
logDebug(" 🔄 更新文件路径: ${existingDoc.filePath} -> ${file.absolutePath}")
|
||||
val updatedDoc = existingDoc.copy(
|
||||
filePath = file.absolutePath,
|
||||
lastModified = file.lastModified()
|
||||
)
|
||||
pdfRepository.insertOrUpdateDocument(updatedDoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打印数据库中的总记录数
|
||||
pdfRepository.getAllDocuments().collect { docs ->
|
||||
logDebug("📊 数据库中共有: ${docs.size} 个PDF记录")
|
||||
docs.forEach { doc ->
|
||||
logDebug(
|
||||
" 📖 ${doc.fileName} - ${doc.pageCount}页 - ${
|
||||
FileUtils.formatFileSize(
|
||||
doc.fileSize
|
||||
)
|
||||
} - ${doc.thumbnailPath}"
|
||||
)
|
||||
}
|
||||
|
||||
// 标记扫描完成
|
||||
ScanManager.markScanComplete(this@MainActivity)
|
||||
val lastScanTime = ScanManager.getLastScanTime(this@MainActivity)
|
||||
logDebug("✅ 扫描完成,记录时间: ${java.util.Date(lastScanTime)}")
|
||||
callback.invoke(true)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
logError("❌ 扫描出错: ${e.message}", e)
|
||||
callback.invoke(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
@ -15,7 +15,7 @@ import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||
import com.all.pdfreader.pro.app.ui.act.MainActivity
|
||||
import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import com.all.pdfreader.pro.app.util.PdfScanner
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HomeFrag : BaseFrag(), MainActivity.SortableFragment {
|
||||
@ -48,7 +48,8 @@ class HomeFrag : BaseFrag(), MainActivity.SortableFragment {
|
||||
// 下拉刷新示例
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
lifecycleScope.launch {
|
||||
(activity as? MainActivity)?.scanAndLoadPdfFiles { b ->
|
||||
val pdfScanner = PdfScanner(requireContext(), getRepository())
|
||||
pdfScanner.scanAndLoadPdfFiles { b ->
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
22
app/src/main/java/com/all/pdfreader/pro/app/util/LogUtil.kt
Normal file
22
app/src/main/java/com/all/pdfreader/pro/app/util/LogUtil.kt
Normal file
@ -0,0 +1,22 @@
|
||||
package com.all.pdfreader.pro.app.util
|
||||
|
||||
import android.util.Log
|
||||
|
||||
object LogUtil {
|
||||
|
||||
fun logDebug(tag: String, message: String) {
|
||||
Log.d(tag, message)
|
||||
}
|
||||
|
||||
fun logError(tag: String, message: String, throwable: Throwable? = null) {
|
||||
Log.e(tag, message, throwable)
|
||||
}
|
||||
|
||||
fun logInfo(tag: String, message: String) {
|
||||
Log.i(tag, message)
|
||||
}
|
||||
|
||||
fun logWarning(tag: String, message: String) {
|
||||
Log.w(tag, message)
|
||||
}
|
||||
}
|
||||
127
app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt
Normal file
127
app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt
Normal file
@ -0,0 +1,127 @@
|
||||
package com.all.pdfreader.pro.app.util
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class PdfScanner(
|
||||
private val context: Context,
|
||||
private val pdfRepository: PdfRepository
|
||||
) {
|
||||
|
||||
suspend fun scanAndLoadPdfFiles(callback: (Boolean) -> Unit = {}) {
|
||||
if (!StoragePermissionHelper.hasBasicStoragePermission(context)) {
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 权限不足")
|
||||
callback.invoke(false)
|
||||
return
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 🔍 开始扫描PDF文件...")
|
||||
|
||||
// 扫描应用私有目录(无需权限)
|
||||
val privateFiles = FileUtils.scanPdfFiles(context)
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 📁 应用私有目录找到: ${privateFiles.size} 个PDF文件")
|
||||
privateFiles.forEach { file ->
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 📄 ${file.name} (${FileUtils.formatFileSize(file.length())})")
|
||||
}
|
||||
|
||||
// 扫描MediaStore(需要权限)
|
||||
val mediaStoreFiles = FileUtils.scanPdfFilesFromMediaStore(context)
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 📱 MediaStore找到: ${mediaStoreFiles.size} 个PDF文件")
|
||||
mediaStoreFiles.forEach { file ->
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 📱 ${file.name} (${FileUtils.formatFileSize(file.length())})")
|
||||
}
|
||||
|
||||
// 合并并去重
|
||||
val allFiles = (privateFiles + mediaStoreFiles).distinctBy { it.absolutePath }
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 📊 总计扫描到: ${allFiles.size} 个PDF文件")
|
||||
|
||||
// 处理每个PDF文件
|
||||
allFiles.forEachIndexed { index, file ->
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 🔄 处理文件 ${index + 1}/${allFiles.size}: ${file.name}")
|
||||
|
||||
if (FileUtils.isPdfFile(file)) {
|
||||
val fileHash = FileUtils.calculateFileHash(file.absolutePath)
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 🔑 文件哈希: $fileHash")
|
||||
|
||||
if (fileHash != null) {
|
||||
val existingDoc = pdfRepository.getDocumentByHash(fileHash)
|
||||
|
||||
if (existingDoc == null) {
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 🆕 发现新PDF文件: ${file.name}")
|
||||
val metadata =
|
||||
PdfMetadataExtractor.extractMetadata(file.absolutePath)
|
||||
val document = PdfDocumentEntity(
|
||||
fileHash = fileHash,
|
||||
filePath = file.absolutePath,
|
||||
fileName = file.name,
|
||||
fileSize = file.length(),
|
||||
lastModified = file.lastModified(),
|
||||
pageCount = metadata?.pageCount ?: 0,
|
||||
metadataTitle = metadata?.title,
|
||||
metadataAuthor = metadata?.author,
|
||||
metadataSubject = metadata?.subject,
|
||||
metadataKeywords = metadata?.keywords,
|
||||
metadataCreationDate = metadata?.creationDate?.time,
|
||||
metadataModificationDate = metadata?.modificationDate?.time
|
||||
)
|
||||
pdfRepository.insertOrUpdateDocument(document)
|
||||
LogUtil.logDebug("ocean", "PdfScanner: ✅ 已保存到数据库: ${file.name}")
|
||||
} else {
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 📋 文件已存在: ${file.name}")
|
||||
if (existingDoc.filePath != file.absolutePath) {
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 🔄 更新文件路径: ${existingDoc.filePath} -> ${file.absolutePath}")
|
||||
val updatedDoc = existingDoc.copy(
|
||||
filePath = file.absolutePath,
|
||||
lastModified = file.lastModified()
|
||||
)
|
||||
pdfRepository.insertOrUpdateDocument(updatedDoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 打印数据库中的总记录数
|
||||
pdfRepository.getAllDocuments().collect { docs ->
|
||||
LogUtil.logDebug("ocean", "PdfScanner: 📊 数据库中共有: ${docs.size} 个PDF记录")
|
||||
docs.forEach { doc ->
|
||||
LogUtil.logDebug(
|
||||
"ocean",
|
||||
"PdfScanner: 📖 ${doc.fileName} - ${doc.pageCount}页 - ${FileUtils.formatFileSize(doc.fileSize)} - ${doc.thumbnailPath}"
|
||||
)
|
||||
}
|
||||
|
||||
// 标记扫描完成
|
||||
ScanManager.markScanComplete(context)
|
||||
val lastScanTime = ScanManager.getLastScanTime(context)
|
||||
LogUtil.logDebug("ocean", "PdfScanner: ✅ 扫描完成,记录时间: ${java.util.Date(lastScanTime)}")
|
||||
callback.invoke(true)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("ocean", "PdfScanner: ❌ 扫描出错: ${e.message}", e)
|
||||
callback.invoke(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldScan(): Boolean {
|
||||
return ScanManager.shouldScan(context)
|
||||
}
|
||||
|
||||
fun getLastScanTime(): Long {
|
||||
return ScanManager.getLastScanTime(context)
|
||||
}
|
||||
|
||||
fun getHoursSinceLastScan(): Long {
|
||||
val lastScan = getLastScanTime()
|
||||
return TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis() - lastScan)
|
||||
}
|
||||
}
|
||||
@ -4,8 +4,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<LinearLayout
|
||||
@ -84,7 +84,8 @@
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@color/line_color" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user