显示pdf

This commit is contained in:
ocean 2025-09-04 18:32:01 +08:00
parent 9b5cd5e014
commit daaa2e4de5
27 changed files with 329 additions and 59 deletions

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,7 +1,10 @@
import org.gradle.kotlin.dsl.implementation
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
alias(libs.plugins.devtools.ksp) alias(libs.plugins.devtools.ksp)
id("kotlin-parcelize")
} }
android { android {
@ -54,4 +57,7 @@ dependencies {
implementation(libs.androidx.room.runtime) implementation(libs.androidx.room.runtime)
ksp(libs.androidx.room.compiler) ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.ktx) implementation(libs.androidx.room.ktx)
implementation(libs.glide)
implementation(libs.androidpdfviewer)
implementation(libs.pdfbox.android)
} }

View File

@ -19,3 +19,5 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-keep class com.shockwave.**

View File

@ -11,7 +11,8 @@
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- 读取所有文件权限Android 11+ 需要特殊处理) --> <!-- 读取所有文件权限Android 11+ 需要特殊处理) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" <uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<application <application
@ -21,10 +22,10 @@
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.PDFReaderPro" android:theme="@style/Theme.PDFReaderPro"
android:requestLegacyExternalStorage="true"
tools:targetApi="36"> tools:targetApi="36">
<activity <activity
@ -42,13 +43,20 @@
android:name=".ui.act.MainActivity" android:name=".ui.act.MainActivity"
android:exported="true" android:exported="true"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.PDFReaderPro"> android:theme="@style/Theme.PDFReaderPro" />
<activity
android:name=".ui.act.PdfViewActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.PDFReaderPro">
<!-- 处理PDF文件打开 --> <!-- 处理PDF文件打开 -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="application/pdf" /> <data android:mimeType="application/pdf" />
<data android:scheme="file" /> <data android:scheme="file" />
<data android:scheme="content" /> <data android:scheme="content" />

View File

@ -1,8 +1,11 @@
package com.all.pdfreader.pro.app.room.entity package com.all.pdfreader.pro.app.room.entity
import android.os.Parcelable
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
@Parcelize
@Entity(tableName = "pdf_documents") @Entity(tableName = "pdf_documents")
data class PdfDocumentEntity( data class PdfDocumentEntity(
@PrimaryKey @PrimaryKey
@ -28,4 +31,4 @@ data class PdfDocumentEntity(
val metadataKeywords: String? = null, // PDF元数据关键词 val metadataKeywords: String? = null, // PDF元数据关键词
val metadataCreationDate: Long? = null, // PDF创建时间 val metadataCreationDate: Long? = null, // PDF创建时间
val metadataModificationDate: Long? = null // PDF修改时间 val metadataModificationDate: Long? = null // PDF修改时间
) ): Parcelable

View File

@ -4,11 +4,13 @@ import android.app.Activity
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
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.sp.AppStore
import com.all.pdfreader.pro.app.util.StoragePermissionHelper import com.all.pdfreader.pro.app.util.StoragePermissionHelper
import com.tom_roush.pdfbox.contentstream.operator.text.ShowText
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
@ -60,6 +62,10 @@ abstract class BaseActivity : AppCompatActivity() {
Log.w("ocean", "$TAG: $message") Log.w("ocean", "$TAG: $message")
} }
protected fun showToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
//获取数据库实例 //获取数据库实例
protected fun getRepository(): PdfRepository { protected fun getRepository(): PdfRepository {
return PdfRepository.getInstance() return PdfRepository.getInstance()

View File

@ -0,0 +1,100 @@
package com.all.pdfreader.pro.app.ui.act
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
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.room.entity.PdfDocumentEntity
import com.github.barteksc.pdfviewer.listener.OnErrorListener
import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener
import com.github.barteksc.pdfviewer.listener.OnPageChangeListener
import kotlinx.coroutines.launch
import java.io.File
class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeListener,
OnErrorListener {
override val TAG: String = "PdfViewActivity"
private lateinit var binding: ActivityPdfViewBinding
private lateinit var pdfDocument: PdfDocumentEntity
private val pdfRepository = getRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPdfViewBinding.inflate(layoutInflater)
setContentView(binding.root)
// 获取传递的PDF文档数据
pdfDocument = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(EXTRA_PDF_DOCUMENT, PdfDocumentEntity::class.java)
} else {
@Suppress("DEPRECATION") intent.getParcelableExtra<PdfDocumentEntity>(EXTRA_PDF_DOCUMENT)
} ?: throw IllegalArgumentException("PDF document data is required")
loadPdf()
}
private fun loadPdf() {
// 使用传递的文件路径加载PDF
val file = File(pdfDocument.filePath)
if (file.exists()) {
binding.pdfview
.fromFile(file)
.defaultPage(pdfDocument.lastReadPage) // 从上次阅读页码开始
.enableDoubletap(true)// 是否允许双击缩放
.onLoad(this)
.enableAnnotationRendering(true) // 是否渲染注释(如评论、颜色、表单等)
.onError(this)
.onPageChange(this)
.load()
} else {
showToast(getString(R.string.file_not) + ": ${pdfDocument.fileName}")
finish()
}
}
//PDF 文档加载完成时回调
override fun loadComplete(nbPages: Int) {
}
//PDF 加载出错时回调
override fun onError(t: Throwable?) {
}
//页面切换时回调
override fun onPageChanged(page: Int, pageCount: Int) {
// 保存阅读进度
pdfDocument = pdfDocument.copy(
lastReadPage = page, readingProgress = (page.toFloat() / pageCount.toFloat()) * 100
)
}
override fun onDestroy() {
super.onDestroy()
saveReadingProgress()
}
private fun saveReadingProgress() {
lifecycleScope.launch {
pdfRepository.updateReadingProgress(
pdfDocument.fileHash, pdfDocument.lastReadPage, pdfDocument.readingProgress
)
}
}
companion object {
private const val EXTRA_PDF_DOCUMENT = "extra_pdf_document"
// 创建启动Intent的便捷方法
fun createIntent(context: Context, pdfDocument: PdfDocumentEntity): Intent {
return Intent(context, PdfViewActivity::class.java).apply {
putExtra(EXTRA_PDF_DOCUMENT, pdfDocument)
}
}
}
}

View File

@ -6,8 +6,12 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.all.pdfreader.pro.app.databinding.AdapterPdfItemBinding import com.all.pdfreader.pro.app.databinding.AdapterPdfItemBinding
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.dpToPx
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.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
class PdfAdapter( class PdfAdapter(
private var pdfList: MutableList<PdfDocumentEntity>, private var pdfList: MutableList<PdfDocumentEntity>,
@ -30,6 +34,9 @@ class PdfAdapter(
holder.binding.tvFileName.text = item.fileName holder.binding.tvFileName.text = item.fileName
holder.binding.tvFileSize.text = item.fileSize.toFormatFileSize() holder.binding.tvFileSize.text = item.fileSize.toFormatFileSize()
holder.binding.tvFileDate.text = item.lastModified.toSlashDate() holder.binding.tvFileDate.text = item.lastModified.toSlashDate()
Glide.with(holder.binding.root).load(item.thumbnailPath)
.transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context)))
.into(holder.binding.tvFileImg)
holder.binding.root.setOnClickListener { holder.binding.root.setOnClickListener {
onItemClick(item) onItemClick(item)

View File

@ -14,6 +14,7 @@ import com.all.pdfreader.pro.app.model.SortConfig
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.ui.act.MainActivity import com.all.pdfreader.pro.app.ui.act.MainActivity
import com.all.pdfreader.pro.app.ui.act.PdfViewActivity
import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter
import com.all.pdfreader.pro.app.util.PdfScanner import com.all.pdfreader.pro.app.util.PdfScanner
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -37,7 +38,9 @@ class HomeFrag : BaseFrag(), MainActivity.SortableFragment {
private fun initView() { private fun initView() {
adapter = PdfAdapter(pdfList = mutableListOf(), onItemClick = { pdf -> adapter = PdfAdapter(pdfList = mutableListOf(), onItemClick = { pdf ->
Toast.makeText(requireContext(), "点击: ${pdf.fileName}", Toast.LENGTH_SHORT).show() val intent = PdfViewActivity.createIntent(requireContext(), pdf)
startActivity(intent)
}, onMoreClick = { pdf -> }, onMoreClick = { pdf ->
Toast.makeText(requireContext(), "更多操作: ${pdf.fileName}", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "更多操作: ${pdf.fileName}", Toast.LENGTH_SHORT).show()
}) })

View File

@ -1,5 +1,6 @@
package com.all.pdfreader.pro.app.util package com.all.pdfreader.pro.app.util
import android.content.Context
import android.view.View import android.view.View
object AppUtils { object AppUtils {
@ -36,4 +37,9 @@ object AppUtils {
.start() .start()
} }
} }
fun Int.dpToPx(context: Context): Int {
return (this * context.resources.displayMetrics.density).toInt()
}
} }

View File

@ -3,29 +3,17 @@ package com.all.pdfreader.pro.app.util
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build
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 android.webkit.MimeTypeMap
import androidx.annotation.RequiresApi
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
import com.all.pdfreader.pro.app.util.StoragePermissionHelper
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
object FileUtils { object FileUtils {
private val PDF_MIME_TYPES = setOf(
"application/pdf", "application/x-pdf"
)
fun scanPdfFiles(context: Context): List<File> { fun scanPdfFiles(context: Context): List<File> {
val pdfFiles = mutableListOf<File>() val pdfFiles = mutableListOf<File>()

View File

@ -2,6 +2,8 @@ package com.all.pdfreader.pro.app.util
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.pdf.PdfRenderer import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.util.Log import android.util.Log
@ -13,6 +15,8 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import androidx.core.graphics.createBitmap import androidx.core.graphics.createBitmap
import com.github.barteksc.pdfviewer.PDFView
import java.util.concurrent.CountDownLatch
class PdfScanner( class PdfScanner(
private val context: Context, private val context: Context,
@ -32,16 +36,28 @@ class PdfScanner(
// 扫描应用私有目录(无需权限) // 扫描应用私有目录(无需权限)
val privateFiles = FileUtils.scanPdfFiles(context) val privateFiles = FileUtils.scanPdfFiles(context)
LogUtil.logDebug("ocean", "PdfScanner: 📁 应用私有目录找到: ${privateFiles.size} 个PDF文件") LogUtil.logDebug(
"ocean",
"PdfScanner: 📁 应用私有目录找到: ${privateFiles.size} 个PDF文件"
)
privateFiles.forEach { file -> privateFiles.forEach { file ->
LogUtil.logDebug("ocean", "PdfScanner: 📄 ${file.name} (${FileUtils.formatFileSize(file.length())})") LogUtil.logDebug(
"ocean",
"PdfScanner: 📄 ${file.name} (${FileUtils.formatFileSize(file.length())})"
)
} }
// 扫描MediaStore需要权限 // 扫描MediaStore需要权限
val mediaStoreFiles = FileUtils.scanPdfFilesFromMediaStore(context) val mediaStoreFiles = FileUtils.scanPdfFilesFromMediaStore(context)
LogUtil.logDebug("ocean", "PdfScanner: 📱 MediaStore找到: ${mediaStoreFiles.size} 个PDF文件") LogUtil.logDebug(
"ocean",
"PdfScanner: 📱 MediaStore找到: ${mediaStoreFiles.size} 个PDF文件"
)
mediaStoreFiles.forEach { file -> mediaStoreFiles.forEach { file ->
LogUtil.logDebug("ocean", "PdfScanner: 📱 ${file.name} (${FileUtils.formatFileSize(file.length())})") LogUtil.logDebug(
"ocean",
"PdfScanner: 📱 ${file.name} (${FileUtils.formatFileSize(file.length())})"
)
} }
// 合并并去重 // 合并并去重
@ -50,7 +66,10 @@ class PdfScanner(
// 处理每个PDF文件 // 处理每个PDF文件
allFiles.forEachIndexed { index, file -> allFiles.forEachIndexed { index, file ->
LogUtil.logDebug("ocean", "PdfScanner: 🔄 处理文件 ${index + 1}/${allFiles.size}: ${file.name}") LogUtil.logDebug(
"ocean",
"PdfScanner: 🔄 处理文件 ${index + 1}/${allFiles.size}: ${file.name}"
)
if (FileUtils.isPdfFile(file)) { if (FileUtils.isPdfFile(file)) {
val fileHash = FileUtils.calculateFileHash(file.absolutePath) val fileHash = FileUtils.calculateFileHash(file.absolutePath)
@ -60,7 +79,10 @@ class PdfScanner(
val existingDoc = pdfRepository.getDocumentByHash(fileHash) val existingDoc = pdfRepository.getDocumentByHash(fileHash)
if (existingDoc == null) { if (existingDoc == null) {
LogUtil.logDebug("ocean", "PdfScanner: 🆕 发现新PDF文件: ${file.name}") LogUtil.logDebug(
"ocean",
"PdfScanner: 🆕 发现新PDF文件: ${file.name}"
)
val thumbnailPath = generateThumbnail(context, file) val thumbnailPath = generateThumbnail(context, file)
val metadata = val metadata =
@ -81,11 +103,20 @@ class PdfScanner(
metadataModificationDate = metadata?.modificationDate?.time metadataModificationDate = metadata?.modificationDate?.time
) )
pdfRepository.insertOrUpdateDocument(document) pdfRepository.insertOrUpdateDocument(document)
LogUtil.logDebug("ocean", "PdfScanner: ✅ 已保存到数据库: ${file.name}") LogUtil.logDebug(
"ocean",
"PdfScanner: ✅ 已保存到数据库: ${file.name}"
)
} else { } else {
LogUtil.logDebug("ocean", "PdfScanner: 📋 文件已存在: ${file.name}") LogUtil.logDebug(
"ocean",
"PdfScanner: 📋 文件已存在: ${file.name}"
)
if (existingDoc.filePath != file.absolutePath) { if (existingDoc.filePath != file.absolutePath) {
LogUtil.logDebug("ocean", "PdfScanner: 🔄 更新文件路径: ${existingDoc.filePath} -> ${file.absolutePath}") LogUtil.logDebug(
"ocean",
"PdfScanner: 🔄 更新文件路径: ${existingDoc.filePath} -> ${file.absolutePath}"
)
val updatedDoc = existingDoc.copy( val updatedDoc = existingDoc.copy(
filePath = file.absolutePath, filePath = file.absolutePath,
lastModified = file.lastModified() lastModified = file.lastModified()
@ -103,14 +134,21 @@ class PdfScanner(
docs.forEach { doc -> docs.forEach { doc ->
LogUtil.logDebug( LogUtil.logDebug(
"ocean", "ocean",
"PdfScanner: 📖 ${doc.fileName} - ${doc.pageCount}页 - ${FileUtils.formatFileSize(doc.fileSize)} - ${doc.thumbnailPath}" "PdfScanner: 📖 ${doc.fileName} - ${doc.pageCount}页 - ${
FileUtils.formatFileSize(
doc.fileSize
)
} - ${doc.thumbnailPath}"
) )
} }
// 标记扫描完成 // 标记扫描完成
ScanManager.markScanComplete(context) ScanManager.markScanComplete(context)
val lastScanTime = ScanManager.getLastScanTime(context) val lastScanTime = ScanManager.getLastScanTime(context)
LogUtil.logDebug("ocean", "PdfScanner: ✅ 扫描完成,记录时间: ${java.util.Date(lastScanTime)}") LogUtil.logDebug(
"ocean",
"PdfScanner: ✅ 扫描完成,记录时间: ${java.util.Date(lastScanTime)}"
)
callback.invoke(true) callback.invoke(true)
} }
@ -137,12 +175,17 @@ class PdfScanner(
private fun generateThumbnail(context: Context, pdfFile: File): String? { private fun generateThumbnail(context: Context, pdfFile: File): String? {
return try { return try {
val fileDescriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY) val fileDescriptor =
ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY)
val pdfRenderer = PdfRenderer(fileDescriptor) val pdfRenderer = PdfRenderer(fileDescriptor)
if (pdfRenderer.pageCount > 0) { if (pdfRenderer.pageCount > 0) {
val page = pdfRenderer.openPage(0) val page = pdfRenderer.openPage(0)
// 创建 Bitmap
val bitmap = createBitmap(page.width, page.height) val bitmap = createBitmap(page.width, page.height)
val canvas = Canvas(bitmap)
canvas.drawColor(Color.WHITE) // 填充白色背景
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
page.close() page.close()
@ -169,5 +212,4 @@ class PdfScanner(
null null
} }
} }
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M927.9,478.1 L168.9,478.1l308.7,-308.9c11.7,-11.7 11.4,-30.9 -0.6,-42.9 -12,-12 -31.2,-12.2 -42.9,-0.5L75.2,484.9c-2,1.7 -3.8,3.6 -5.3,5.7 -4,5.4 -6,11.8 -5.9,18.3 -0.1,7.8 2.7,15.6 8.6,21.4l361.6,361.7c11.7,11.7 30.9,11.4 42.9,-0.5 12,-12 12.2,-31.2 0.6,-42.9L168.4,539.5l759.4,0c16.5,0 29.9,-13.7 29.9,-30.7S944.4,478.1 927.9,478.1z"
android:fillColor="#2c2c2c"/>
</vector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:color="@color/line_color"
android:width="1dp"/>
<corners android:radius="8dp" />
</shape>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -90,7 +90,7 @@
<LinearLayout <LinearLayout
android:id="@+id/pnLayout" android:id="@+id/pnLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="58dp" android:layout_height="64dp"
android:background="@drawable/dr_rounded_corner_top_bg_grey" android:background="@drawable/dr_rounded_corner_top_bg_grey"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
@ -115,18 +115,20 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/poppins_medium"
android:text="@string/notice" android:text="@string/notice"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" /> android:textSize="14sp" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:fontFamily="@font/poppins_regular"
android:maxLines="2" android:maxLines="2"
android:text="@string/permission_notice" android:text="@string/permission_notice"
android:textColor="@color/black_60" android:textColor="@color/black_60"
android:textSize="14sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@ -140,14 +142,14 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/dr_click_btn_bg" android:background="@drawable/dr_click_btn_bg"
android:fontFamily="@font/poppins_semibold"
android:paddingStart="24dp" android:paddingStart="24dp"
android:paddingTop="4dp" android:paddingTop="4dp"
android:paddingEnd="24dp" android:paddingEnd="24dp"
android:paddingBottom="4dp" android:paddingBottom="4dp"
android:text="@string/go" android:text="@string/go"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="14sp" android:textSize="14sp" />
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -173,6 +175,7 @@
android:id="@+id/home_tv" android:id="@+id/home_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/poppins_regular"
android:text="@string/home" android:text="@string/home"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="14sp" /> android:textSize="14sp" />
@ -196,6 +199,7 @@
android:id="@+id/recently_tv" android:id="@+id/recently_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/poppins_regular"
android:text="@string/recently" android:text="@string/recently"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="14sp" /> android:textSize="14sp" />
@ -219,6 +223,7 @@
android:id="@+id/favorite_tv" android:id="@+id/favorite_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/poppins_regular"
android:text="@string/favorite" android:text="@string/favorite"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="14sp" /> android:textSize="14sp" />
@ -242,6 +247,7 @@
android:id="@+id/tools_tv" android:id="@+id/tools_tv"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/poppins_regular"
android:text="@string/tools" android:text="@string/tools"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="14sp" /> android:textSize="14sp" />

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/backBtn"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:gravity="center">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/back_black" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/bg_color">
<com.github.barteksc.pdfviewer.PDFView
android:id="@+id/pdfview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>

View File

@ -2,8 +2,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:background="@color/bg_color" android:background="@color/bg_color"
android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<!-- App Logo --> <!-- App Logo -->
@ -19,10 +19,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:fontFamily="@font/poppins_semibold"
android:text="@string/app_name" android:text="@string/app_name"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="28sp" android:textSize="28sp" />
android:textStyle="bold" />
<!-- App Description --> <!-- App Description -->
<TextView <TextView
@ -31,6 +31,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:alpha="0.8" android:alpha="0.8"
android:fontFamily="@font/poppins_medium"
android:text="@string/splash_desc" android:text="@string/splash_desc"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" /> android:textSize="16sp" />

View File

@ -14,12 +14,19 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<RelativeLayout
android:layout_width="56dp"
android:layout_height="56dp"
android:background="@drawable/dr_item_img_frame">
<ImageView <ImageView
android:id="@+id/tvFileImg" android:id="@+id/tvFileImg"
android:layout_width="54dp" android:layout_width="56dp"
android:layout_height="54dp" android:layout_height="56dp"
android:src="@mipmap/ic_launcher_round" /> android:src="@mipmap/ic_launcher_round" />
</RelativeLayout>
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -32,12 +39,12 @@
android:id="@+id/tvFileName" android:id="@+id/tvFileName"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="marquee"
android:maxLines="1"
android:text="@string/app_name" android:text="@string/app_name"
android:textColor="@color/black" android:textColor="@color/black"
android:maxLines="1"
android:ellipsize="marquee"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:fontFamily="@font/poppins_semibold"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -51,6 +58,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="2月27,2025" android:text="2月27,2025"
android:fontFamily="@font/poppins_medium"
android:textColor="@color/black_60" android:textColor="@color/black_60"
android:textSize="14sp" /> android:textSize="14sp" />
@ -60,6 +68,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:text="8.1MB" android:text="8.1MB"
android:fontFamily="@font/poppins_medium"
android:textColor="@color/black_60" android:textColor="@color/black_60"
android:textSize="14sp" /> android:textSize="14sp" />

View File

@ -40,17 +40,18 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:fontFamily="@font/poppins_semibold"
android:gravity="center" android:gravity="center"
android:text="@string/permission_required" android:text="@string/permission_required"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="20sp" android:textSize="20sp" />
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/pr_desc" android:id="@+id/pr_desc"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:fontFamily="@font/poppins_regular"
android:gravity="center" android:gravity="center"
android:text="@string/permission_required_desc_1" android:text="@string/permission_required_desc_1"
android:textColor="@color/black_80" android:textColor="@color/black_80"
@ -76,6 +77,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:fontFamily="@font/poppins_regular"
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@string/permission_required_desc_2" android:text="@string/permission_required_desc_2"
android:textColor="@color/black_80" android:textColor="@color/black_80"
@ -105,10 +107,10 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@font/poppins_semibold"
android:text="@string/allow_access" android:text="@string/allow_access"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp" />
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -13,7 +13,7 @@
android:text="@string/sort_by" android:text="@string/sort_by"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="18sp" android:textSize="18sp"
android:textStyle="bold" /> android:fontFamily="@font/poppins_semibold"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -41,6 +41,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/created_date" android:text="@string/created_date"
android:fontFamily="@font/poppins_regular"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" /> android:textSize="16sp" />
@ -69,6 +70,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:fontFamily="@font/poppins_regular"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/file_name" android:text="@string/file_name"
android:textColor="@color/black" android:textColor="@color/black"
@ -101,6 +103,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_weight="1" android:layout_weight="1"
android:fontFamily="@font/poppins_regular"
android:text="@string/file_size" android:text="@string/file_size"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" /> android:textSize="16sp" />
@ -145,6 +148,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_weight="1" android:layout_weight="1"
android:fontFamily="@font/poppins_regular"
android:text="@string/ascending" android:text="@string/ascending"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" /> android:textSize="16sp" />
@ -177,6 +181,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/descending" android:text="@string/descending"
android:fontFamily="@font/poppins_regular"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" /> android:textSize="16sp" />
@ -211,7 +216,7 @@
android:text="@string/cancel" android:text="@string/cancel"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:fontFamily="@font/poppins_semibold" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
@ -229,7 +234,7 @@
android:text="@string/ok" android:text="@string/ok"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:fontFamily="@font/poppins_semibold" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -18,7 +18,9 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="40dp" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@ -21,6 +21,7 @@
<string name="file_size">File Size</string> <string name="file_size">File Size</string>
<string name="ascending">Ascending</string> <string name="ascending">Ascending</string>
<string name="descending">Descending</string> <string name="descending">Descending</string>
<string name="permission_denied">权限被拒绝</string> <string name="permission_denied">Permission Denied</string>
<string name="pd_content_notice">Unable to access PDF file, please grant storage permission in settings</string> <string name="pd_content_notice">Unable to access PDF file, please grant storage permission in settings</string>
<string name="file_not">File does not exist</string>
</resources> </resources>

View File

@ -1,6 +1,8 @@
[versions] [versions]
androidpdfviewer = "3.2.8"
appcompat = "1.7.1" appcompat = "1.7.1"
agp = "8.10.1" agp = "8.10.1"
glide = "5.0.4"
kotlin = "2.0.21" kotlin = "2.0.21"
ksp = "2.0.21-1.0.27" ksp = "2.0.21-1.0.27"
coreKtx = "1.17.0" coreKtx = "1.17.0"
@ -10,6 +12,7 @@ espressoCore = "3.7.0"
lifecycleRuntimeKtx = "2.9.2" lifecycleRuntimeKtx = "2.9.2"
immersionbar = "3.2.2" immersionbar = "3.2.2"
immersionbarKtx = "3.2.2" immersionbarKtx = "3.2.2"
pdfboxAndroid = "2.0.27.0"
room_version = "2.7.2" room_version = "2.7.2"
swiperefreshlayout = "1.1.0" swiperefreshlayout = "1.1.0"
recyclerview = "1.4.0" recyclerview = "1.4.0"
@ -17,11 +20,13 @@ protoliteWellKnownTypes = "18.0.1"
material = "1.12.0" material = "1.12.0"
[libraries] [libraries]
androidpdfviewer = { module = "com.github.marain87:AndroidPdfViewer", version.ref = "androidpdfviewer" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room_version" } 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-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room_version" }
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room_version" } androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room_version" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
@ -30,6 +35,7 @@ immersionbar = { group = "com.geyifeng.immersionbar", name = "immersionbar", ver
immersionbar-ktx = { group = "com.geyifeng.immersionbar", name = "immersionbar-ktx", version.ref = "immersionbarKtx" } immersionbar-ktx = { group = "com.geyifeng.immersionbar", name = "immersionbar-ktx", version.ref = "immersionbarKtx" }
androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" } androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" }
androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" } androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" }
pdfbox-android = { module = "com.tom-roush:pdfbox-android", version.ref = "pdfboxAndroid" }
protolite-well-known-types = { group = "com.google.firebase", name = "protolite-well-known-types", version.ref = "protoliteWellKnownTypes" } protolite-well-known-types = { group = "com.google.firebase", name = "protolite-well-known-types", version.ref = "protoliteWellKnownTypes" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }

View File

@ -16,6 +16,7 @@ dependencyResolutionManagement {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven("https://jitpack.io")
} }
} }