diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..f50d242 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(./gradlew:*)" + ], + "deny": [] + } +} \ 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 a25c230..f340e63 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 @@ -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) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt index da388b9..7b30cb4 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt @@ -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 } } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/LogUtil.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/LogUtil.kt new file mode 100644 index 0000000..e2a86d8 --- /dev/null +++ b/app/src/main/java/com/all/pdfreader/pro/app/util/LogUtil.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt new file mode 100644 index 0000000..1e2c570 --- /dev/null +++ b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfScanner.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_pdf_item.xml b/app/src/main/res/layout/adapter_pdf_item.xml index b5541ea..9d49abd 100644 --- a/app/src/main/res/layout/adapter_pdf_item.xml +++ b/app/src/main/res/layout/adapter_pdf_item.xml @@ -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">