1.再次优化扫描文件性能,异步获取缩略图
2.添加列表item,更多设置选项对话框 3.修复数据库键值,不使用hash来作为键值,依据文件路径来判定 4.修复密码输入框实现,使用自带的错误等提示TextInputEditText
This commit is contained in:
parent
ff14a8f3ee
commit
369dc9d129
@ -19,18 +19,18 @@ interface BookmarkDao {
|
||||
@Query("SELECT * FROM bookmarks WHERE id = :bookmarkId")
|
||||
suspend fun getById(bookmarkId: Long): BookmarkEntity?
|
||||
|
||||
@Query("SELECT * FROM bookmarks WHERE pdfHash = :pdfHash ORDER BY pageNumber ASC, createTime ASC")
|
||||
fun getBookmarksByPdf(pdfHash: String): Flow<List<BookmarkEntity>>
|
||||
@Query("SELECT * FROM bookmarks WHERE filePath = :filePath ORDER BY pageNumber ASC, createTime ASC")
|
||||
fun getBookmarksByPdf(filePath: String): Flow<List<BookmarkEntity>>
|
||||
|
||||
@Query("SELECT * FROM bookmarks WHERE pdfHash = :pdfHash AND pageNumber = :pageNumber")
|
||||
suspend fun getBookmarksByPage(pdfHash: String, pageNumber: Int): List<BookmarkEntity>
|
||||
@Query("SELECT * FROM bookmarks WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
||||
suspend fun getBookmarksByPage(filePath: String, pageNumber: Int): List<BookmarkEntity>
|
||||
|
||||
@Query("SELECT COUNT(*) FROM bookmarks WHERE pdfHash = :pdfHash")
|
||||
suspend fun getBookmarkCount(pdfHash: String): Int
|
||||
@Query("SELECT COUNT(*) FROM bookmarks WHERE filePath = :filePath")
|
||||
suspend fun getBookmarkCount(filePath: String): Int
|
||||
|
||||
@Query("DELETE FROM bookmarks WHERE pdfHash = :pdfHash")
|
||||
suspend fun deleteAllByPdf(pdfHash: String)
|
||||
@Query("DELETE FROM bookmarks WHERE filePath = :filePath")
|
||||
suspend fun deleteAllByPdf(filePath: String)
|
||||
|
||||
@Query("DELETE FROM bookmarks WHERE pdfHash = :pdfHash AND pageNumber = :pageNumber")
|
||||
suspend fun deleteByPage(pdfHash: String, pageNumber: Int)
|
||||
@Query("DELETE FROM bookmarks WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
||||
suspend fun deleteByPage(filePath: String, pageNumber: Int)
|
||||
}
|
||||
@ -19,21 +19,21 @@ interface NoteDao {
|
||||
@Query("SELECT * FROM notes WHERE id = :noteId")
|
||||
suspend fun getById(noteId: Long): NoteEntity?
|
||||
|
||||
@Query("SELECT * FROM notes WHERE pdfHash = :pdfHash ORDER BY pageNumber ASC, createTime ASC")
|
||||
fun getNotesByPdf(pdfHash: String): Flow<List<NoteEntity>>
|
||||
@Query("SELECT * FROM notes WHERE filePath = :filePath ORDER BY pageNumber ASC, createTime ASC")
|
||||
fun getNotesByPdf(filePath: String): Flow<List<NoteEntity>>
|
||||
|
||||
@Query("SELECT * FROM notes WHERE pdfHash = :pdfHash AND pageNumber = :pageNumber")
|
||||
suspend fun getNotesByPage(pdfHash: String, pageNumber: Int): List<NoteEntity>
|
||||
@Query("SELECT * FROM notes WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
||||
suspend fun getNotesByPage(filePath: String, pageNumber: Int): List<NoteEntity>
|
||||
|
||||
@Query("SELECT * FROM notes WHERE pdfHash = :pdfHash AND noteType = :noteType")
|
||||
fun getNotesByType(pdfHash: String, noteType: String): Flow<List<NoteEntity>>
|
||||
@Query("SELECT * FROM notes WHERE filePath = :filePath AND noteType = :noteType")
|
||||
fun getNotesByType(filePath: String, noteType: String): Flow<List<NoteEntity>>
|
||||
|
||||
@Query("SELECT COUNT(*) FROM notes WHERE pdfHash = :pdfHash")
|
||||
suspend fun getNoteCount(pdfHash: String): Int
|
||||
@Query("SELECT COUNT(*) FROM notes WHERE filePath = :filePath")
|
||||
suspend fun getNoteCount(filePath: String): Int
|
||||
|
||||
@Query("DELETE FROM notes WHERE pdfHash = :pdfHash")
|
||||
suspend fun deleteAllByPdf(pdfHash: String)
|
||||
@Query("DELETE FROM notes WHERE filePath = :filePath")
|
||||
suspend fun deleteAllByPdf(filePath: String)
|
||||
|
||||
@Query("DELETE FROM notes WHERE pdfHash = :pdfHash AND pageNumber = :pageNumber")
|
||||
suspend fun deleteByPage(pdfHash: String, pageNumber: Int)
|
||||
@Query("DELETE FROM notes WHERE filePath = :filePath AND pageNumber = :pageNumber")
|
||||
suspend fun deleteByPage(filePath: String, pageNumber: Int)
|
||||
}
|
||||
@ -34,6 +34,6 @@ interface PdfDocumentDao {
|
||||
@Delete
|
||||
suspend fun delete(document: PdfDocumentEntity)
|
||||
|
||||
@Query("DELETE FROM pdf_documents WHERE fileHash = :fileHash")
|
||||
suspend fun deleteByHash(fileHash: String)
|
||||
@Query("DELETE FROM pdf_documents WHERE filePath = :filePath")
|
||||
suspend fun deleteByPath(filePath: String)
|
||||
}
|
||||
@ -11,25 +11,25 @@ interface RecentReadDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertOrUpdate(recentRead: RecentReadEntity)
|
||||
|
||||
@Query("SELECT * FROM recently_read WHERE pdfHash = :pdfHash")
|
||||
suspend fun getByPdfHash(pdfHash: String): RecentReadEntity?
|
||||
@Query("SELECT * FROM recently_read WHERE filePath = :filePath")
|
||||
suspend fun getByPdfHash(filePath: String): RecentReadEntity?
|
||||
|
||||
@Query("""
|
||||
SELECT pdf_documents.*
|
||||
FROM pdf_documents
|
||||
INNER JOIN recently_read ON pdf_documents.fileHash = recently_read.pdfHash
|
||||
INNER JOIN recently_read ON pdf_documents.filePath = recently_read.filePath
|
||||
ORDER BY recently_read.lastOpenedTime DESC
|
||||
""")
|
||||
fun getRecentReadDocuments(): Flow<List<PdfDocumentEntity>>
|
||||
|
||||
@Query("UPDATE recently_read SET lastOpenedTime = :time, openedCount = openedCount + 1 WHERE pdfHash = :pdfHash")
|
||||
suspend fun updateOpenTime(pdfHash: String, time: Long = System.currentTimeMillis())
|
||||
@Query("UPDATE recently_read SET lastOpenedTime = :time, openedCount = openedCount + 1 WHERE filePath = :filePath")
|
||||
suspend fun updateOpenTime(filePath: String, time: Long = System.currentTimeMillis())
|
||||
|
||||
@Query("UPDATE recently_read SET totalReadTime = totalReadTime + :additionalTime WHERE pdfHash = :pdfHash")
|
||||
suspend fun addReadTime(pdfHash: String, additionalTime: Long)
|
||||
@Query("UPDATE recently_read SET totalReadTime = totalReadTime + :additionalTime WHERE filePath = :filePath")
|
||||
suspend fun addReadTime(filePath: String, additionalTime: Long)
|
||||
|
||||
@Query("DELETE FROM recently_read WHERE pdfHash = :pdfHash")
|
||||
suspend fun deleteByPdfHash(pdfHash: String)
|
||||
@Query("DELETE FROM recently_read WHERE filePath = :filePath")
|
||||
suspend fun deleteByPdfPath(filePath: String)
|
||||
|
||||
@Query("DELETE FROM recently_read WHERE lastOpenedTime < :cutoffTime")
|
||||
suspend fun deleteOldRecents(cutoffTime: Long)
|
||||
|
||||
@ -9,17 +9,17 @@ import androidx.room.PrimaryKey
|
||||
tableName = "bookmarks",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = PdfDocumentEntity::class,
|
||||
parentColumns = ["fileHash"],
|
||||
childColumns = ["pdfHash"],
|
||||
parentColumns = ["filePath"],
|
||||
childColumns = ["filePath"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)],
|
||||
indices = [Index(value = ["pdfHash"])]
|
||||
indices = [Index(value = ["filePath"])]
|
||||
)
|
||||
data class BookmarkEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long = 0,
|
||||
|
||||
val pdfHash: String, // 关联PdfDocumentEntity的fileHash
|
||||
val filePath: String, // 关联PdfDocumentEntity的fileHash
|
||||
val pageNumber: Int, // 页码(从0开始)
|
||||
val label: String, // 书签标签
|
||||
val positionX: Float = 0f, // 页面内X位置
|
||||
|
||||
@ -9,17 +9,17 @@ import androidx.room.PrimaryKey
|
||||
tableName = "notes",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = PdfDocumentEntity::class,
|
||||
parentColumns = ["fileHash"],
|
||||
childColumns = ["pdfHash"],
|
||||
parentColumns = ["filePath"],
|
||||
childColumns = ["filePath"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)],
|
||||
indices = [Index(value = ["pdfHash"])]
|
||||
indices = [Index(value = ["filePath"])]
|
||||
)
|
||||
data class NoteEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long = 0,
|
||||
|
||||
val pdfHash: String, // 关联PdfDocumentEntity的fileHash
|
||||
val filePath: String, // 关联PdfDocumentEntity的fileHash
|
||||
val pageNumber: Int, // 页码(从0开始)
|
||||
val noteType: String, // 注释类型: HIGHLIGHT, TEXT_NOTE, DRAWING
|
||||
val content: String, // 注释内容(文本或序列化的绘制数据)
|
||||
|
||||
@ -9,9 +9,9 @@ import kotlinx.parcelize.Parcelize
|
||||
@Entity(tableName = "pdf_documents")
|
||||
data class PdfDocumentEntity(
|
||||
@PrimaryKey
|
||||
val fileHash: String, // 文件内容哈希(MD5/SHA-1)
|
||||
|
||||
val filePath: String, // 当前文件路径
|
||||
|
||||
val fileHash: String? = null, // 文件内容哈希(MD5/SHA-1),扫描时不进行获取,需要做判定再单独获取
|
||||
val fileName: String, // 文件名
|
||||
val fileSize: Long, // 文件大小(字节)
|
||||
val lastModified: Long, // 文件最后修改时间
|
||||
|
||||
@ -9,17 +9,17 @@ import androidx.room.PrimaryKey
|
||||
tableName = "recently_read",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = PdfDocumentEntity::class,
|
||||
parentColumns = ["fileHash"],
|
||||
childColumns = ["pdfHash"],
|
||||
parentColumns = ["filePath"],
|
||||
childColumns = ["filePath"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)],
|
||||
indices = [Index(value = ["pdfHash"])]
|
||||
indices = [Index(value = ["filePath"])]
|
||||
)
|
||||
data class RecentReadEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long = 0,
|
||||
|
||||
val pdfHash: String, // 关联PdfDocumentEntity的fileHash
|
||||
val filePath: String, // 关联PdfDocumentEntity的filePath
|
||||
val lastOpenedTime: Long, // 最后打开时间
|
||||
val openedCount: Int = 1, // 打开次数
|
||||
val totalReadTime: Long = 0, // 总阅读时长(毫秒)
|
||||
|
||||
@ -41,16 +41,16 @@ class PdfRepository private constructor(context: Context) {
|
||||
fun searchDocuments(query: String): Flow<List<PdfDocumentEntity>> =
|
||||
pdfDao.searchDocuments(query)
|
||||
|
||||
suspend fun updateFavoriteStatus(fileHash: String, isFavorite: Boolean) {
|
||||
val document = pdfDao.getByPath(fileHash)?.copy(
|
||||
suspend fun updateFavoriteStatus(filePath: String, isFavorite: Boolean) {
|
||||
val document = pdfDao.getByPath(filePath)?.copy(
|
||||
isFavorite = isFavorite,
|
||||
addedToFavoriteTime = if (isFavorite) System.currentTimeMillis() else null
|
||||
)
|
||||
document?.let { pdfDao.update(it) }
|
||||
}
|
||||
|
||||
suspend fun updateReadingProgress(fileHash: String, page: Int, progress: Float) {
|
||||
val document = pdfDao.getByPath(fileHash)?.copy(
|
||||
suspend fun updateReadingProgress(filePath: String, page: Int, progress: Float) {
|
||||
val document = pdfDao.getByPath(filePath)?.copy(
|
||||
lastOpenedTime = System.currentTimeMillis(),
|
||||
lastReadPage = page,
|
||||
readingProgress = progress
|
||||
@ -58,29 +58,37 @@ class PdfRepository private constructor(context: Context) {
|
||||
document?.let { pdfDao.update(it) }
|
||||
}
|
||||
|
||||
suspend fun updatePasswordStatus(fileHash: String, isPassword: Boolean) {
|
||||
val document = pdfDao.getByPath(fileHash)?.copy(
|
||||
suspend fun updatePasswordStatus(filePath: String, isPassword: Boolean) {
|
||||
val document = pdfDao.getByPath(filePath)?.copy(
|
||||
isPassword = isPassword
|
||||
)
|
||||
document?.let { pdfDao.update(it) }
|
||||
}
|
||||
|
||||
suspend fun updatePassword(fileHash: String, password: String?) {
|
||||
val document = pdfDao.getByPath(fileHash)?.copy(
|
||||
suspend fun updatePassword(filePath: String, password: String?) {
|
||||
val document = pdfDao.getByPath(filePath)?.copy(
|
||||
password = password
|
||||
)
|
||||
document?.let { pdfDao.update(it) }
|
||||
}
|
||||
|
||||
suspend fun updateThumbnailPath(filePath: String, path: String) {
|
||||
val document = pdfDao.getByPath(filePath)?.copy(
|
||||
thumbnailPath = path,
|
||||
)
|
||||
document?.let { pdfDao.update(it) }
|
||||
}
|
||||
|
||||
|
||||
// 最近阅读相关操作
|
||||
suspend fun addToRecent(pdfHash: String, page: Int = 0) {
|
||||
val existing = recentDao.getByPdfHash(pdfHash)
|
||||
suspend fun addToRecent(filePath: String, page: Int = 0) {
|
||||
val existing = recentDao.getByPdfHash(filePath)
|
||||
if (existing != null) {
|
||||
recentDao.updateOpenTime(pdfHash)
|
||||
recentDao.updateOpenTime(filePath)
|
||||
} else {
|
||||
recentDao.insertOrUpdate(
|
||||
RecentReadEntity(
|
||||
pdfHash = pdfHash,
|
||||
filePath = filePath,
|
||||
lastOpenedTime = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
@ -113,7 +121,7 @@ class PdfRepository private constructor(context: Context) {
|
||||
// 组合查询
|
||||
suspend fun getPdfWithDetails(pdfHash: String): Flow<PdfDetails> {
|
||||
return combine(
|
||||
pdfDao.getByPath(pdfHash)?.let { kotlinx.coroutines.flow.flowOf(it) }
|
||||
pdfDao.getByHash(pdfHash)?.let { kotlinx.coroutines.flow.flowOf(it) }
|
||||
?: kotlinx.coroutines.flow.flowOf(null),
|
||||
bookmarkDao.getBookmarksByPdf(pdfHash),
|
||||
noteDao.getNotesByPdf(pdfHash)
|
||||
@ -144,11 +152,11 @@ class PdfRepository private constructor(context: Context) {
|
||||
}
|
||||
|
||||
// 数据清理
|
||||
suspend fun deleteDocument(fileHash: String) {
|
||||
pdfDao.deleteByHash(fileHash)
|
||||
recentDao.deleteByPdfHash(fileHash)
|
||||
bookmarkDao.deleteAllByPdf(fileHash)
|
||||
noteDao.deleteAllByPdf(fileHash)
|
||||
suspend fun deleteDocument(filePath: String) {
|
||||
pdfDao.deleteByPath(filePath)
|
||||
recentDao.deleteByPdfPath(filePath)
|
||||
bookmarkDao.deleteAllByPdf(filePath)
|
||||
noteDao.deleteAllByPdf(filePath)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@ -10,16 +10,12 @@ import com.all.pdfreader.pro.app.PDFReaderApplication
|
||||
import com.all.pdfreader.pro.app.R
|
||||
import com.all.pdfreader.pro.app.databinding.ActivityMainBinding
|
||||
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
|
||||
import com.all.pdfreader.pro.app.ui.fragment.HomeFrag
|
||||
import com.all.pdfreader.pro.app.ui.fragment.RecentlyFrag
|
||||
import com.all.pdfreader.pro.app.ui.fragment.ToolsFrag
|
||||
import com.all.pdfreader.pro.app.model.SortConfig
|
||||
import com.all.pdfreader.pro.app.model.SortField
|
||||
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.PdfScanner
|
||||
import com.all.pdfreader.pro.app.util.StoragePermissionHelper
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
|
||||
@ -123,7 +123,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList
|
||||
private fun saveReadingProgress() {
|
||||
lifecycleScope.launch {
|
||||
repository.updateReadingProgress(
|
||||
pdfDocument.fileHash, pdfDocument.lastReadPage, pdfDocument.readingProgress
|
||||
pdfDocument.filePath, pdfDocument.lastReadPage, pdfDocument.readingProgress
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,128 @@
|
||||
package com.all.pdfreader.pro.app.ui.dialog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.all.pdfreader.pro.app.R
|
||||
import com.all.pdfreader.pro.app.databinding.DialogListMoreBinding
|
||||
import com.all.pdfreader.pro.app.databinding.DialogPermissionBinding
|
||||
import com.all.pdfreader.pro.app.databinding.DialogSortBinding
|
||||
import com.all.pdfreader.pro.app.model.SortConfig
|
||||
import com.all.pdfreader.pro.app.model.SortDirection
|
||||
import com.all.pdfreader.pro.app.model.SortField
|
||||
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
|
||||
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||
import com.all.pdfreader.pro.app.sp.AppStore
|
||||
import com.all.pdfreader.pro.app.ui.act.MainActivity.SortableFragment
|
||||
import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
|
||||
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
|
||||
import com.all.pdfreader.pro.app.util.FileUtils.toFormatFileSize
|
||||
import com.all.pdfreader.pro.app.util.FileUtils.toSlashDate
|
||||
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
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 lateinit var pdfDocument: PdfDocumentEntity
|
||||
private var isFavorite: Boolean = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DialogListMoreBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.window?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
|
||||
?.setBackgroundResource(R.drawable.dr_rounded_corner_12_bg_white)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewModel.pdfDocument.observe(this) { document ->
|
||||
document?.let {
|
||||
pdfDocument = it
|
||||
isFavorite = pdfDocument.isFavorite
|
||||
initUi()
|
||||
setupOnClick()
|
||||
|
||||
} ?: run {
|
||||
showToast(getString(R.string.file_not))
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
viewModel.getPDFDocument(filePath)
|
||||
}
|
||||
|
||||
private fun initUi() {
|
||||
binding.tvFileName.text = pdfDocument.fileName
|
||||
binding.tvFileSize.text = pdfDocument.fileSize.toFormatFileSize()
|
||||
binding.tvFileDate.text = pdfDocument.lastModified.toSlashDate()
|
||||
if (pdfDocument.isPassword) {
|
||||
binding.lockLayout.visibility = View.VISIBLE
|
||||
binding.tvFileImg.visibility = View.GONE
|
||||
} else {
|
||||
binding.lockLayout.visibility = View.GONE
|
||||
binding.tvFileImg.visibility = View.VISIBLE
|
||||
Glide.with(binding.root).load(pdfDocument.thumbnailPath)
|
||||
.transform(CenterCrop(), RoundedCorners(8.dpToPx(binding.root.context)))
|
||||
.into(binding.tvFileImg)
|
||||
}
|
||||
updateCollectUi(isFavorite)
|
||||
}
|
||||
|
||||
private fun setupOnClick() {
|
||||
binding.collectBtn.setClickWithAnimation(duration = 250) {
|
||||
isFavorite = !isFavorite
|
||||
updateCollectUi(isFavorite)
|
||||
saveCollectState(isFavorite)
|
||||
}
|
||||
binding.renameFileBtn.setOnClickListener {
|
||||
RenameDialogFragment(pdfDocument.filePath, onOkClick = {
|
||||
|
||||
}, onCancelClick = {
|
||||
|
||||
}).show(parentFragmentManager, "ListMoreDialogFragment")
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveCollectState(b: Boolean) {
|
||||
pdfDocument = pdfDocument.copy(
|
||||
isFavorite = b
|
||||
)
|
||||
lifecycleScope.launch {
|
||||
pdfRepository.updateFavoriteStatus(pdfDocument.filePath, pdfDocument.isFavorite)
|
||||
}
|
||||
if (b) {
|
||||
showToast(getString(R.string.added_to_favorites))
|
||||
} else {
|
||||
showToast(getString(R.string.removed_from_favorites))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCollectUi(b: Boolean) {
|
||||
if (b) {
|
||||
binding.collectIv.setImageResource(R.drawable.collected)
|
||||
} else {
|
||||
binding.collectIv.setImageResource(R.drawable.collect)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showToast(message: String) {
|
||||
Toast.makeText(requireActivity(), message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ package com.all.pdfreader.pro.app.ui.dialog
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -21,7 +20,6 @@ class PdfPasswordProtectionDialogFragment(
|
||||
) : DialogFragment() {
|
||||
|
||||
private lateinit var binding: DialogPdfPasswordProtectionBinding
|
||||
var isPasswordVisible = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
@ -67,23 +65,5 @@ class PdfPasswordProtectionDialogFragment(
|
||||
binding.tilPassword.error = getString(R.string.incorrect_password)
|
||||
}
|
||||
}
|
||||
binding.showPasswordBtn.setOnClickListener {
|
||||
isPasswordVisible = !isPasswordVisible
|
||||
showOrHidePassword(isPasswordVisible)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showOrHidePassword(b: Boolean) {
|
||||
if (b) {
|
||||
binding.etPassword.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
binding.showPasswordIv.setImageResource(R.drawable.show_password)
|
||||
} else {
|
||||
binding.etPassword.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
binding.showPasswordIv.setImageResource(R.drawable.hide_password)
|
||||
}
|
||||
// 保持光标在末尾
|
||||
binding.etPassword.setSelection(binding.etPassword.text?.length ?: 0)
|
||||
}
|
||||
}
|
||||
@ -3,18 +3,15 @@ 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
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.all.pdfreader.pro.app.R
|
||||
import com.all.pdfreader.pro.app.databinding.DialogPdfSetPasswordBinding
|
||||
import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
|
||||
class PdfSetPasswordDialog(
|
||||
private val onCancelled: () -> Unit, private val onPasswordSet: (String) -> Unit
|
||||
@ -22,8 +19,6 @@ class PdfSetPasswordDialog(
|
||||
|
||||
) {
|
||||
private lateinit var binding: DialogPdfSetPasswordBinding
|
||||
private var isEnterPasswordVisible = false
|
||||
private var isConfirmPasswordVisible = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
@ -66,23 +61,6 @@ class PdfSetPasswordDialog(
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
binding.enterShowPasswordBtn.setOnClickListener {
|
||||
isEnterPasswordVisible = !isEnterPasswordVisible
|
||||
showOrHidePassword(
|
||||
binding.etPassword,
|
||||
binding.enterShowPasswordIv,
|
||||
isEnterPasswordVisible
|
||||
)
|
||||
}
|
||||
binding.confirmShowPasswordBtn.setOnClickListener {
|
||||
isConfirmPasswordVisible = !isConfirmPasswordVisible
|
||||
showOrHidePassword(
|
||||
binding.etConfirmPassword,
|
||||
binding.confirmShowPasswordIv,
|
||||
isConfirmPasswordVisible
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTextWatchers() {
|
||||
@ -140,17 +118,4 @@ class PdfSetPasswordDialog(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun showOrHidePassword(inputView: TextInputEditText, imageView: ImageView, b: Boolean) {
|
||||
if (b) {
|
||||
inputView.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
imageView.setImageResource(R.drawable.show_password)
|
||||
} else {
|
||||
inputView.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
imageView.setImageResource(R.drawable.hide_password)
|
||||
}
|
||||
// 保持光标在末尾
|
||||
inputView.setSelection(inputView.text?.length ?: 0)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package com.all.pdfreader.pro.app.ui.dialog
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
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.isPdfPasswordCorrect
|
||||
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
||||
import java.io.File
|
||||
|
||||
class RenameDialogFragment(
|
||||
private val filePath: String,
|
||||
private val onOkClick: () -> Unit,
|
||||
private val onCancelClick: () -> Unit
|
||||
) : DialogFragment() {
|
||||
|
||||
private lateinit var binding: DialogRenameFileBinding
|
||||
private val viewModel by lazy { ViewModelProvider(this)[PdfViewModel::class.java] }
|
||||
private lateinit var pdfDocument: PdfDocumentEntity
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DialogRenameFileBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.window?.apply {
|
||||
// 去掉系统默认的背景 padding
|
||||
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
|
||||
// 设置宽度为全屏减去 16dp
|
||||
val margin = resources.getDimensionPixelSize(R.dimen.dialog_margin) // 16dp
|
||||
val width = resources.displayMetrics.widthPixels - margin * 2
|
||||
setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
viewModel.pdfDocument.observe(this) { document ->
|
||||
document?.let {
|
||||
pdfDocument = it
|
||||
initView()
|
||||
setupOnClick()
|
||||
} ?: run {
|
||||
showToast(getString(R.string.file_not))
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
viewModel.getPDFDocument(filePath)
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.etName.showKeyboard()
|
||||
binding.etName.setText(pdfDocument.fileName)
|
||||
// 保持光标在末尾
|
||||
binding.etName.setSelection(binding.etName.text?.length ?: 0)
|
||||
|
||||
}
|
||||
|
||||
private fun setupOnClick() {
|
||||
binding.tvCancel.setOnClickListener {
|
||||
onCancelClick()
|
||||
dismiss()
|
||||
}
|
||||
binding.tvConfirm.setOnClickListener {
|
||||
val text = binding.etName.text.toString()
|
||||
if (validateEnter(text)) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateEnter(name: String): Boolean {
|
||||
// 不允许为空
|
||||
if (name.isBlank()) {
|
||||
binding.tilName.error = getString(R.string.name_not_empty)
|
||||
return false
|
||||
}
|
||||
|
||||
// 名字未做修改
|
||||
if (name == pdfDocument.fileName) {
|
||||
binding.tilName.error = getString(R.string.name_not_changed)
|
||||
return false
|
||||
}
|
||||
|
||||
// 含有非法字符
|
||||
val invalidChars = "[/\\\\:*?\"<>|]".toRegex()
|
||||
if (invalidChars.containsMatchIn(name)) {
|
||||
binding.tilName.error = getString(R.string.name_invalid_chars)
|
||||
return false
|
||||
}
|
||||
|
||||
// 长度过长
|
||||
if (name.length > 255) {
|
||||
binding.tilName.error = getString(R.string.name_too_long)
|
||||
return false
|
||||
}
|
||||
|
||||
// 与现有文件重名
|
||||
val parentDir = File(pdfDocument.filePath).parentFile
|
||||
if (parentDir != null && File(parentDir, name).exists()) {
|
||||
binding.tilName.error = getString(R.string.name_already_exists)
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁止开头/结尾空格
|
||||
if (name != name.trim()) {
|
||||
binding.tilName.error = getString(R.string.name_start_end_space)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private fun showToast(message: String) {
|
||||
Toast.makeText(requireActivity(), message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.all.pdfreader.pro.app.R
|
||||
import com.all.pdfreader.pro.app.databinding.DialogPermissionBinding
|
||||
import com.all.pdfreader.pro.app.databinding.DialogSortBinding
|
||||
import com.all.pdfreader.pro.app.model.SortConfig
|
||||
@ -30,6 +31,12 @@ class SortDialogFragment(
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.window?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
|
||||
?.setBackgroundResource(R.drawable.dr_rounded_corner_12_bg_white)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val appStore = AppStore(requireActivity())
|
||||
|
||||
@ -15,6 +15,7 @@ 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.PdfViewActivity
|
||||
import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter
|
||||
import com.all.pdfreader.pro.app.ui.dialog.ListMoreDialogFragment
|
||||
import com.all.pdfreader.pro.app.util.PdfScanner
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -40,7 +41,7 @@ class HomeFrag : BaseFrag(), MainActivity.SortableFragment {
|
||||
val intent = PdfViewActivity.createIntent(requireContext(), pdf.filePath)
|
||||
startActivity(intent)
|
||||
}, onMoreClick = { pdf ->
|
||||
|
||||
ListMoreDialogFragment(pdf.filePath).show(parentFragmentManager, TAG)
|
||||
})
|
||||
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
@ -14,7 +14,11 @@ object AppUtils {
|
||||
*
|
||||
* @param onClick 点击后立即执行的逻辑
|
||||
*/
|
||||
fun View.setClickWithAnimation(onClick: () -> Unit) {
|
||||
fun View.setClickWithAnimation(
|
||||
scaleFactor: Float = 1.2f,
|
||||
duration: Long = 150,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
this.setOnClickListener {
|
||||
// 禁用点击,防止动画未完成重复点击
|
||||
this.isEnabled = false
|
||||
@ -24,14 +28,14 @@ object AppUtils {
|
||||
|
||||
// 播放动画
|
||||
this.animate()
|
||||
.scaleX(1.2f)
|
||||
.scaleY(1.2f)
|
||||
.setDuration(150)
|
||||
.scaleX(scaleFactor)
|
||||
.scaleY(scaleFactor)
|
||||
.setDuration(duration)
|
||||
.withEndAction {
|
||||
this.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(150)
|
||||
.setDuration(duration)
|
||||
.withEndAction {
|
||||
// 动画结束恢复点击
|
||||
this.isEnabled = true
|
||||
|
||||
@ -237,6 +237,9 @@ object FileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算文件哈希
|
||||
*/
|
||||
fun calculateFileHash(filePath: String): String? {
|
||||
return try {
|
||||
val file = File(filePath)
|
||||
|
||||
@ -2,9 +2,7 @@ package com.all.pdfreader.pro.app.util
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.pdf.PdfRenderer
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import androidx.core.graphics.createBitmap
|
||||
@ -12,7 +10,10 @@ import com.all.pdfreader.pro.app.PDFReaderApplication
|
||||
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.isPdfEncrypted
|
||||
import com.shockwave.pdfium.PdfDocument
|
||||
import com.shockwave.pdfium.PdfiumCore
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -29,7 +30,9 @@ class PdfScanner(
|
||||
suspend fun scanAndLoadPdfFiles(isNeedFullScan: Boolean, callback: (Boolean) -> Unit = {}) {
|
||||
if (!StoragePermissionHelper.hasBasicStoragePermission(context)) {
|
||||
LogUtil.logDebug(TAG, "权限不足")
|
||||
callback.invoke(false)
|
||||
withContext(Dispatchers.Main) {
|
||||
callback.invoke(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
scanMutex.withLock {// 保证同一时间只有一次扫描
|
||||
@ -64,23 +67,30 @@ class PdfScanner(
|
||||
LogUtil.logDebug(TAG, "密码状态变化 -> ${doc.fileName}")
|
||||
updatedDoc = updatedDoc.copy(isPassword = currentIsPassword)
|
||||
}
|
||||
// 缩略图(仅非加密文件)
|
||||
if (!currentIsPassword) {
|
||||
val newThumbnail = generateThumbnail(context, file)
|
||||
if (doc.thumbnailPath != newThumbnail) {
|
||||
updatedDoc =
|
||||
updatedDoc.copy(thumbnailPath = newThumbnail)
|
||||
}
|
||||
} else if (doc.thumbnailPath != null) {
|
||||
updatedDoc = updatedDoc.copy(thumbnailPath = null)
|
||||
}
|
||||
pdfRepository.insertOrUpdateDocument(updatedDoc)
|
||||
LogUtil.logDebug(TAG, "✅数据库已更新: ${doc.fileName}")
|
||||
|
||||
if (!currentIsPassword) {
|
||||
// 异步生成缩略图,但要避免阻塞
|
||||
launch(Dispatchers.IO) {
|
||||
LogUtil.logDebug(TAG, "异步获取图片更新数据")
|
||||
val newThumbnail = generateFastThumbnail(context, file)
|
||||
if (newThumbnail != null && doc.thumbnailPath != newThumbnail) {
|
||||
pdfRepository.updateThumbnailPath(doc.filePath, newThumbnail)
|
||||
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
||||
}
|
||||
}
|
||||
} else if (doc.thumbnailPath != null) {
|
||||
val updatedDocWithoutThumb =
|
||||
updatedDoc.copy(thumbnailPath = null)
|
||||
pdfRepository.insertOrUpdateDocument(updatedDocWithoutThumb)
|
||||
LogUtil.logDebug(TAG, "✅图片为Null: ${doc.fileName}")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 文件不存在 → 删除数据库记录,并触发全盘扫描
|
||||
LogUtil.logDebug(TAG, "文件不存在 -> ${doc.fileName}, 删除记录")
|
||||
pdfRepository.deleteDocument(doc.fileHash)
|
||||
pdfRepository.deleteDocument(doc.filePath)
|
||||
needFullScan = true
|
||||
}
|
||||
}
|
||||
@ -103,98 +113,92 @@ class PdfScanner(
|
||||
TAG,
|
||||
"🔄处理文件 ${index + 1}/${allFiles.size}: ${file.name} - ${file.absolutePath}"
|
||||
)
|
||||
|
||||
if (FileUtils.isPdfFile(file)) {
|
||||
val fileHash = FileUtils.calculateFileHash(file.absolutePath)
|
||||
LogUtil.logDebug(TAG, "🔑文件哈希: $fileHash")
|
||||
val existingDoc =
|
||||
pdfRepository.getDocumentByPath(file.absolutePath)
|
||||
|
||||
if (fileHash != null) {
|
||||
val existingDoc =
|
||||
pdfRepository.getDocumentByPath(file.absolutePath)
|
||||
if (existingDoc == null) {
|
||||
LogUtil.logDebug(
|
||||
TAG, "🆕发现新PDF文件: ${file.name}"
|
||||
)
|
||||
val isPassword = isPdfEncrypted(file)
|
||||
LogUtil.logDebug(TAG, "isPassword->${isPassword}")
|
||||
|
||||
if (existingDoc == null) {
|
||||
LogUtil.logDebug(
|
||||
TAG, "🆕发现新PDF文件: ${file.name}"
|
||||
)
|
||||
var thumbnailPath: String? = null
|
||||
val isPassword = isPdfEncrypted(file)
|
||||
LogUtil.logDebug(TAG, "isPassword->${isPassword}")
|
||||
if (!isPassword) {//没有密码的情况下才去获取缩略图
|
||||
thumbnailPath = generateThumbnail(context, file) ?: ""
|
||||
}
|
||||
LogUtil.logDebug(TAG, "thumbnailPath->${thumbnailPath}")
|
||||
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,
|
||||
thumbnailPath = thumbnailPath,
|
||||
metadataTitle = metadata?.title,
|
||||
metadataAuthor = metadata?.author,
|
||||
metadataSubject = metadata?.subject,
|
||||
metadataKeywords = metadata?.keywords,
|
||||
metadataCreationDate = metadata?.creationDate?.time,
|
||||
metadataModificationDate = metadata?.modificationDate?.time,
|
||||
isPassword = isPassword
|
||||
)
|
||||
pdfRepository.insertOrUpdateDocument(document)
|
||||
LogUtil.logDebug(
|
||||
TAG, " ✅ 已保存到数据库: ${file.name}"
|
||||
)
|
||||
} else {
|
||||
LogUtil.logDebug(TAG, " 📋 文件已存在: ${file.name}")
|
||||
// 🔹 文件已存在,检查是否需要更新
|
||||
var needUpdate = false
|
||||
var updatedDoc = existingDoc.copy()
|
||||
val metadata =
|
||||
PdfMetadataExtractor.extractMetadata(file.absolutePath)
|
||||
val document = PdfDocumentEntity(
|
||||
filePath = file.absolutePath,
|
||||
fileName = file.name,
|
||||
fileSize = file.length(),
|
||||
lastModified = file.lastModified(),
|
||||
pageCount = metadata?.pageCount ?: 0,
|
||||
thumbnailPath = null,
|
||||
metadataTitle = metadata?.title,
|
||||
metadataAuthor = metadata?.author,
|
||||
metadataSubject = metadata?.subject,
|
||||
metadataKeywords = metadata?.keywords,
|
||||
metadataCreationDate = metadata?.creationDate?.time,
|
||||
metadataModificationDate = metadata?.modificationDate?.time,
|
||||
isPassword = isPassword
|
||||
)
|
||||
pdfRepository.insertOrUpdateDocument(document)
|
||||
LogUtil.logDebug(TAG, " ✅ 已保存到数据库: ${file.name}")
|
||||
|
||||
// 路径/修改时间更新
|
||||
if (existingDoc.filePath != file.absolutePath || existingDoc.lastModified != file.lastModified()) {
|
||||
LogUtil.logDebug(TAG, "✅ 路径/修改时间需要更新")
|
||||
updatedDoc = updatedDoc.copy(
|
||||
filePath = file.absolutePath,
|
||||
lastModified = file.lastModified()
|
||||
)
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
// 是否加密更新
|
||||
val currentIsPassword = isPdfEncrypted(file)
|
||||
if (existingDoc.isPassword != currentIsPassword) {
|
||||
LogUtil.logDebug(TAG, "✅ 密码状态需要更新")
|
||||
updatedDoc =
|
||||
updatedDoc.copy(isPassword = currentIsPassword)
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
if (!currentIsPassword) {
|
||||
// 如果不是加密 PDF,再生成缩略图
|
||||
val newThumbnail = generateThumbnail(context, file)
|
||||
if (existingDoc.thumbnailPath != newThumbnail) {
|
||||
LogUtil.logDebug(TAG, "✅ 缩略图需要更新")
|
||||
updatedDoc =
|
||||
updatedDoc.copy(thumbnailPath = newThumbnail)
|
||||
needUpdate = true
|
||||
if (!isPassword) {//没有密码的情况下才去获取缩略图
|
||||
launch(Dispatchers.IO){
|
||||
val newThumbnail = generateFastThumbnail(context, file)
|
||||
if (newThumbnail != null && document.thumbnailPath != newThumbnail) {
|
||||
pdfRepository.updateThumbnailPath(document.filePath, newThumbnail)
|
||||
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
||||
}
|
||||
} else {
|
||||
updatedDoc = updatedDoc.copy(thumbnailPath = null)
|
||||
needUpdate = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LogUtil.logDebug(TAG, " 📋 文件已存在: ${file.name}")
|
||||
// 🔹 文件已存在,检查是否需要更新
|
||||
var needUpdate = false
|
||||
var updatedDoc = existingDoc.copy()
|
||||
|
||||
// 执行更新
|
||||
if (needUpdate) {
|
||||
pdfRepository.insertOrUpdateDocument(updatedDoc)
|
||||
LogUtil.logDebug(
|
||||
TAG, "✅ 数据库已更新: ${file.name}"
|
||||
)
|
||||
} else {
|
||||
LogUtil.logDebug(
|
||||
TAG, "⏩ 无需更新: ${file.name}"
|
||||
)
|
||||
// 路径/修改时间更新
|
||||
if (existingDoc.filePath != file.absolutePath || existingDoc.lastModified != file.lastModified()) {
|
||||
LogUtil.logDebug(TAG, "✅ 路径/修改时间需要更新")
|
||||
updatedDoc = updatedDoc.copy(
|
||||
filePath = file.absolutePath,
|
||||
lastModified = file.lastModified()
|
||||
)
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
// 是否加密更新
|
||||
val currentIsPassword = isPdfEncrypted(file)
|
||||
if (existingDoc.isPassword != currentIsPassword) {
|
||||
LogUtil.logDebug(TAG, "✅ 密码状态需要更新")
|
||||
updatedDoc =
|
||||
updatedDoc.copy(isPassword = currentIsPassword)
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
if (needUpdate) {
|
||||
pdfRepository.insertOrUpdateDocument(updatedDoc)
|
||||
LogUtil.logDebug(TAG, "✅ 数据库已更新: ${file.name}")
|
||||
} else {
|
||||
LogUtil.logDebug(TAG, "⏩ 无需更新: ${file.name}")
|
||||
}
|
||||
|
||||
// 处理缩略图
|
||||
if (!currentIsPassword) {
|
||||
launch(Dispatchers.IO) {
|
||||
LogUtil.logDebug(TAG, "异步获取图片更新数据")
|
||||
val newThumbnail = generateFastThumbnail(context, file)
|
||||
if (newThumbnail != null && existingDoc.thumbnailPath != newThumbnail) {
|
||||
pdfRepository.updateThumbnailPath(existingDoc.filePath, newThumbnail)
|
||||
LogUtil.logDebug(TAG, "✅ 缩略图已更新")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val noThumbDoc = updatedDoc.copy(thumbnailPath = null)
|
||||
pdfRepository.insertOrUpdateDocument(noThumbDoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -202,7 +206,8 @@ class PdfScanner(
|
||||
// 打印数据库中的总记录数
|
||||
pdfRepository.getAllDocumentsOnce().forEach { doc ->
|
||||
LogUtil.logDebug(
|
||||
TAG, " 📖 ${doc.fileName} - ${doc.pageCount}页 - ${
|
||||
TAG,
|
||||
" 📖 ${doc.fileName} - ${doc.filePath} - ${doc.pageCount}页 - ${
|
||||
FileUtils.formatFileSize(
|
||||
doc.fileSize
|
||||
)
|
||||
@ -229,10 +234,14 @@ class PdfScanner(
|
||||
TAG, "$string 本次扫描耗时: $scannerTime ms (${scannerTime / 1000.0} 秒)"
|
||||
)
|
||||
PDFReaderApplication.isNeedFullScan = false
|
||||
callback.invoke(true)
|
||||
withContext(Dispatchers.Main) {
|
||||
callback.invoke(true)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ 扫描出错: ${e.message}", e)
|
||||
callback.invoke(false)
|
||||
withContext(Dispatchers.Main) {
|
||||
callback.invoke(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,40 +261,87 @@ class PdfScanner(
|
||||
}
|
||||
|
||||
|
||||
private fun generateThumbnail(context: Context, pdfFile: File): String? {
|
||||
// private fun generateFastThumbnail(context: Context, pdfFile: File): String? {
|
||||
// return try {
|
||||
// val fileDescriptor =
|
||||
// ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY)
|
||||
// val pdfRenderer = PdfRenderer(fileDescriptor)
|
||||
//
|
||||
// if (pdfRenderer.pageCount > 0) {
|
||||
// val page = pdfRenderer.openPage(0)
|
||||
// // 创建 Bitmap
|
||||
// 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.close()
|
||||
//
|
||||
// // 保存到缓存目录
|
||||
// val cacheDir = File(context.cacheDir, "thumbnails")
|
||||
// if (!cacheDir.exists()) cacheDir.mkdirs()
|
||||
// val thumbFile = File(cacheDir, pdfFile.nameWithoutExtension + ".jpg")
|
||||
//
|
||||
// FileOutputStream(thumbFile).use { out ->
|
||||
// bitmap.compress(Bitmap.CompressFormat.JPEG, 80, out)
|
||||
// }
|
||||
//
|
||||
// pdfRenderer.close()
|
||||
// fileDescriptor.close()
|
||||
//
|
||||
// thumbFile.absolutePath
|
||||
// } else {
|
||||
// pdfRenderer.close()
|
||||
// fileDescriptor.close()
|
||||
// null
|
||||
// }
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun generateFastThumbnail(
|
||||
context: Context,
|
||||
pdfFile: File,
|
||||
maxWidth: Int = 200,
|
||||
maxHeight: Int = 300
|
||||
): String? {
|
||||
return try {
|
||||
val fileDescriptor =
|
||||
ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY)
|
||||
val pdfRenderer = PdfRenderer(fileDescriptor)
|
||||
val pdfiumCore = PdfiumCore(context)
|
||||
val fd = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY)
|
||||
val pdfDocument: PdfDocument = pdfiumCore.newDocument(fd)
|
||||
|
||||
if (pdfRenderer.pageCount > 0) {
|
||||
val page = pdfRenderer.openPage(0)
|
||||
// 创建 Bitmap
|
||||
val bitmap = createBitmap(page.width, page.height)
|
||||
val canvas = Canvas(bitmap)
|
||||
canvas.drawColor(Color.WHITE) // 填充白色背景
|
||||
// 打开第一页
|
||||
pdfiumCore.openPage(pdfDocument, 0)
|
||||
|
||||
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
|
||||
page.close()
|
||||
// 获取原始页宽高
|
||||
val width = pdfiumCore.getPageWidthPoint(pdfDocument, 0)
|
||||
val height = pdfiumCore.getPageHeightPoint(pdfDocument, 0)
|
||||
|
||||
// 保存到缓存目录
|
||||
val cacheDir = File(context.cacheDir, "thumbnails")
|
||||
if (!cacheDir.exists()) cacheDir.mkdirs()
|
||||
val thumbFile = File(cacheDir, pdfFile.nameWithoutExtension + ".jpg")
|
||||
// 计算缩略图尺寸
|
||||
val scale = minOf(maxWidth.toFloat() / width, maxHeight.toFloat() / height)
|
||||
val thumbWidth = (width * scale).toInt()
|
||||
val thumbHeight = (height * scale).toInt()
|
||||
|
||||
FileOutputStream(thumbFile).use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, out)
|
||||
}
|
||||
val bitmap = createBitmap(thumbWidth, thumbHeight)
|
||||
bitmap.eraseColor(Color.WHITE) // 白底
|
||||
|
||||
pdfRenderer.close()
|
||||
fileDescriptor.close()
|
||||
// 渲染第一页到 Bitmap
|
||||
pdfiumCore.renderPageBitmap(pdfDocument, bitmap, 0, 0, 0, thumbWidth, thumbHeight)
|
||||
|
||||
thumbFile.absolutePath
|
||||
} else {
|
||||
pdfRenderer.close()
|
||||
fileDescriptor.close()
|
||||
null
|
||||
// 保存到缓存
|
||||
val cacheDir = File(context.cacheDir, "thumbnails")
|
||||
if (!cacheDir.exists()) cacheDir.mkdirs()
|
||||
val thumbFile = File(cacheDir, pdfFile.nameWithoutExtension + ".jpg")
|
||||
FileOutputStream(thumbFile).use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, out)
|
||||
}
|
||||
|
||||
pdfiumCore.closeDocument(pdfDocument)
|
||||
fd.close()
|
||||
|
||||
thumbFile.absolutePath
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
|
||||
9
app/src/main/res/drawable/collect.xml
Normal file
9
app/src/main/res/drawable/collect.xml
Normal 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="M859.7,253.9c-44.8,-44.8 -102.4,-70.4 -166.4,-70.4 -61.9,0 -121.6,25.6 -166.4,70.4l-14.9,17.1 -17.1,-17.1c-44.8,-44.8 -102.4,-70.4 -166.4,-70.4 -61.9,0 -121.6,25.6 -166.4,70.4 -91.7,91.7 -91.7,243.2 0,337.1l324.3,330.7c6.4,6.4 14.9,8.5 23.5,8.5s17.1,-4.3 23.5,-8.5l324.3,-330.7c44.8,-44.8 68.3,-104.5 68.3,-168.5s-21.3,-123.7 -66.1,-168.5zM814.9,544L512,853.3 209.1,544c-66.1,-68.3 -66.1,-179.2 0,-247.5 32,-32 74.7,-51.2 119.5,-51.2 44.8,0 87.5,17.1 119.5,51.2l38.4,40.5c12.8,12.8 34.1,12.8 44.8,0l38.4,-40.5c32,-32 74.7,-51.2 119.5,-51.2 44.8,0 87.5,17.1 119.5,51.2 32,32 49.1,76.8 49.1,123.7s-12.8,91.7 -42.7,123.7z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/collected.xml
Normal file
9
app/src/main/res/drawable/collected.xml
Normal 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="M669.8,130.8c71.6,-11.1 138.9,11.5 193.3,64.5 55.3,53.9 81.8,125 74.3,199.5 -7.5,73.6 -46.5,146.4 -112.3,210.5 -18.3,17.9 -67.7,66.2 -138.5,135.6 -31.8,31.2 -65.7,64.4 -99.8,98L553.6,871.5l-13.2,12.9a40.6,40.6 0,0 1,-56.8 0l-114.6,-112.6 -24.2,-23.7a677626.4,677626.4 0,0 0,-145.9 -142.8C133.1,541.2 94.1,468.5 86.6,394.8c-7.6,-74.5 18.9,-145.6 74.3,-199.5 54.4,-53.1 121.7,-75.6 193.3,-64.5 53.2,8.2 107.1,34.7 157.8,76.9 50.7,-42.2 104.6,-68.7 157.8,-76.9z"
|
||||
android:fillColor="#F1494F"/>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/delete.xml
Normal file
12
app/src/main/res/drawable/delete.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M874.7,202.7L360.5,202.7c-21.3,0 -40.5,8.5 -55.5,23.5l-217.6,234.7c-25.6,27.7 -25.6,72.5 0,100.3l217.6,234.7c14.9,14.9 34.1,23.5 55.5,23.5L874.7,819.2c40.5,0 74.7,-34.1 74.7,-74.7L949.3,277.3c0,-40.5 -34.1,-74.7 -74.7,-74.7zM885.3,746.7c0,6.4 -4.3,10.7 -10.7,10.7L360.5,757.3c-2.1,0 -6.4,-2.1 -8.5,-4.3l-217.6,-234.7c-4.3,-4.3 -4.3,-10.7 0,-14.9l217.6,-234.7c2.1,-2.1 4.3,-4.3 8.5,-4.3L874.7,264.5c6.4,0 10.7,4.3 10.7,10.7L885.3,746.7z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:pathData="M684.8,403.2c-12.8,-12.8 -32,-12.8 -44.8,0l-64,64 -61.9,-61.9c-12.8,-12.8 -32,-12.8 -44.8,0 -12.8,12.8 -12.8,32 0,44.8l61.9,61.9 -61.9,61.9c-12.8,12.8 -12.8,32 0,44.8 6.4,6.4 14.9,8.5 23.5,8.5s17.1,-2.1 23.5,-8.5l61.9,-61.9L640,618.7c6.4,6.4 14.9,8.5 23.5,8.5s17.1,-2.1 23.5,-8.5c12.8,-12.8 12.8,-32 0,-44.8L620.8,512l61.9,-61.9c12.8,-12.8 12.8,-34.1 2.1,-46.9z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/dr_dialog_indicator_bg.xml
Normal file
10
app/src/main/res/drawable/dr_dialog_indicator_bg.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<size
|
||||
android:width="24dp"
|
||||
android:height="4dp" />
|
||||
<solid android:color="@color/line_color" />
|
||||
|
||||
</shape>
|
||||
5
app/src/main/res/drawable/dr_password_state.xml
Normal file
5
app/src/main/res/drawable/dr_password_state.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/show_password" android:state_checked="true"/>
|
||||
<item android:drawable="@drawable/hide_password"/>
|
||||
</selector>
|
||||
12
app/src/main/res/drawable/duplicate.xml
Normal file
12
app/src/main/res/drawable/duplicate.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<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="M853.3,224h-53.3L800,170.7c0,-40.5 -34.1,-74.7 -74.7,-74.7L170.7,96C130.1,96 96,130.1 96,170.7v554.7c0,40.5 34.1,74.7 74.7,74.7h53.3L224,853.3c0,40.5 34.1,74.7 74.7,74.7h554.7c40.5,0 74.7,-34.1 74.7,-74.7L928,298.7c0,-40.5 -34.1,-74.7 -74.7,-74.7zM160,725.3L160,170.7c0,-6.4 4.3,-10.7 10.7,-10.7h554.7c6.4,0 10.7,4.3 10.7,10.7v554.7c0,6.4 -4.3,10.7 -10.7,10.7L170.7,736c-6.4,0 -10.7,-4.3 -10.7,-10.7zM864,853.3c0,6.4 -4.3,10.7 -10.7,10.7L298.7,864c-6.4,0 -10.7,-4.3 -10.7,-10.7v-53.3L725.3,800c40.5,0 74.7,-34.1 74.7,-74.7L800,288L853.3,288c6.4,0 10.7,4.3 10.7,10.7v554.7z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:pathData="M576,416h-96V320c0,-17.1 -14.9,-32 -32,-32s-32,14.9 -32,32v96H320c-17.1,0 -32,14.9 -32,32s14.9,32 32,32h96V576c0,17.1 14.9,32 32,32s32,-14.9 32,-32v-96H576c17.1,0 32,-14.9 32,-32s-14.9,-32 -32,-32z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/file_delete.xml
Normal file
12
app/src/main/res/drawable/file_delete.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<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="M842.7,285.9l-187.7,-187.7c-14.9,-14.9 -32,-21.3 -53.3,-21.3L234.7,76.8C194.1,74.7 160,108.8 160,149.3v725.3c0,40.5 34.1,74.7 74.7,74.7h554.7c40.5,0 74.7,-34.1 74.7,-74.7L864,337.1c0,-19.2 -8.5,-38.4 -21.3,-51.2zM797.9,330.7c-2.1,2.1 -4.3,0 -8.5,0h-170.7c-6.4,0 -10.7,-4.3 -10.7,-10.7L608,149.3c0,-2.1 0,-6.4 -2.1,-8.5 0,0 2.1,0 2.1,2.1l189.9,187.7zM789.3,885.3L234.7,885.3c-6.4,0 -10.7,-4.3 -10.7,-10.7L224,149.3c0,-6.4 4.3,-10.7 10.7,-10.7h311.5c-2.1,4.3 -2.1,6.4 -2.1,10.7v170.7c0,40.5 34.1,74.7 74.7,74.7h170.7c4.3,0 6.4,0 10.7,-2.1L800,874.7c0,6.4 -4.3,10.7 -10.7,10.7z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:pathData="M640,586.7H384c-17.1,0 -32,14.9 -32,32s14.9,32 32,32h256c17.1,0 32,-14.9 32,-32s-14.9,-32 -32,-32z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
@ -1,9 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="366.8dp"
|
||||
android:height="256dp"
|
||||
android:width="24dp"
|
||||
android:height="17dp"
|
||||
android:viewportWidth="1467"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M299.1,18.5l917.2,917.2a34.1,34.1 0,0 1,-48.3 48.3L250.9,66.7A34.1,34.1 0,1 1,299.1 18.5zM324.6,188.7l50.6,50.6a918.1,918.1 0,0 0,-113.9 67.5,829.3 829.3,0 0,0 -93.1,75.2l-14.1,13.6c-4.5,4.5 -8.9,8.9 -13.1,13.3l-12,12.9A438.3,438.3 0,0 0,99.7 457.4l-7.3,10.6c-10,15.2 -15.5,27.6 -15.9,35.9 -0.6,15.4 23.2,51.4 64.1,93.3l12.7,12.8c28.7,27.9 64.2,57.8 104.4,85.5 140.2,96.5 308.7,155.6 475.9,155.6 75.1,0 150,-11 222.1,-31.2l54.7,54.7c-85.3,27.7 -178.8,44.7 -276.8,44.7 -256.6,0 -483.8,-126.3 -612.9,-246l-13.2,-12.6c-12.9,-12.6 -24.6,-25 -35.2,-37.1l-10.1,-12a468.4,468.4 0,0 1,-4.8 -5.9l-8.9,-11.7C21.6,557.3 7.3,524.7 8.3,501.2c0.9,-21.5 13.8,-50.7 37,-83.5l8.6,-11.7 9.5,-12 5.1,-6.1 10.7,-12.4 5.6,-6.2 11.9,-12.6 12.6,-12.8c4.4,-4.3 8.8,-8.6 13.4,-12.9l14.1,-12.9c12.1,-10.8 24.9,-21.6 38.4,-32.3l16.6,-12.8a976.1,976.1 0,0 1,132.7 -84.2zM733.6,83c400.6,0 725.3,284.8 725.3,418.1 0,42.4 -32.9,100.2 -90.6,159.2l-12.8,12.7 -6.7,6.3 -13.9,12.7c-4.7,4.2 -9.6,8.4 -14.6,12.6l-15.3,12.6c-5.2,4.2 -10.5,8.3 -16,12.5l-16.6,12.4a929.4,929.4 0,0 1,-125.5 76.1l-51,-50.9a880.5,880.5 0,0 0,112.4 -63.8c58.2,-39 106.9,-82.3 140.3,-123.6 28.1,-34.7 41.9,-64.1 41.9,-78.7 0,-14.6 -13.8,-44 -41.8,-78.7 -33.4,-41.3 -82.1,-84.7 -140.3,-123.6 -136.9,-91.6 -305.5,-147.6 -474.9,-147.6 -74.3,0 -148.5,11.7 -220.2,33.2l-54.1,-54.2c84.4,-29.1 177,-47.2 274.4,-47.2zM536.7,400.8l52,52a152.7,152.7 0,0 0,193.2 193.2l52,52.1a220.9,220.9 0,0 1,-297.2 -297.3zM733.6,280.2a220.9,220.9 0,0 1,196.9 321.3L878.5,549.5a152.7,152.7 0,0 0,-193.2 -193.2L633.2,304.3a220,220 0,0 1,100.4 -24.1z"
|
||||
android:fillColor="#bfbfbf"/>
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
|
||||
9
app/src/main/res/drawable/print.xml
Normal file
9
app/src/main/res/drawable/print.xml
Normal 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="M819.2,364.8h-44.8L774.4,128c0,-17.1 -14.9,-32 -32,-32L281.6,96c-17.1,0 -32,14.9 -32,32v236.8L204.8,364.8c-59.7,0 -108.8,49.1 -108.8,108.8v192c0,59.7 49.1,108.8 108.8,108.8h44.8L249.6,896c0,17.1 14.9,32 32,32h460.8c17.1,0 32,-14.9 32,-32v-121.6h44.8c59.7,0 108.8,-49.1 108.8,-108.8v-192c0,-59.7 -49.1,-108.8 -108.8,-108.8zM313.6,160h396.8v204.8L313.6,364.8L313.6,160zM710.4,864L313.6,864L313.6,620.8h396.8v243.2zM864,665.6c0,25.6 -19.2,44.8 -44.8,44.8h-44.8v-121.6c0,-17.1 -14.9,-32 -32,-32L281.6,556.8c-17.1,0 -32,14.9 -32,32v121.6L204.8,710.4c-25.6,0 -44.8,-19.2 -44.8,-44.8v-192c0,-25.6 19.2,-44.8 44.8,-44.8h614.4c25.6,0 44.8,19.2 44.8,44.8v192z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
15
app/src/main/res/drawable/prompt.xml
Normal file
15
app/src/main/res/drawable/prompt.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<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="M512,74.7C270.9,74.7 74.7,270.9 74.7,512S270.9,949.3 512,949.3 949.3,753.1 949.3,512 753.1,74.7 512,74.7zM512,885.3c-204.8,0 -373.3,-168.5 -373.3,-373.3S307.2,138.7 512,138.7 885.3,307.2 885.3,512 716.8,885.3 512,885.3z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:pathData="M512,320m-42.7,0a42.7,42.7 0,1 0,85.3 0,42.7 42.7,0 1,0 -85.3,0Z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:pathData="M512,437.3c-17.1,0 -32,14.9 -32,32v234.7c0,17.1 14.9,32 32,32s32,-14.9 32,-32V469.3c0,-17.1 -14.9,-32 -32,-32z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/rename_text.xml
Normal file
9
app/src/main/res/drawable/rename_text.xml
Normal 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="M853.3,138.7H170.7c-17.1,0 -32,14.9 -32,32v128c0,17.1 14.9,32 32,32s32,-14.9 32,-32V202.7h277.3v618.7H384c-17.1,0 -32,14.9 -32,32s14.9,32 32,32h256c17.1,0 32,-14.9 32,-32s-14.9,-32 -32,-32h-96v-618.7h277.3V298.7c0,17.1 14.9,32 32,32s32,-14.9 32,-32V170.7c0,-17.1 -14.9,-32 -32,-32z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/share.xml
Normal file
12
app/src/main/res/drawable/share.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<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="M874.7,544c-17.1,0 -32,14.9 -32,32v256c0,6.4 -4.3,10.7 -10.7,10.7H192c-6.4,0 -10.7,-4.3 -10.7,-10.7V192c0,-6.4 4.3,-10.7 10.7,-10.7h256c17.1,0 32,-14.9 32,-32s-14.9,-32 -32,-32H192C151.5,117.3 117.3,151.5 117.3,192v640c0,40.5 34.1,74.7 74.7,74.7h640c40.5,0 74.7,-34.1 74.7,-74.7V576c0,-17.1 -14.9,-32 -32,-32z"
|
||||
android:fillColor="#666666"/>
|
||||
<path
|
||||
android:pathData="M874.7,117.3H640c-17.1,0 -32,14.9 -32,32s14.9,32 32,32h157.9L509.9,467.2c-12.8,12.8 -12.8,32 0,44.8 6.4,6.4 14.9,8.5 23.5,8.5s17.1,-2.1 23.5,-8.5l285.9,-285.9V384c0,17.1 14.9,32 32,32s32,-14.9 32,-32V149.3c0,-17.1 -14.9,-32 -32,-32z"
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
@ -1,9 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="433dp"
|
||||
android:height="256dp"
|
||||
android:width="24dp"
|
||||
android:height="14dp"
|
||||
android:viewportWidth="1732"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M872.8,34.5c462.2,0 836.9,328.7 836.9,482.5 0,49 -37.9,115.6 -104.5,183.7l-14.7,14.6 -7.7,7.3 -16,14.6c-5.5,4.9 -11.1,9.7 -16.8,14.6l-17.6,14.5c-6,4.8 -12.2,9.6 -18.4,14.4l-19.2,14.3c-153.2,110.8 -375.1,204.5 -622,204.5 -296,0 -558.2,-145.7 -707.2,-283.9l-15.3,-14.5c-14.8,-14.5 -28.4,-28.8 -40.6,-42.8l-11.7,-13.9a540.4,540.4 0,0 1,-5.5 -6.9l-10.3,-13.5C51.2,581.7 34.8,544.1 35.8,516.9c1,-24.9 16,-58.4 42.7,-96.4l10,-13.5 10.9,-13.9c1.9,-2.4 3.8,-4.7 5.8,-7.1l12.3,-14.3 6.5,-7.2 13.7,-14.6 14.6,-14.7c5,-4.9 10.2,-9.9 15.5,-14.8l16.3,-14.9c13.9,-12.4 28.8,-24.9 44.3,-37.3l19.1,-14.8c152.8,-115.4 375.5,-219.1 625.2,-219.1zM872.8,113.2c-192.4,0 -384.2,67.9 -544.9,179.4a957,957 0,0 0,-107.4 86.7l-16.3,15.6c-5.2,5.2 -10.3,10.3 -15.1,15.4l-13.9,14.9a505.7,505.7 0,0 0,-33.8 41.2l-8.4,12.2c-11.5,17.6 -18,31.9 -18.4,41.5 -0.7,17.7 26.9,59.2 74,107.6l14.7,14.7c33.1,32.2 74,66.6 120.5,98.6 161.8,111.3 356.2,179.6 549.1,179.6 195.5,0 390.1,-64.5 548,-170.2 67.2,-45 123.4,-95 161.9,-142.7 32.3,-40 48.3,-74 48.3,-90.8 0,-16.8 -16,-50.7 -48.2,-90.8 -38.5,-47.7 -94.7,-97.7 -161.9,-142.7 -157.9,-105.7 -352.5,-170.3 -548,-170.3zM872.8,262a254.9,254.9 0,1 1,0 509.9,254.9 254.9,0 0,1 0,-509.9zM872.8,340.8a176.2,176.2 0,1 0,0 352.4,176.2 176.2,0 0,0 0,-352.4z"
|
||||
android:fillColor="#bfbfbf"/>
|
||||
android:fillColor="#666666"/>
|
||||
</vector>
|
||||
|
||||
293
app/src/main/res/layout/dialog_list_more.xml
Normal file
293
app/src/main/res/layout/dialog_list_more.xml
Normal file
@ -0,0 +1,293 @@
|
||||
<?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="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center">
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="4dp"
|
||||
android:background="@drawable/dr_dialog_indicator_bg" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/dr_item_img_frame">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/tvFileImg"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@mipmap/ic_launcher_round" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/lock_layout"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/dr_item_lock_bg"
|
||||
android:gravity="center"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/lock" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFileName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:fontFamily="@font/poppins_medium"
|
||||
android:maxLines="1"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFileDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/black_60"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvFileSize"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/black_60"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/collectBtn"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/collectIv"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/collect" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/renameFileBtn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/rename_text" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:text="@string/rename_file"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/prompt" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:text="@string/file_details"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/share" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:text="@string/share_file"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/print" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:text="@string/print_pdf"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@color/line_color" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/duplicate" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:fontFamily="@font/poppins_medium"
|
||||
android:text="@string/duplicate_file"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/lock" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:fontFamily="@font/poppins_medium"
|
||||
android:text="@string/set_password"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/file_delete" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:fontFamily="@font/poppins_medium"
|
||||
android:text="@string/delete_file"
|
||||
android:textColor="@color/grey_text_color"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@ -38,7 +38,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/enter_password"
|
||||
app:endIconMode="password_toggle"
|
||||
app:passwordToggleEnabled="true">
|
||||
app:endIconDrawable="@drawable/dr_password_state">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etPassword"
|
||||
@ -50,21 +50,6 @@
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/showPasswordBtn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignEnd="@id/tilPassword"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/showPasswordIv"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/hide_password" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/enter_password"
|
||||
app:endIconDrawable="@drawable/dr_password_state"
|
||||
app:endIconMode="password_toggle"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
@ -50,22 +51,6 @@
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/enterShowPasswordBtn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignEnd="@id/tilPassword"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/enterShowPasswordIv"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/hide_password" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
@ -78,6 +63,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/confirm_password"
|
||||
app:endIconDrawable="@drawable/dr_password_state"
|
||||
app:endIconMode="password_toggle"
|
||||
app:passwordToggleEnabled="true">
|
||||
|
||||
@ -91,22 +77,6 @@
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/confirmShowPasswordBtn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignEnd="@id/tilConfirmPassword"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/confirmShowPasswordIv"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/hide_password" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
72
app/src/main/res/layout/dialog_rename_file.xml
Normal file
72
app/src/main/res/layout/dialog_rename_file.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/dr_rounded_corner_12_bg_white"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:fontFamily="@font/poppins_semibold"
|
||||
android:text="@string/rename_file"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/enter_name"
|
||||
app:endIconDrawable="@drawable/delete"
|
||||
app:endIconMode="clear_text">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="@font/poppins_regular"
|
||||
android:padding="12dp"
|
||||
android:text="@string/cancel"
|
||||
android:textColor="@color/black_80" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvConfirm"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/poppins_medium"
|
||||
android:padding="12dp"
|
||||
android:text="@string/ok"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@ -1,9 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/dr_rounded_corner_12_bg_white"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
|
||||
@ -13,4 +13,5 @@
|
||||
<color name="bg_color">#F6F6F6</color>
|
||||
<color name="line_color">#E0E0E0</color>
|
||||
<color name="black_img_color">#2c2c2c</color>
|
||||
<color name="grey_text_color">#666666</color>
|
||||
</resources>
|
||||
@ -39,4 +39,21 @@
|
||||
<string name="pdf_loading_failed">PDF loading failed</string>
|
||||
<string name="password_too_short">Password must be at least 4 characters</string>
|
||||
<string name="password_not_match">Passwords do not match</string>
|
||||
<string name="rename_file">Rename</string>
|
||||
<string name="file_details">Details</string>
|
||||
<string name="share_file">Share</string>
|
||||
<string name="print_pdf">Print</string>
|
||||
<string name="added_to_favorites">Added to Favorite</string>
|
||||
<string name="removed_from_favorites">Removed from Favorites</string>
|
||||
<string name="delete_file">Delete File</string>
|
||||
<string name="set_password">Set Password</string>
|
||||
<string name="remove_password">Remove Password</string>
|
||||
<string name="duplicate_file">Duplicate File</string>
|
||||
<string name="enter_name">Enter a name</string>
|
||||
<string name="name_not_empty">File name cannot be empty</string>
|
||||
<string name="name_not_changed">File name has not been changed</string>
|
||||
<string name="name_invalid_chars">File name contains invalid characters: / \ : * ? " < > |</string>
|
||||
<string name="name_too_long">File name is too long (max 255 characters)</string>
|
||||
<string name="name_already_exists">A file with the same name already exists</string>
|
||||
<string name="name_start_end_space">File name cannot start or end with space</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user