优化搜索界面,添加工具加密等操作。
This commit is contained in:
parent
6ffb08dc4f
commit
5bf2952c03
@ -74,7 +74,7 @@
|
|||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.act.SplitPdfResultActivity"
|
android:name=".ui.act.PdfResultActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:screenOrientation="portrait" />
|
android:screenOrientation="portrait" />
|
||||||
|
|||||||
@ -4,7 +4,8 @@ enum class PdfPickerSource {
|
|||||||
NONE, // 没有任何
|
NONE, // 没有任何
|
||||||
MERGE, // PDF合并
|
MERGE, // PDF合并
|
||||||
SPLIT, // PDF拆分
|
SPLIT, // PDF拆分
|
||||||
LOCK, // PDF加密/解密
|
LOCK, // PDF加密
|
||||||
|
UNLOCK, // PDF解密
|
||||||
TO_IMAGES, // PDF转图片
|
TO_IMAGES, // PDF转图片
|
||||||
TO_LONG_IMAGE // PDF转长图
|
TO_LONG_IMAGE // PDF转长图
|
||||||
}
|
}
|
||||||
@ -3,5 +3,6 @@ package com.all.pdfreader.pro.app.model
|
|||||||
data class PdfSplitResultItem(
|
data class PdfSplitResultItem(
|
||||||
val filePath: String,
|
val filePath: String,
|
||||||
val thumbnailPath: String? = null,
|
val thumbnailPath: String? = null,
|
||||||
var isSelected: Boolean
|
var isSelected: Boolean = false,
|
||||||
|
var isPassword: Boolean = false
|
||||||
)
|
)
|
||||||
@ -62,7 +62,7 @@ class MergePdfActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
binding.continueNowBtn.setOnSingleClickListener {
|
binding.continueNowBtn.setOnSingleClickListener {
|
||||||
val list = selectedList.map { it.filePath }
|
val list = selectedList.map { it.filePath }
|
||||||
val intent = SplitPdfResultActivity.createIntentInputFile(
|
val intent = PdfResultActivity.createIntentInputFile(
|
||||||
this, ArrayList(list), PdfPickerSource.MERGE
|
this, ArrayList(list), PdfPickerSource.MERGE
|
||||||
)
|
)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
|||||||
@ -15,6 +15,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.adapter.PdfAdapter
|
import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter
|
||||||
|
import com.all.pdfreader.pro.app.ui.dialog.PdfSetPasswordDialog
|
||||||
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
|
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
|
||||||
import com.all.pdfreader.pro.app.util.AppUtils.setOnSingleClickListener
|
import com.all.pdfreader.pro.app.util.AppUtils.setOnSingleClickListener
|
||||||
import com.gyf.immersionbar.ImmersionBar
|
import com.gyf.immersionbar.ImmersionBar
|
||||||
@ -80,7 +81,21 @@ class PdfPickerActivity : BaseActivity() {
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
PdfPickerSource.LOCK -> {}
|
PdfPickerSource.LOCK -> {
|
||||||
|
PdfSetPasswordDialog(onOkClick = { password ->
|
||||||
|
val intent = PdfResultActivity.createIntentLock(
|
||||||
|
this, pdf.filePath, password,
|
||||||
|
PdfPickerSource.LOCK
|
||||||
|
)
|
||||||
|
startActivity(intent)
|
||||||
|
finish()
|
||||||
|
}).show(supportFragmentManager, "PdfSetPasswordDialog")
|
||||||
|
}
|
||||||
|
|
||||||
|
PdfPickerSource.UNLOCK -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
PdfPickerSource.TO_IMAGES -> {}
|
PdfPickerSource.TO_IMAGES -> {}
|
||||||
PdfPickerSource.TO_LONG_IMAGE -> {}
|
PdfPickerSource.TO_LONG_IMAGE -> {}
|
||||||
PdfPickerSource.NONE -> {}
|
PdfPickerSource.NONE -> {}
|
||||||
@ -109,17 +124,25 @@ class PdfPickerActivity : BaseActivity() {
|
|||||||
private fun initData() {
|
private fun initData() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
PdfRepository.getInstance().getAllDocuments().collect { list ->
|
PdfRepository.getInstance().getAllDocuments().collect { list ->
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
binding.noFilesLayout.visibility = View.VISIBLE
|
||||||
|
return@collect
|
||||||
|
}
|
||||||
val sortedList = sortDocuments(list)
|
val sortedList = sortDocuments(list)
|
||||||
// 标记已选择项
|
// 标记已选择项
|
||||||
if (fromActivityResult == MergePdfActivity.TAG) {
|
if (fromActivityResult == MergePdfActivity.TAG) {
|
||||||
markSelectedItems(sortedList, historyList)
|
markSelectedItems(sortedList, historyList)
|
||||||
}
|
}
|
||||||
if (list.isNotEmpty()) {
|
// 再根据来源过滤
|
||||||
adapter.updateData(sortedList)
|
val displayList = when (source) {
|
||||||
binding.noFilesLayout.visibility = View.GONE
|
PdfPickerSource.LOCK -> sortedList.filter { !it.isPassword }//只展示没有被加密的
|
||||||
} else {
|
PdfPickerSource.UNLOCK -> sortedList.filter { it.isPassword }//只展示已经被加密的
|
||||||
binding.noFilesLayout.visibility = View.VISIBLE
|
else -> sortedList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adapter.updateData(displayList)
|
||||||
|
binding.noFilesLayout.visibility =
|
||||||
|
if (displayList.isEmpty()) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package com.all.pdfreader.pro.app.ui.act
|
package com.all.pdfreader.pro.app.ui.act
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@ -16,11 +15,13 @@ import com.all.pdfreader.pro.app.databinding.ActivityPdfSplitResultBinding
|
|||||||
import com.all.pdfreader.pro.app.model.PdfPickerSource
|
import com.all.pdfreader.pro.app.model.PdfPickerSource
|
||||||
import com.all.pdfreader.pro.app.model.PdfSelectedPagesItem
|
import com.all.pdfreader.pro.app.model.PdfSelectedPagesItem
|
||||||
import com.all.pdfreader.pro.app.model.PdfSplitResultItem
|
import com.all.pdfreader.pro.app.model.PdfSplitResultItem
|
||||||
import com.all.pdfreader.pro.app.ui.adapter.SplitPdfResultAdapter
|
import com.all.pdfreader.pro.app.room.repository.PdfRepository
|
||||||
|
import com.all.pdfreader.pro.app.ui.adapter.PdfResultAdapter
|
||||||
import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment
|
import com.all.pdfreader.pro.app.ui.dialog.PromptDialogFragment
|
||||||
import com.all.pdfreader.pro.app.util.AppUtils
|
import com.all.pdfreader.pro.app.util.AppUtils
|
||||||
import com.all.pdfreader.pro.app.util.FileUtils.toUnderscoreDateTime
|
import com.all.pdfreader.pro.app.util.FileUtils.toUnderscoreDateTime
|
||||||
import com.all.pdfreader.pro.app.util.PdfScanner
|
import com.all.pdfreader.pro.app.util.PdfScanner
|
||||||
|
import com.all.pdfreader.pro.app.util.PdfSecurityUtils
|
||||||
import com.all.pdfreader.pro.app.util.PdfUtils
|
import com.all.pdfreader.pro.app.util.PdfUtils
|
||||||
import com.gyf.immersionbar.ImmersionBar
|
import com.gyf.immersionbar.ImmersionBar
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -29,18 +30,20 @@ import kotlinx.coroutines.withContext
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
class SplitPdfResultActivity : BaseActivity() {
|
class PdfResultActivity : BaseActivity() {
|
||||||
override val TAG: String = "SplitPdfResultActivity"
|
override val TAG: String = "PdfResultActivity"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val EXTRA_SELECTED_LIST = "extra_selected_list"
|
private const val EXTRA_SELECTED_LIST = "extra_selected_list"
|
||||||
private const val EXTRA_FILE_LIST = "extra_file_list"
|
private const val EXTRA_FILE_LIST = "extra_file_list"
|
||||||
private const val EXTRA_SOURCE = "extra_source"
|
private const val EXTRA_SOURCE = "extra_source"
|
||||||
|
private const val EXTRA_PASSWORD = "extra_password"
|
||||||
|
private const val EXTRA_FILE_PATH = "extra_file_path"
|
||||||
|
|
||||||
fun createIntentPdfSelectedPagesItem(
|
fun createIntentPdfSelectedPagesItem(
|
||||||
context: Context, list: ArrayList<PdfSelectedPagesItem>, source: PdfPickerSource
|
context: Context, list: ArrayList<PdfSelectedPagesItem>, source: PdfPickerSource
|
||||||
): Intent {
|
): Intent {
|
||||||
return Intent(context, SplitPdfResultActivity::class.java).apply {
|
return Intent(context, PdfResultActivity::class.java).apply {
|
||||||
putParcelableArrayListExtra(EXTRA_SELECTED_LIST, list)
|
putParcelableArrayListExtra(EXTRA_SELECTED_LIST, list)
|
||||||
putExtra(EXTRA_SOURCE, source)
|
putExtra(EXTRA_SOURCE, source)
|
||||||
}
|
}
|
||||||
@ -49,23 +52,35 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
fun createIntentInputFile(
|
fun createIntentInputFile(
|
||||||
context: Context, list: ArrayList<String>, source: PdfPickerSource
|
context: Context, list: ArrayList<String>, source: PdfPickerSource
|
||||||
): Intent {
|
): Intent {
|
||||||
return Intent(context, SplitPdfResultActivity::class.java).apply {
|
return Intent(context, PdfResultActivity::class.java).apply {
|
||||||
putStringArrayListExtra(EXTRA_FILE_LIST, list)
|
putStringArrayListExtra(EXTRA_FILE_LIST, list)
|
||||||
putExtra(EXTRA_SOURCE, source)
|
putExtra(EXTRA_SOURCE, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createIntentLock(
|
||||||
|
context: Context, filepath: String, password: String, source: PdfPickerSource
|
||||||
|
): Intent {
|
||||||
|
return Intent(context, PdfResultActivity::class.java).apply {
|
||||||
|
putExtra(EXTRA_FILE_PATH, filepath)
|
||||||
|
putExtra(EXTRA_PASSWORD, password)
|
||||||
|
putExtra(EXTRA_SOURCE, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var binding: ActivityPdfSplitResultBinding
|
private lateinit var binding: ActivityPdfSplitResultBinding
|
||||||
private lateinit var adapter: SplitPdfResultAdapter
|
private lateinit var adapter: PdfResultAdapter
|
||||||
private var resultList: MutableList<PdfSplitResultItem> = mutableListOf()
|
private var resultList: MutableList<PdfSplitResultItem> = mutableListOf()
|
||||||
private lateinit var selectedList: ArrayList<PdfSelectedPagesItem>
|
private lateinit var selectedList: ArrayList<PdfSelectedPagesItem>
|
||||||
private lateinit var inputFile: ArrayList<String>
|
private lateinit var inputFile: ArrayList<String>
|
||||||
private lateinit var source: PdfPickerSource
|
private lateinit var source: PdfPickerSource
|
||||||
private var isSplitting = false
|
private var isProcessing = false
|
||||||
private var exitDialog: PromptDialogFragment? = null
|
private var exitDialog: PromptDialogFragment? = null
|
||||||
private val pdfRepository = getRepository()
|
private val pdfRepository = getRepository()
|
||||||
private lateinit var pdfScanner: PdfScanner
|
private lateinit var pdfScanner: PdfScanner
|
||||||
|
private lateinit var filepath: String
|
||||||
|
private lateinit var password: String
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -76,6 +91,8 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
.navigationBarColor(R.color.bg_color).init()
|
.navigationBarColor(R.color.bg_color).init()
|
||||||
selectedList = requireParcelableArrayList(EXTRA_SELECTED_LIST)
|
selectedList = requireParcelableArrayList(EXTRA_SELECTED_LIST)
|
||||||
inputFile = requireStringArrayList(EXTRA_FILE_LIST)
|
inputFile = requireStringArrayList(EXTRA_FILE_LIST)
|
||||||
|
filepath = intent.getStringExtra(EXTRA_FILE_PATH) ?: ""
|
||||||
|
password = intent.getStringExtra(EXTRA_PASSWORD) ?: ""
|
||||||
source = getSerializableOrDefault(EXTRA_SOURCE, PdfPickerSource.NONE)
|
source = getSerializableOrDefault(EXTRA_SOURCE, PdfPickerSource.NONE)
|
||||||
if (source == PdfPickerSource.NONE) {
|
if (source == PdfPickerSource.NONE) {
|
||||||
showToast(getString(R.string.pdf_loading_failed))
|
showToast(getString(R.string.pdf_loading_failed))
|
||||||
@ -94,6 +111,12 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
finish()
|
finish()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else if (source == PdfPickerSource.LOCK) {
|
||||||
|
if (filepath.isEmpty() || password.isEmpty()) {
|
||||||
|
showToast(getString(R.string.pdf_loading_failed))
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pdfScanner = PdfScanner(this, pdfRepository)
|
pdfScanner = PdfScanner(this, pdfRepository)
|
||||||
@ -103,7 +126,7 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
adapter = SplitPdfResultAdapter(resultList)
|
adapter = PdfResultAdapter(resultList)
|
||||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
}
|
}
|
||||||
@ -111,7 +134,7 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
private fun initData() {
|
private fun initData() {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
isSplitting = true
|
isProcessing = true
|
||||||
binding.processingLayout.visibility = View.VISIBLE
|
binding.processingLayout.visibility = View.VISIBLE
|
||||||
binding.progressBar.isIndeterminate = false
|
binding.progressBar.isIndeterminate = false
|
||||||
binding.progressBar.progress = 0
|
binding.progressBar.progress = 0
|
||||||
@ -143,7 +166,7 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
})?.let { resultFile ->
|
})?.let { resultFile ->
|
||||||
val thumbnails =
|
val thumbnails =
|
||||||
AppUtils.generateFastThumbnail(this@SplitPdfResultActivity, resultFile)
|
AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile)
|
||||||
val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false)
|
val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false)
|
||||||
pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) {
|
pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) {
|
||||||
resultList.add(result)
|
resultList.add(result)
|
||||||
@ -172,13 +195,27 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
})?.let { resultFile ->
|
})?.let { resultFile ->
|
||||||
val thumbnails =
|
val thumbnails =
|
||||||
AppUtils.generateFastThumbnail(this@SplitPdfResultActivity, resultFile)
|
AppUtils.generateFastThumbnail(this@PdfResultActivity, resultFile)
|
||||||
val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false)
|
val result = PdfSplitResultItem(resultFile.absolutePath, thumbnails, false)
|
||||||
pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) {
|
pdfScanner.addNewPdfToDatabase(result.filePath, result.thumbnailPath) {
|
||||||
resultList.add(result)
|
resultList.add(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (source == PdfPickerSource.LOCK) {
|
||||||
|
binding.congratulationsDesc.text = getString(R.string.set_password_successfully)
|
||||||
|
PdfSecurityUtils.setPasswordToPdfWithProgress(
|
||||||
|
filepath, password, password
|
||||||
|
) { progress ->
|
||||||
|
binding.progressBar.progress = progress
|
||||||
|
binding.progressTv.text = "$progress"
|
||||||
|
}.let { it ->
|
||||||
|
if (it) {
|
||||||
|
PdfRepository.getInstance().updateIsPassword(filepath, true)
|
||||||
|
}
|
||||||
|
val result = PdfSplitResultItem(filePath = filepath, isPassword = true)
|
||||||
|
resultList.add(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
binding.processingLayout.visibility = View.GONE
|
binding.processingLayout.visibility = View.GONE
|
||||||
@ -187,7 +224,7 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
resultList[0].isSelected = true
|
resultList[0].isSelected = true
|
||||||
}
|
}
|
||||||
adapter.updateAdapter()
|
adapter.updateAdapter()
|
||||||
isSplitting = false//拆分结束
|
isProcessing = false//拆分结束
|
||||||
exitDialog?.dismissAllowingStateLoss()
|
exitDialog?.dismissAllowingStateLoss()
|
||||||
exitDialog = null
|
exitDialog = null
|
||||||
}
|
}
|
||||||
@ -216,12 +253,15 @@ class SplitPdfResultActivity : BaseActivity() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
isProcessing = false
|
||||||
|
exitDialog?.dismissAllowingStateLoss()
|
||||||
|
exitDialog = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupBackPressedCallback() {
|
private fun setupBackPressedCallback() {
|
||||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
if (isSplitting) {
|
if (isProcessing) {
|
||||||
exitDialog = PromptDialogFragment(
|
exitDialog = PromptDialogFragment(
|
||||||
getString(R.string.exit_split),
|
getString(R.string.exit_split),
|
||||||
getString(R.string.confirm_discard_changes),
|
getString(R.string.confirm_discard_changes),
|
||||||
@ -25,6 +25,7 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
|
import kotlin.math.log
|
||||||
|
|
||||||
class SearchActivity : BaseActivity() {
|
class SearchActivity : BaseActivity() {
|
||||||
override val TAG: String = "SearchActivity"
|
override val TAG: String = "SearchActivity"
|
||||||
@ -87,8 +88,9 @@ class SearchActivity : BaseActivity() {
|
|||||||
delay(150)
|
delay(150)
|
||||||
pdfRepository.searchDocuments(query).collectLatest { list ->
|
pdfRepository.searchDocuments(query).collectLatest { list ->
|
||||||
if (list.isNotEmpty()) {
|
if (list.isNotEmpty()) {
|
||||||
adapter.updateData(list)
|
adapter.updateData(list) {
|
||||||
adapter.highlightItems(query.trim())
|
adapter.highlightItems(query.trim())
|
||||||
|
}
|
||||||
binding.noFilesLayout.visibility = View.GONE
|
binding.noFilesLayout.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
adapter.updateData(emptyList())
|
adapter.updateData(emptyList())
|
||||||
|
|||||||
@ -5,9 +5,7 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.all.pdfreader.pro.app.R
|
import com.all.pdfreader.pro.app.R
|
||||||
@ -140,7 +138,7 @@ class SplitPdfActivity : BaseActivity() {
|
|||||||
binding.splitBtn.setOnSingleClickListener {
|
binding.splitBtn.setOnSingleClickListener {
|
||||||
logDebug("${selectedList.size}")
|
logDebug("${selectedList.size}")
|
||||||
//因为图片做的路径缓存方式,所以这里直接传入整个集合到result页处理
|
//因为图片做的路径缓存方式,所以这里直接传入整个集合到result页处理
|
||||||
val intent = SplitPdfResultActivity.createIntentPdfSelectedPagesItem(
|
val intent = PdfResultActivity.createIntentPdfSelectedPagesItem(
|
||||||
this,
|
this,
|
||||||
ArrayList(selectedList),
|
ArrayList(selectedList),
|
||||||
PdfPickerSource.SPLIT
|
PdfPickerSource.SPLIT
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.all.pdfreader.pro.app.R
|
import com.all.pdfreader.pro.app.R
|
||||||
import com.all.pdfreader.pro.app.databinding.AdapterSplitSelectedResultItemBinding
|
import com.all.pdfreader.pro.app.databinding.AdapterSelectedResultItemBinding
|
||||||
import com.all.pdfreader.pro.app.model.PdfSplitResultItem
|
import com.all.pdfreader.pro.app.model.PdfSplitResultItem
|
||||||
import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
|
import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
@ -14,15 +14,15 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
|||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class SplitPdfResultAdapter(
|
class PdfResultAdapter(
|
||||||
private val list: MutableList<PdfSplitResultItem>
|
private val list: MutableList<PdfSplitResultItem>
|
||||||
) : RecyclerView.Adapter<SplitPdfResultAdapter.PdfViewHolder>() {
|
) : RecyclerView.Adapter<PdfResultAdapter.PdfViewHolder>() {
|
||||||
|
|
||||||
inner class PdfViewHolder(val binding: AdapterSplitSelectedResultItemBinding) :
|
inner class PdfViewHolder(val binding: AdapterSelectedResultItemBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root)
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PdfViewHolder(
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PdfViewHolder(
|
||||||
AdapterSplitSelectedResultItemBinding.inflate(
|
AdapterSelectedResultItemBinding.inflate(
|
||||||
LayoutInflater.from(parent.context), parent, false
|
LayoutInflater.from(parent.context), parent, false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -35,6 +35,11 @@ class SplitPdfResultAdapter(
|
|||||||
.transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context)))
|
.transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context)))
|
||||||
.into(holder.binding.image)
|
.into(holder.binding.image)
|
||||||
}
|
}
|
||||||
|
if (item.isPassword) {
|
||||||
|
holder.binding.lockLayout.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
holder.binding.lockLayout.visibility = View.GONE
|
||||||
|
}
|
||||||
holder.binding.nameTv.text = File(item.filePath).name
|
holder.binding.nameTv.text = File(item.filePath).name
|
||||||
holder.binding.pathTv.text =
|
holder.binding.pathTv.text =
|
||||||
holder.binding.root.context.getString(R.string.path_details, item.filePath)
|
holder.binding.root.context.getString(R.string.path_details, item.filePath)
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package com.all.pdfreader.pro.app.ui.adapter
|
package com.all.pdfreader.pro.app.ui.adapter
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -24,7 +25,7 @@ class SearchPdfAdapter(
|
|||||||
private var showMoreButton: Boolean = true, // 是否显示更多按钮
|
private var showMoreButton: Boolean = true, // 是否显示更多按钮
|
||||||
private var showCheckButton: Boolean = false // 是否显示选择框按钮
|
private var showCheckButton: Boolean = false // 是否显示选择框按钮
|
||||||
) : ListAdapter<PdfDocumentEntity, SearchPdfAdapter.PdfViewHolder>(PdfDiffCallback()) {
|
) : ListAdapter<PdfDocumentEntity, SearchPdfAdapter.PdfViewHolder>(PdfDiffCallback()) {
|
||||||
|
private var searchKeyword: String? = null // 保存当前搜索关键字
|
||||||
inner class PdfViewHolder(val binding: AdapterPdfItemBinding) :
|
inner class PdfViewHolder(val binding: AdapterPdfItemBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root)
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
@ -54,12 +55,13 @@ class SearchPdfAdapter(
|
|||||||
|
|
||||||
holder.binding.checkBtn.visibility = if (showCheckButton) View.VISIBLE else View.GONE
|
holder.binding.checkBtn.visibility = if (showCheckButton) View.VISIBLE else View.GONE
|
||||||
holder.binding.moreBtn.visibility = if (showMoreButton) View.VISIBLE else View.GONE
|
holder.binding.moreBtn.visibility = if (showMoreButton) View.VISIBLE else View.GONE
|
||||||
|
holder.binding.deleteBtn.visibility = View.GONE
|
||||||
|
|
||||||
// 文件名高亮,如果 highlightKeyword 为 null 或空字符串,就显示普通文本
|
val keyword = highlightKeyword ?: searchKeyword
|
||||||
holder.binding.tvFileName.text = if (highlightKeyword.isNullOrBlank()) {
|
holder.binding.tvFileName.text = if (keyword.isNullOrBlank()) {
|
||||||
item.fileName
|
item.fileName
|
||||||
} else {
|
} else {
|
||||||
item.fileName.toHighlightedSpannable(highlightKeyword, holder.binding.root.context.getColor(R.color.icon_sel_on_color))
|
item.fileName.toHighlightedSpannable(keyword, holder.binding.root.context.getColor(R.color.icon_sel_on_color))
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.binding.tvFileSize.text = item.fileSize.toFormatFileSize()
|
holder.binding.tvFileSize.text = item.fileSize.toFormatFileSize()
|
||||||
@ -85,14 +87,17 @@ class SearchPdfAdapter(
|
|||||||
holder.binding.moreBtn.setOnClickListener { onMoreClick(item) }
|
holder.binding.moreBtn.setOnClickListener { onMoreClick(item) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateData(newList: List<PdfDocumentEntity>) {
|
fun updateData(newList: List<PdfDocumentEntity>, onComplete: (() -> Unit)? = null) {
|
||||||
submitList(newList.toList())
|
submitList(newList.toList()) {
|
||||||
|
onComplete?.invoke()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 搜索场景使用:只刷新高亮,不刷新整个列表
|
* 搜索场景使用:只刷新高亮,不刷新整个列表
|
||||||
*/
|
*/
|
||||||
fun highlightItems(keyword: String) {
|
fun highlightItems(keyword: String) {
|
||||||
|
searchKeyword = keyword
|
||||||
for (i in 0 until itemCount) {
|
for (i in 0 until itemCount) {
|
||||||
notifyItemChanged(i, keyword)
|
notifyItemChanged(i, keyword)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@ class FileDetailsDialogFragment() : BottomSheetDialogFragment() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
dialog?.window?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
|
dialog?.window?.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
|
||||||
?.setBackgroundResource(R.drawable.dr_rounded_corner_12_bg_white)
|
?.setBackgroundResource(R.drawable.dr_rc_top_12_bg_white)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@ -197,7 +197,9 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment()
|
|||||||
if (pdfDocument.isPassword) {
|
if (pdfDocument.isPassword) {
|
||||||
PdfRemovePasswordDialog().show(parentFragmentManager, "PdfRemovePasswordDialog")
|
PdfRemovePasswordDialog().show(parentFragmentManager, "PdfRemovePasswordDialog")
|
||||||
} else {
|
} else {
|
||||||
PdfSetPasswordDialog().show(parentFragmentManager, "PdfSetPasswordDialog")
|
PdfSetPasswordDialog(onOkClick = { password ->
|
||||||
|
viewModel.setPassword(pdfDocument.filePath, password)
|
||||||
|
}).show(parentFragmentManager, "PdfSetPasswordDialog")
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,24 +7,17 @@ import android.text.TextWatcher
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import com.all.pdfreader.pro.app.R
|
import com.all.pdfreader.pro.app.R
|
||||||
import com.all.pdfreader.pro.app.databinding.DialogPdfSetPasswordBinding
|
import com.all.pdfreader.pro.app.databinding.DialogPdfSetPasswordBinding
|
||||||
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.AppUtils.showKeyboard
|
||||||
import com.all.pdfreader.pro.app.util.ToastUtils
|
import com.all.pdfreader.pro.app.util.ToastUtils
|
||||||
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
|
|
||||||
import kotlin.getValue
|
|
||||||
|
|
||||||
class PdfSetPasswordDialog() : DialogFragment(
|
class PdfSetPasswordDialog(
|
||||||
|
private val onOkClick: (password: String) -> Unit = {}
|
||||||
) {
|
) : DialogFragment() {
|
||||||
private lateinit var binding: DialogPdfSetPasswordBinding
|
private lateinit var binding: DialogPdfSetPasswordBinding
|
||||||
private val viewModel: PdfViewModel by activityViewModels()
|
|
||||||
private lateinit var pdfDocument: PdfDocumentEntity
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
@ -48,15 +41,9 @@ class PdfSetPasswordDialog() : DialogFragment(
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
viewModel.pdfDocument.value?.let {
|
binding.etPassword.showKeyboard()
|
||||||
pdfDocument = it
|
setupListeners()
|
||||||
binding.etPassword.showKeyboard()
|
setupTextWatchers()
|
||||||
setupListeners()
|
|
||||||
setupTextWatchers()
|
|
||||||
} ?: run {
|
|
||||||
showToast(getString(R.string.file_not))
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupListeners() {
|
private fun setupListeners() {
|
||||||
@ -68,7 +55,7 @@ class PdfSetPasswordDialog() : DialogFragment(
|
|||||||
val password = binding.etPassword.text.toString()
|
val password = binding.etPassword.text.toString()
|
||||||
val confirmPassword = binding.etConfirmPassword.text.toString()
|
val confirmPassword = binding.etConfirmPassword.text.toString()
|
||||||
if (validatePassword(password, confirmPassword)) {
|
if (validatePassword(password, confirmPassword)) {
|
||||||
viewModel.setPassword(pdfDocument.filePath, password)
|
onOkClick(password)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,5 +33,9 @@ class ToolsFrag : Fragment() {
|
|||||||
val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.SPLIT)
|
val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.SPLIT)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
binding.lockBtn.setOnClickListener {
|
||||||
|
val intent = PdfPickerActivity.createIntent(requireActivity(), PdfPickerSource.LOCK)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,23 +1,27 @@
|
|||||||
package com.all.pdfreader.pro.app.util
|
package com.all.pdfreader.pro.app.util
|
||||||
|
|
||||||
|
import android.graphics.pdf.PdfDocument
|
||||||
import com.tom_roush.pdfbox.pdmodel.PDDocument
|
import com.tom_roush.pdfbox.pdmodel.PDDocument
|
||||||
import com.tom_roush.pdfbox.pdmodel.encryption.AccessPermission
|
import com.tom_roush.pdfbox.pdmodel.encryption.AccessPermission
|
||||||
import com.tom_roush.pdfbox.pdmodel.encryption.StandardProtectionPolicy
|
import com.tom_roush.pdfbox.pdmodel.encryption.StandardProtectionPolicy
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object PdfSecurityUtils {
|
object PdfSecurityUtils {
|
||||||
|
|
||||||
fun setPasswordToPdf(
|
fun setPasswordToPdf(
|
||||||
inputFilePath: String,
|
inputFilePath: String, userPassword: String, ownerPassword: String
|
||||||
userPassword: String,
|
|
||||||
ownerPassword: String
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return try {
|
return try {
|
||||||
PDDocument.load(File(inputFilePath)).use { document ->
|
PDDocument.load(File(inputFilePath)).use { document ->
|
||||||
val accessPermission = AccessPermission()
|
val accessPermission = AccessPermission()
|
||||||
val protectionPolicy =
|
val protectionPolicy =
|
||||||
StandardProtectionPolicy(ownerPassword, userPassword, accessPermission)
|
StandardProtectionPolicy(ownerPassword, userPassword, accessPermission)
|
||||||
protectionPolicy.encryptionKeyLength = 256
|
protectionPolicy.encryptionKeyLength = 128
|
||||||
protectionPolicy.permissions = accessPermission
|
protectionPolicy.permissions = accessPermission
|
||||||
document.protect(protectionPolicy)
|
document.protect(protectionPolicy)
|
||||||
document.save(inputFilePath)
|
document.save(inputFilePath)
|
||||||
@ -29,6 +33,44 @@ object PdfSecurityUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun setPasswordToPdfWithProgress(
|
||||||
|
inputFilePath: String,
|
||||||
|
userPassword: String,
|
||||||
|
ownerPassword: String,
|
||||||
|
onProgress: (Int) -> Unit
|
||||||
|
): Boolean = withContext(Dispatchers.IO) {
|
||||||
|
var success = false
|
||||||
|
// 假进度变量
|
||||||
|
var progress = 0
|
||||||
|
val job = launch {
|
||||||
|
while (isActive && progress < 98) {
|
||||||
|
delay(50)
|
||||||
|
// 专业曲线:前 80%慢,后 20%快
|
||||||
|
progress += when {
|
||||||
|
progress < 80 -> 1 // 前 80% 每次 +1
|
||||||
|
progress < 90 -> 2 // 80~90% 每次 +2
|
||||||
|
else -> 3 // 90~98% 每次 +3
|
||||||
|
}
|
||||||
|
if (progress > 98) progress = 98
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
onProgress(progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success = setPasswordToPdf(inputFilePath, userPassword, ownerPassword)
|
||||||
|
|
||||||
|
// 停止假进度
|
||||||
|
job.cancel()
|
||||||
|
|
||||||
|
// 最终完成直接 100%
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
onProgress(100)
|
||||||
|
}
|
||||||
|
success
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun removePasswordFromPdf(inputFilePath: String, password: String): Boolean {
|
fun removePasswordFromPdf(inputFilePath: String, password: String): Boolean {
|
||||||
return try {
|
return try {
|
||||||
PDDocument.load(File(inputFilePath), password).use { document ->
|
PDDocument.load(File(inputFilePath), password).use { document ->
|
||||||
|
|||||||
@ -109,6 +109,7 @@
|
|||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
android:id="@+id/congratulationsDesc"
|
||||||
style="@style/TextViewFont_PopMedium"
|
style="@style/TextViewFont_PopMedium"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -134,7 +135,7 @@
|
|||||||
android:layout_height="150dp"
|
android:layout_height="150dp"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
tools:itemCount="3"
|
tools:itemCount="3"
|
||||||
tools:listitem="@layout/adapter_split_selected_result_item" />
|
tools:listitem="@layout/adapter_selected_result_item" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@ -49,16 +49,6 @@
|
|||||||
android:layout_margin="0.5dp" />
|
android:layout_margin="0.5dp" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/collectState"
|
|
||||||
android:layout_width="16dp"
|
|
||||||
android:layout_height="16dp"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:background="@drawable/dr_collect_state_bg"
|
|
||||||
android:padding="2dp"
|
|
||||||
android:src="@drawable/collected_white" />
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/lock_layout"
|
android:id="@+id/lock_layout"
|
||||||
android:layout_width="56dp"
|
android:layout_width="56dp"
|
||||||
@ -73,6 +63,15 @@
|
|||||||
android:src="@drawable/lock" />
|
android:src="@drawable/lock" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/collectState"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:background="@drawable/dr_collect_state_bg"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:src="@drawable/collected_white" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@ -37,6 +37,21 @@
|
|||||||
android:layout_width="56dp"
|
android:layout_width="56dp"
|
||||||
android:layout_height="56dp" />
|
android:layout_height="56dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/lock_layout"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
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>
|
</RelativeLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@ -22,4 +22,17 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/split_pdf" />
|
android:text="@string/split_pdf" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/lockBtn"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/lock_pdf" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/unLockBtn"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/unlock_pdf" />
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@ -29,7 +29,9 @@
|
|||||||
<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>
|
<string name="file_not">File does not exist</string>
|
||||||
<string name="lock">Lock</string>
|
<string name="lock">Lock</string>
|
||||||
|
<string name="lock_pdf">Lock PDF</string>
|
||||||
<string name="unlock">Unlock</string>
|
<string name="unlock">Unlock</string>
|
||||||
|
<string name="unlock_pdf">Unlock PDF</string>
|
||||||
<string name="eye_protect">Eye Protect</string>
|
<string name="eye_protect">Eye Protect</string>
|
||||||
<string name="bookmarks">Bookmarks</string>
|
<string name="bookmarks">Bookmarks</string>
|
||||||
<string name="no_bookmarks_page">No Bookmarks Page</string>
|
<string name="no_bookmarks_page">No Bookmarks Page</string>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user