diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfTextSearchTestActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfTextSearchTestActivity.kt deleted file mode 100644 index c4c897c..0000000 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfTextSearchTestActivity.kt +++ /dev/null @@ -1,226 +0,0 @@ -package com.all.pdfreader.pro.app.ui.act - -import android.os.Bundle -import android.util.Log -import android.view.View -import android.widget.Toast -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import com.all.pdfreader.pro.app.databinding.ActivityPdfViewerTestBinding -import com.all.pdfreader.pro.app.ui.adapter.SearchResultsAdapter -import com.all.pdfreader.pro.app.util.PDFHighlighter -import com.all.pdfreader.pro.app.util.PDFSearchManager -import com.github.barteksc.pdfviewer.listener.OnPageChangeListener -import com.tom_roush.pdfbox.pdmodel.PDDocument -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.io.File - -class PdfTextSearchTestActivity : BaseActivity() { - override val TAG: String = "PdfTextSearchTestActivity" - private lateinit var binding: ActivityPdfViewerTestBinding - private lateinit var searchManager: PDFSearchManager - private lateinit var highlighter: PDFHighlighter - - private var pdfFile: File? = null - private val searchResults = mutableListOf() - private lateinit var adapter: SearchResultsAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityPdfViewerTestBinding.inflate(layoutInflater) - setContentView(binding.root) - val pdfPath = intent.getStringExtra("pdf_path") ?: "" - if (pdfPath.isEmpty()) { - finish() - return - } - pdfFile = File(pdfPath) - initViews() - setupPDFViewer() - loadPDF() - } - - private fun initViews() { - adapter = SearchResultsAdapter(searchResults) { result -> - onSearchResultClicked(result, true) - } - binding.searchResultsRecyclerView.adapter = adapter - binding.searchResultsRecyclerView.layoutManager = LinearLayoutManager(this) - - binding.searchButton.alpha = 0.5f - binding.searchButton.isEnabled = false - binding.searchButton.setOnClickListener { - performSearch() - } - binding.clearButton.setOnClickListener { - clearHighlights() - } - } - - private fun setupPDFViewer() { - searchManager = PDFSearchManager(binding.pdfView) - highlighter = PDFHighlighter(binding.pdfView, searchManager) - - initSearchDocument() - } - - private fun initSearchDocument() { - lifecycleScope.launch(Dispatchers.IO) { - searchManager.getDocument( - pdfFile!!, - onLoaded = { doc -> - lifecycleScope.launch(Dispatchers.IO) { - checkPageHasText(doc, binding.pdfView.currentPage) - } - }, - onError = { e -> - runOnUiThread { - binding.searchButton.alpha = 0.5f - binding.searchButton.isEnabled = false - } - } - ) - } - } - - - /** - * 异步检查指定页是否有文字并更新按钮 - */ - private suspend fun checkPageHasText(doc: PDDocument, pageIndex: Int): Boolean { - val hasText = searchManager.pageHasText(doc, pageIndex, minChars = 8) // 可调整阈值 - withContext(Dispatchers.Main) { - if (hasText) { - binding.searchButton.alpha = 1f - binding.searchButton.isEnabled = true - } else { - binding.searchButton.alpha = 0.5f - binding.searchButton.isEnabled = false - } - } - return hasText - } - - private var pageCheckJob: Job? = null - - private fun loadPDF() { - binding.pdfView.fromFile(pdfFile) - .enableSwipe(true) - .swipeHorizontal(true) - .enableDoubletap(true) - .defaultPage(0) - .onDraw(highlighter) // 设置绘制监听器 - .enableAnnotationRendering(true) - .password(null) - .scrollHandle(null) - .swipeHorizontal(false) - .enableAntialiasing(true) - .onPageChange(object : OnPageChangeListener { - override fun onPageChanged(page: Int, pageCount: Int) { - logDebug("page->$page") - logDebug("pageCount->$pageCount") - val doc = searchManager.getCachedDoc() - if (doc != null) { - lifecycleScope.launch(Dispatchers.IO) { - pageCheckJob?.cancel() - pageCheckJob = lifecycleScope.launch(Dispatchers.IO) { - delay(50) // 防抖 - val hasText = checkPageHasText(doc, page) - //确定这页有文字,是否进行搜索过,当前搜索的文字 - if (hasText && searchManager.hasSearched && searchManager.currentSearchText != null) { - searchTextIng( - pdfFile!!, - searchManager.currentSearchText ?: "", - binding.pdfView.currentPage - ) - } - } - } - } - - } - }) - .spacing(0) - .load() - } - - private fun performSearch() { - val searchText = binding.searchEditText.text.toString().trim() - if (searchText.isEmpty()) { - Toast.makeText(this, "请输入搜索关键词", Toast.LENGTH_SHORT).show() - return - } - - val file = pdfFile ?: return - - binding.progressBar.visibility = View.VISIBLE - - lifecycleScope.launch(Dispatchers.IO) { - searchTextIng(file, searchText, binding.pdfView.currentPage) - } - } - - private fun searchTextIng(pdfFile: File, key: String, targetPage: Int? = null) { - lifecycleScope.launch(Dispatchers.IO) { - try { - val results = searchManager.searchText(pdfFile, key, targetPage) - - withContext(Dispatchers.Main) { - binding.progressBar.visibility = View.GONE - searchResults.clear() - searchResults.addAll(results) - adapter.notifyDataSetChanged() - - if (!results.isEmpty()) { - results.firstOrNull()?.let { firstResult -> - onSearchResultClicked(firstResult) - } - } - } - } catch (e: Exception) { - withContext(Dispatchers.Main) { - binding.progressBar.visibility = View.GONE - } - } - } - } - - private fun onSearchResultClicked( - result: PDFSearchManager.TextSearchResult, - isJumpTo: Boolean = false - ) { - if (isJumpTo) { - // 跳转到对应页面 (PDFView 页面索引从0开始) - binding.pdfView.jumpTo(result.pageNumber - 1) - } - - // 设置高亮 - highlighter.setCurrentPageHighlights(result.pageNumber) - - // 标记已搜索高亮 - searchManager.currentSearchText = result.text - searchManager.hasSearched = true - - // 可选:滚动到搜索结果列表中的对应项 - val index = searchResults.indexOf(result) - if (index != -1) { - binding.searchResultsRecyclerView.scrollToPosition(index) - } - } - - private fun clearHighlights() { - searchManager.clearHighlights() - highlighter.clearCurrentHighlights() - searchResults.clear() - adapter.notifyDataSetChanged() - } - - override fun onDestroy() { - super.onDestroy() - searchManager.closeCachedDocument() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt index 09e4e2b..f135d0e 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/PdfViewActivity.kt @@ -71,6 +71,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList private lateinit var searchManager: PDFSearchManager private lateinit var highlighter: PDFHighlighter private var pageCheckJob: Job? = null + private var currentPassword: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -269,7 +270,6 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList finish() return } - setupPDFSearchManager()//初始化搜索文本需要的类 if (pdfDocument.isPassword) { showPasswordDialog(file) } else { @@ -316,6 +316,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList pageCheckJob?.cancel() // 启动新的防抖任务 pageCheckJob = lifecycleScope.launch(Dispatchers.IO) { + highlighter.clearCurrentHighlights()//新的之前先清除 delay(120) // 防抖 120ms val hasText = checkPageHasText(doc, page) if (hasText && searchManager.hasSearched && !searchManager.currentSearchText.isNullOrEmpty()) { @@ -339,6 +340,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList private fun showPasswordDialog(file: File) { PdfPasswordProtectionDialogFragment(file, onOkClick = { password -> tryLoadPdfWithPassword(file, password) + currentPassword = password }, onCancelClick = { finish() }).show(supportFragmentManager, TAG) @@ -349,6 +351,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList } private fun loadPdfInternal(file: File, password: String?) { + setupPDFSearchManager(password)//初始化搜索文本需要的类 binding.pdfview.fromFile(file).apply { password?.let { password(it) } // 只有在有密码时才调用 defaultPage(pdfDocument.lastReadPage) // 从上次阅读页码开始 @@ -454,17 +457,17 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList } } - private fun setupPDFSearchManager() { + private fun setupPDFSearchManager(password: String?) { searchManager = PDFSearchManager(binding.pdfview) highlighter = PDFHighlighter(binding.pdfview, searchManager) - initSearchDocument() + initSearchDocument(password) } - private fun initSearchDocument() { + private fun initSearchDocument(password: String?) { val file = File(pdfDocument.filePath) lifecycleScope.launch(Dispatchers.IO) { - searchManager.getDocument(file, onLoaded = { doc -> + searchManager.getDocument(file, password, onLoaded = { doc -> lifecycleScope.launch(Dispatchers.IO) { checkPageHasText(doc, binding.pdfview.currentPage) } @@ -504,7 +507,7 @@ class PdfViewActivity : BaseActivity(), OnLoadCompleteListener, OnPageChangeList private fun searchTextIng(pdfFile: File, key: String, targetPage: Int? = null) { lifecycleScope.launch(Dispatchers.IO) { try { - val results = searchManager.searchText(pdfFile, key, targetPage) + val results = searchManager.searchText(pdfFile, key, targetPage, currentPassword) withContext(Dispatchers.Main) { if (!results.isEmpty()) { diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SearchResultsAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SearchResultsAdapter.kt deleted file mode 100644 index ef9bfd5..0000000 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SearchResultsAdapter.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.all.pdfreader.pro.app.ui.adapter - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.all.pdfreader.pro.app.R -import com.all.pdfreader.pro.app.util.PDFSearchManager - -class SearchResultsAdapter( - private val results: List, - private val onItemClick: (PDFSearchManager.TextSearchResult) -> Unit -) : RecyclerView.Adapter() { - - class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { - val pageText: TextView = view.findViewById(R.id.pageText) - val matchCount: TextView = view.findViewById(R.id.matchCount) - val previewText: TextView = view.findViewById(R.id.previewText) - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_search_result, parent, false) - return ViewHolder(view) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val result = results[position] - - holder.pageText.text = "第 ${result.pageNumber} 页" - holder.matchCount.text = "${result.positions.size} 处匹配" - holder.previewText.text = "搜索: \"${result.text}\"" - - holder.itemView.setOnClickListener { - onItemClick(result) - } - } - - override fun getItemCount() = results.size -} \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt index 6b38f44..cf1a224 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt @@ -14,7 +14,6 @@ import com.all.pdfreader.pro.app.model.SortConfig import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.room.repository.PdfRepository import com.all.pdfreader.pro.app.ui.act.MainActivity -import com.all.pdfreader.pro.app.ui.act.PdfTextSearchTestActivity 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 diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/PDFSearchManager.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/PDFSearchManager.kt index f663e6f..0e39b20 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/util/PDFSearchManager.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/util/PDFSearchManager.kt @@ -67,6 +67,7 @@ class PDFSearchManager(private val pdfView: PDFView) { */ fun getDocument( file: File, + password: String?, onLoaded: ((PDDocument) -> Unit)? = null, onError: ((Exception) -> Unit)? = null ): PDDocument { @@ -74,7 +75,11 @@ class PDFSearchManager(private val pdfView: PDFView) { try { // 如果是新的文件,则关闭旧文档 cachedDoc?.close() - cachedDoc = PDDocument.load(file) + cachedDoc = if (!password.isNullOrEmpty()) { + PDDocument.load(file, password) + } else { + PDDocument.load(file) + } cachedPath = file.absolutePath // 通知加载成功 @@ -114,14 +119,19 @@ class PDFSearchManager(private val pdfView: PDFView) { * @param key 搜索关键词 * @param targetPage 可选参数:指定页码(从 0 开始)。如果为 null 则搜索整本。 */ - fun searchText(pdfFile: File, key: String, targetPage: Int? = null): List { + fun searchText(pdfFile: File, key: String, targetPage: Int? = null,password: String?): List { Log.d("ocean", "searchText pdfFile->$pdfFile") Log.d("ocean", "searchText key->$key") Log.d("ocean", "searchText targetPage->$targetPage") val results = mutableListOf() try { - val doc = getDocument(pdfFile) // 这里不再每次重新 load + // 这里不再每次重新 load + val doc = if (!password.isNullOrEmpty()) { + PDDocument.load(pdfFile, password) + } else { + PDDocument.load(pdfFile) + } val startPage = targetPage ?: 0 val endPage = targetPage ?: (doc.numberOfPages - 1) diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d704804..7eb4aed 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -21,6 +21,7 @@ android:id="@+id/sidebarBtn" android:layout_width="44dp" android:layout_height="44dp" + android:visibility="gone" android:layout_marginStart="6dp" android:background="@drawable/dr_click_effect_oval_transparent" android:gravity="center"> @@ -36,7 +37,7 @@ style="@style/TextViewFont_PopMedium" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="6dp" + android:layout_marginStart="16dp" android:layout_marginEnd="6dp" android:layout_weight="1" android:text="@string/app_name" diff --git a/app/src/main/res/layout/activity_pdf_viewer_test.xml b/app/src/main/res/layout/activity_pdf_viewer_test.xml deleted file mode 100644 index e4fd264..0000000 --- a/app/src/main/res/layout/activity_pdf_viewer_test.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - -