diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 8ae65df..045d7aa 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -49,7 +49,8 @@ "Bash(while IFS= read -r file)", "Bash(do if ! grep -q \"import com.lukouguoji.module_base.ktx.formatDate\" \"$file\")", "Bash(then sed -i '' '/import dev.utils.common.DateUtils/a\\\\\nimport com.lukouguoji.module_base.ktx.formatDate\n' \"$file\" echo \"Added formatDate import to: $file\" fi done)", - "Bash(identify:*)" + "Bash(identify:*)", + "WebFetch(domain:github.com)" ], "deny": [], "ask": [] diff --git a/module_base/build.gradle b/module_base/build.gradle index 4c70133..de82b25 100644 --- a/module_base/build.gradle +++ b/module_base/build.gradle @@ -138,6 +138,9 @@ dependencies { api 'io.github.lucksiege:pictureselector:v3.11.2' api "com.github.bumptech.glide:glide:4.15.1" + // PDF预览库 + api 'com.github.barteksc:android-pdf-viewer:2.8.2' + // DevApp - Android 工具类库 api 'io.github.afkt:DevAppX:2.4.0' diff --git a/module_base/src/main/java/com/lukouguoji/module_base/cache/PdfCacheModel.kt b/module_base/src/main/java/com/lukouguoji/module_base/cache/PdfCacheModel.kt new file mode 100644 index 0000000..da0581d --- /dev/null +++ b/module_base/src/main/java/com/lukouguoji/module_base/cache/PdfCacheModel.kt @@ -0,0 +1,18 @@ +package com.lukouguoji.module_base.cache + +import dev.DevUtils + +/** + * PDF文件缓存管理 + * 负责PDF文件的下载和本地缓存 + */ +object PdfCacheModel : FileCacheModel() { + + /** + * PDF文件缓存目录 + * 路径: /data/data/com.lukouguoji.aerologic/files/cacheFiles/pdf/ + */ + override fun folderPath(): String { + return DevUtils.getContext().filesDir.toString() + "/cacheFiles/pdf/" + } +} diff --git a/module_base/src/main/java/com/lukouguoji/module_base/ui/page/preview/PdfPreviewActivity.kt b/module_base/src/main/java/com/lukouguoji/module_base/ui/page/preview/PdfPreviewActivity.kt new file mode 100644 index 0000000..c47d9ca --- /dev/null +++ b/module_base/src/main/java/com/lukouguoji/module_base/ui/page/preview/PdfPreviewActivity.kt @@ -0,0 +1,223 @@ +package com.lukouguoji.module_base.ui.page.preview + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.os.Bundle +import android.view.View +import androidx.lifecycle.lifecycleScope +import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener +import com.github.barteksc.pdfviewer.listener.OnPageChangeListener +import com.github.barteksc.pdfviewer.listener.OnErrorListener +import com.lukouguoji.module_base.R +import com.lukouguoji.module_base.base.BaseBindingActivity +import com.lukouguoji.module_base.cache.PdfCacheModel +import com.lukouguoji.module_base.common.Constant +import com.lukouguoji.module_base.databinding.ActivityPdfPreviewBinding +import com.lukouguoji.module_base.impl.EmptyViewModel +import com.lukouguoji.module_base.ktx.showToast +import com.lukouguoji.module_base.util.MediaUtil +import dev.utils.app.BarUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File + +/** + * PDF预览界面 + * 支持在线PDF下载缓存和本地PDF文件预览 + */ +class PdfPreviewActivity : BaseBindingActivity() { + + // PDF文件路径 (可能是在线URL或本地路径) + private var pdfPath: String = "" + + // 当前页码 (从0开始) + private var currentPage = 0 + + // 总页数 + private var totalPages = 0 + + override fun layoutId() = R.layout.activity_pdf_preview + override fun viewModelClass() = EmptyViewModel::class.java + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // 强制横屏显示 (与项目其他页面保持一致) + resources?.let { + if (it.displayMetrics.widthPixels < it.displayMetrics.heightPixels) { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } else { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + } + } + } + + @SuppressLint("SourceLockedOrientationActivity") + override fun initOnCreate(savedInstanceState: Bundle?) { + // 设置沉浸式状态栏 + BarUtils.transparentStatusBar(this) + + // 获取传递的PDF路径 + pdfPath = intent.getStringExtra(Constant.Key.DATA) ?: "" + if (pdfPath.isEmpty()) { + showToast("PDF路径为空") + finish() + return + } + + // 设置关闭按钮 + binding.ivClose.setOnClickListener { + finish() + } + + // 加载PDF文件 + loadPdfFile() + } + + /** + * 加载PDF文件 (支持在线和本地) + */ + private fun loadPdfFile() { +// val fullUrl = MediaUtil.fillUrl(pdfPath) + val fullUrl = pdfPath + + // 判断是否为在线资源 + if (isOnlineResource(fullUrl)) { + // 在线PDF: 先下载再加载 + downloadAndLoadPdf(fullUrl) + } else { + // 本地PDF: 直接加载 + loadLocalPdf(File(pdfPath)) + } + } + + /** + * 判断是否为在线资源 + */ + private fun isOnlineResource(url: String): Boolean { + return url.startsWith("http://") || url.startsWith("https://") + } + + /** + * 下载在线PDF并加载 + */ + private fun downloadAndLoadPdf(url: String) { + // 显示Loading + showLoading() + + lifecycleScope.launch(Dispatchers.IO) { + try { + // 使用PdfCacheModel下载PDF + PdfCacheModel.saveFile( + url = url, + onProgress = { progress -> + // 更新下载进度 + lifecycleScope.launch(Dispatchers.Main) { + binding.tvLoadingTip.text = "正在下载PDF... $progress%" + } + }, + onSuccess = { file -> + // 下载成功,切换到主线程加载PDF + lifecycleScope.launch(Dispatchers.Main) { + hideLoading() + loadLocalPdf(file) + } + }, + onError = { errorMsg -> + // 下载失败 + lifecycleScope.launch(Dispatchers.Main) { + hideLoading() + showToast("PDF下载失败: $errorMsg") + finish() + } + } + ) + } catch (e: Exception) { + withContext(Dispatchers.Main) { + hideLoading() + showToast("PDF下载异常: ${e.message}") + finish() + } + } + } + } + + /** + * 加载本地PDF文件 + */ + private fun loadLocalPdf(file: File) { + if (!file.exists()) { + showToast("PDF文件不存在") + finish() + return + } + + try { + binding.pdfView.fromFile(file) + .defaultPage(0) // 默认显示第一页 + .enableSwipe(true) // 启用滑动翻页 + .swipeHorizontal(false) // 垂直滑动 + .enableDoubletap(true) // 启用双击缩放 + .enableAnnotationRendering(false) // 禁用注释渲染(提升性能) + .password(null) // PDF密码(如需要) + .scrollHandle(null) // 禁用默认滚动条 + .onLoad(OnLoadCompleteListener { nbPages -> + // PDF加载完成 + totalPages = nbPages + updatePageIndicator() + }) + .onPageChange(OnPageChangeListener { page, pageCount -> + // 页面切换 + currentPage = page + updatePageIndicator() + }) + .onError(OnErrorListener { t -> + // 加载错误 + showToast("PDF加载失败: ${t.message}") + finish() + }) + .load() + } catch (e: Exception) { + showToast("PDF渲染异常: ${e.message}") + finish() + } + } + + /** + * 更新页码指示器 + */ + private fun updatePageIndicator() { + binding.tvPageIndicator.text = "${currentPage + 1} / $totalPages" + } + + /** + * 显示Loading + */ + private fun showLoading() { + binding.loadingLayout.visibility = View.VISIBLE + } + + /** + * 隐藏Loading + */ + private fun hideLoading() { + binding.loadingLayout.visibility = View.GONE + } + + companion object { + /** + * 启动PDF预览 + * @param context 上下文 + * @param pdfPath PDF路径 (支持在线URL或本地路径) + */ + @JvmStatic + fun start(context: Context, pdfPath: String) { + val starter = Intent(context, PdfPreviewActivity::class.java) + .putExtra(Constant.Key.DATA, pdfPath) + context.startActivity(starter) + } + } +} diff --git a/module_base/src/main/release/AndroidManifest.xml b/module_base/src/main/release/AndroidManifest.xml index f706da7..8201453 100644 --- a/module_base/src/main/release/AndroidManifest.xml +++ b/module_base/src/main/release/AndroidManifest.xml @@ -27,6 +27,13 @@ android:configChanges="orientation|keyboardHidden" android:exported="true"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/module_base/src/main/res/values/colors.xml b/module_base/src/main/res/values/colors.xml index 4ace2b5..4f539e4 100644 --- a/module_base/src/main/res/values/colors.xml +++ b/module_base/src/main/res/values/colors.xml @@ -41,4 +41,9 @@ #797979 + + #4D000000 + #80000000 + #B3000000 + \ No newline at end of file diff --git a/module_gjc/src/main/java/com/lukouguoji/gjc/viewModel/GjcInspectionDetailsViewModel.kt b/module_gjc/src/main/java/com/lukouguoji/gjc/viewModel/GjcInspectionDetailsViewModel.kt index 374059a..94f8bcd 100644 --- a/module_gjc/src/main/java/com/lukouguoji/gjc/viewModel/GjcInspectionDetailsViewModel.kt +++ b/module_gjc/src/main/java/com/lukouguoji/gjc/viewModel/GjcInspectionDetailsViewModel.kt @@ -17,6 +17,7 @@ import com.lukouguoji.module_base.ktx.showConfirmDialog import com.lukouguoji.module_base.ktx.showToast import com.lukouguoji.module_base.ktx.toRequestBody import com.lukouguoji.module_base.ui.page.preview.PreviewActivity +import com.lukouguoji.module_base.ui.page.preview.PdfPreviewActivity import com.lukouguoji.module_base.util.MediaUtil import kotlinx.coroutines.launch @@ -154,9 +155,9 @@ class GjcInspectionDetailsViewModel : BaseViewModel() { ) PreviewActivity.start(getTopActivity(), listOf(fileBean)) } - // PDF格式:提示暂不支持 + // PDF格式:使用PdfPreviewActivity预览 attach.name.endsWith(".pdf", true) -> { - showToast("PDF文件预览功能开发中") + PdfPreviewActivity.start(getTopActivity(), attach.path) } // 其他格式 else -> {