From 8af2650f03b859bfdbf72f38a529fa70ace08b09 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Sat, 12 Sep 2020 09:38:43 -0400 Subject: [PATCH 01/21] Search content --- .../main/java/io/legado/app/help/BookHelp.kt | 1 + .../app/ui/book/read/ReadBookActivity.kt | 12 ++ .../io/legado/app/ui/book/read/ReadMenu.kt | 7 + .../book/searchContent/SearchListActivity.kt | 89 ++++++++++ .../book/searchContent/SearchListAdapter.kt | 66 ++++++++ .../book/searchContent/SearchListFragment.kt | 152 ++++++++++++++++++ .../book/searchContent/SearchListViewModel.kt | 32 ++++ .../main/res/layout/activity_search_list.xml | 19 +++ .../main/res/layout/fragment_search_list.xml | 66 ++++++++ app/src/main/res/layout/item_search_list.xml | 31 ++++ app/src/main/res/layout/view_read_menu.xml | 34 ++++ app/src/main/res/values/strings.xml | 1 + 12 files changed, 510 insertions(+) create mode 100644 app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt create mode 100644 app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt create mode 100644 app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt create mode 100644 app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt create mode 100644 app/src/main/res/layout/activity_search_list.xml create mode 100644 app/src/main/res/layout/fragment_search_list.xml create mode 100644 app/src/main/res/layout/item_search_list.xml diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index 89a2bf41b..f52252f0b 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -145,6 +145,7 @@ object BookHelp { return fileNameList } + // 检测该章节是否下载 fun hasContent(book: Book, bookChapter: BookChapter): Boolean { return if (book.isLocalBook()) { true diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 7840123db..af4aa111a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -667,6 +667,18 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo } } + /** + * 打开搜索界面 + */ + override fun openSearchList() { + ReadBook.book?.let { + startActivityForResult( + requestCodeChapterList, + Pair("bookUrl", it.bookUrl) + ) + } + } + /** * 替换规则变化 */ diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt index 6d069ff68..26423e701 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt @@ -200,6 +200,12 @@ class ReadMenu : FrameLayout { } } + ll_search.onClick { + runMenuOut { + callBack?.openSearchList() + } + } + //朗读 ll_read_aloud.onClick { runMenuOut { @@ -291,6 +297,7 @@ class ReadMenu : FrameLayout { fun autoPage() fun openReplaceRule() fun openChapterList() + fun openSearchList() fun showReadStyle() fun showMoreSetting() fun showReadAloudDialog() diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt new file mode 100644 index 000000000..bb8d0cb49 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt @@ -0,0 +1,89 @@ +package io.legado.app.ui.book.searchContent + +import android.os.Bundle +import android.view.Menu +import androidx.appcompat.widget.SearchView +import androidx.core.view.isGone +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentPagerAdapter +import io.legado.app.R +import io.legado.app.base.VMBaseActivity +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.accentColor +import io.legado.app.lib.theme.primaryTextColor +import io.legado.app.utils.getViewModel +import io.legado.app.utils.gone +import io.legado.app.utils.visible +import kotlinx.android.synthetic.main.activity_chapter_list.* +import kotlinx.android.synthetic.main.view_tab_layout.* + + +class SearchListActivity : VMBaseActivity(R.layout.activity_search_list) { + override val viewModel: SearchListViewModel + get() = getViewModel(SearchListViewModel::class.java) + + private var searchView: SearchView? = null + + override fun onActivityCreated(savedInstanceState: Bundle?) { + tab_layout.isTabIndicatorFullWidth = false + tab_layout.setSelectedTabIndicatorColor(accentColor) + intent.getStringExtra("bookUrl")?.let { + viewModel.initBook(it) { + view_pager.adapter = TabFragmentPageAdapter(supportFragmentManager) + tab_layout.setupWithViewPager(view_pager) + } + } + } + + override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.search_view, menu) + val search = menu.findItem(R.id.menu_search) + searchView = search.actionView as SearchView + ATH.setTint(searchView!!, primaryTextColor) + searchView?.maxWidth = resources.displayMetrics.widthPixels + searchView?.onActionViewCollapsed() + searchView?.setOnCloseListener { + tab_layout.visible() + false + } + searchView?.setOnSearchClickListener { tab_layout.gone() } + searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + viewModel.startContentSearch(query) + return false + } + + override fun onQueryTextChange(newText: String): Boolean { + + return false + } + }) + return super.onCompatCreateOptionsMenu(menu) + } + + private inner class TabFragmentPageAdapter internal constructor(fm: FragmentManager) : + FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { + override fun getItem(position: Int): Fragment { + return SearchListFragment() + } + + override fun getCount(): Int { + return 1 + } + + override fun getPageTitle(position: Int): CharSequence? { + return "Search" + } + + } + + override fun onBackPressed() { + if (tab_layout.isGone) { + searchView?.onActionViewCollapsed() + tab_layout.visible() + } else { + super.onBackPressed() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt new file mode 100644 index 000000000..3c5d28971 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt @@ -0,0 +1,66 @@ +package io.legado.app.ui.book.searchContent + +import android.content.Context +import android.view.View +import io.legado.app.R +import io.legado.app.base.adapter.ItemViewHolder +import io.legado.app.base.adapter.SimpleRecyclerAdapter +import io.legado.app.data.entities.BookChapter +import io.legado.app.help.BookHelp +import io.legado.app.lib.theme.accentColor +import io.legado.app.utils.getCompatColor +import io.legado.app.utils.visible +import kotlinx.android.synthetic.main.item_bookmark.view.tv_chapter_name +import kotlinx.android.synthetic.main.item_chapter_list.view.* +import org.jetbrains.anko.sdk27.listeners.onClick + +class SearchListAdapter(context: Context, val callback: Callback) : + SimpleRecyclerAdapter(context, R.layout.item_search_list) { + + val cacheFileNames = hashSetOf() + + override fun convert(holder: ItemViewHolder, item: BookChapter, payloads: MutableList) { + with(holder.itemView) { + val isDur = callback.durChapterIndex() == item.index + val cached = callback.isLocalBook + || cacheFileNames.contains(BookHelp.formatChapterName(item)) + if (payloads.isEmpty()) { + // set search result color here + if (isDur) { + tv_chapter_name.setTextColor(context.accentColor) + } else { + tv_chapter_name.setTextColor(context.getCompatColor(R.color.primaryText)) + } + tv_chapter_name.text = item.title + + upHasCache(this, isDur, cached) + } else { + upHasCache(this, isDur, cached) + } + } + } + + override fun registerListener(holder: ItemViewHolder) { + holder.itemView.onClick { + getItem(holder.layoutPosition)?.let { + callback.openSearchResult(it) + } + } + } + + private fun upHasCache(itemView: View, isDur: Boolean, cached: Boolean) = itemView.apply { + tv_chapter_name.paint.isFakeBoldText = cached + iv_checked.setImageResource(R.drawable.ic_outline_cloud_24) + iv_checked.visible(!cached) + if (isDur) { + iv_checked.setImageResource(R.drawable.ic_check) + iv_checked.visible() + } + } + + interface Callback { + val isLocalBook: Boolean + fun openSearchResult(bookChapter: BookChapter) + fun durChapterIndex(): Int + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt new file mode 100644 index 000000000..be80cb9ac --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -0,0 +1,152 @@ +package io.legado.app.ui.book.searchContent + +import android.annotation.SuppressLint +import android.app.Activity.RESULT_OK +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.lifecycle.LiveData +import io.legado.app.App +import io.legado.app.R +import io.legado.app.base.VMBaseFragment +import io.legado.app.constant.EventBus +import io.legado.app.data.entities.Book +import io.legado.app.data.entities.BookChapter +import io.legado.app.help.BookHelp +import io.legado.app.lib.theme.bottomBackground +import io.legado.app.lib.theme.getPrimaryTextColor +import io.legado.app.ui.widget.recycler.UpLinearLayoutManager +import io.legado.app.ui.widget.recycler.VerticalDivider +import io.legado.app.utils.ColorUtils +import io.legado.app.utils.getViewModelOfActivity +import io.legado.app.utils.observeEvent +import kotlinx.android.synthetic.main.fragment_search_list.* +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.jetbrains.anko.sdk27.listeners.onClick + +class SearchListFragment : VMBaseFragment(R.layout.fragment_search_list), + SearchListAdapter.Callback, + SearchListViewModel.SearchListCallBack{ + override val viewModel: SearchListViewModel + get() = getViewModelOfActivity(SearchListViewModel::class.java) + + lateinit var adapter: SearchListAdapter + private var durChapterIndex = 0 + private lateinit var mLayoutManager: UpLinearLayoutManager + private var tocLiveData: LiveData>? = null + private var scrollToDurChapter = false + + override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { + viewModel.searchCallBack = this + + /* set color for the bottom bar + val bbg = bottomBackground + val btc = requireContext().getPrimaryTextColor(ColorUtils.isColorLight(bbg)) + ll_chapter_base_info.setBackgroundColor(bbg) + tv_current_chapter_info.setTextColor(btc) + iv_chapter_top.setColorFilter(btc) + iv_chapter_bottom.setColorFilter(btc) + + */ + initRecyclerView() + initView() + initBook() + } + + private fun initRecyclerView() { + adapter = SearchListAdapter(requireContext(), this) + mLayoutManager = UpLinearLayoutManager(requireContext()) + recycler_view.layoutManager = mLayoutManager + recycler_view.addItemDecoration(VerticalDivider(requireContext())) + recycler_view.adapter = adapter + } + + private fun initView() { + iv_search_content_top.onClick { mLayoutManager.scrollToPositionWithOffset(0, 0) } + iv_search_content_bottom.onClick { + if (adapter.itemCount > 0) { + mLayoutManager.scrollToPositionWithOffset(adapter.itemCount - 1, 0) + } + } + tv_current_search_info.onClick { + mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0) + } + } + + @SuppressLint("SetTextI18n") + private fun initBook() { + launch { + initDoc() + viewModel.book?.let { + durChapterIndex = it.durChapterIndex + tv_current_search_info.text = + "${it.durChapterTitle}(${it.durChapterIndex + 1}/${it.totalChapterNum})" + initCacheFileNames(it) + } + } + } + + private fun initDoc() { + tocLiveData?.removeObservers(this@SearchListFragment) + tocLiveData = App.db.bookChapterDao().observeByBook(viewModel.bookUrl) + tocLiveData?.observe(viewLifecycleOwner, { + adapter.setItems(it) + if (!scrollToDurChapter) { + mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0) + scrollToDurChapter = true + } + }) + } + + private fun initCacheFileNames(book: Book) { + launch(IO) { + adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) + withContext(Main) { + adapter.notifyItemRangeChanged(0, adapter.getActualItemCount(), true) + } + } + } + + override fun observeLiveBus() { + observeEvent(EventBus.SAVE_CONTENT) { chapter -> + viewModel.book?.bookUrl?.let { bookUrl -> + if (chapter.bookUrl == bookUrl) { + adapter.cacheFileNames.add(BookHelp.formatChapterName(chapter)) + adapter.notifyItemChanged(chapter.index, true) + } + } + } + } + + override fun startContentSearch(newText: String?) { + if (newText.isNullOrBlank()) { + //initDoc() + } else { + if (isLocalBook){ + + } + + tocLiveData?.removeObservers(this) + tocLiveData = App.db.bookChapterDao().liveDataSearch(viewModel.bookUrl, newText) + tocLiveData?.observe(viewLifecycleOwner, { + adapter.setItems(it) + }) + } + } + + override val isLocalBook: Boolean + get() = viewModel.book?.isLocalBook() == true + + override fun durChapterIndex(): Int { + return durChapterIndex + } + + override fun openSearchResult(bookChapter: BookChapter) { + activity?.setResult(RESULT_OK, Intent().putExtra("index", bookChapter.index)) + activity?.finish() + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt new file mode 100644 index 000000000..153f61416 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt @@ -0,0 +1,32 @@ +package io.legado.app.ui.book.searchContent + + +import android.app.Application +import io.legado.app.App +import io.legado.app.base.BaseViewModel +import io.legado.app.data.entities.Book + +class SearchListViewModel(application: Application) : BaseViewModel(application) { + var bookUrl: String = "" + var book: Book? = null + var searchCallBack: SearchListCallBack? = null + + fun initBook(bookUrl: String, success: () -> Unit) { + this.bookUrl = bookUrl + execute { + book = App.db.bookDao().getBook(bookUrl) + }.onSuccess { + success.invoke() + } + } + + fun startContentSearch(newText: String?) { + searchCallBack?.startContentSearch(newText) + } + + + interface SearchListCallBack { + fun startContentSearch(newText: String?) + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_search_list.xml b/app/src/main/res/layout/activity_search_list.xml new file mode 100644 index 000000000..159f77324 --- /dev/null +++ b/app/src/main/res/layout/activity_search_list.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search_list.xml b/app/src/main/res/layout/fragment_search_list.xml new file mode 100644 index 000000000..79fefe11f --- /dev/null +++ b/app/src/main/res/layout/fragment_search_list.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_search_list.xml b/app/src/main/res/layout/item_search_list.xml new file mode 100644 index 000000000..0b8bab809 --- /dev/null +++ b/app/src/main/res/layout/item_search_list.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml index 1b9bc2a98..22a3bc236 100644 --- a/app/src/main/res/layout/view_read_menu.xml +++ b/app/src/main/res/layout/view_read_menu.xml @@ -260,6 +260,40 @@ android:textSize="12sp" /> + + + + + + + + Save, Import, Share theme Share selected sources Sort by update time + Search content \ No newline at end of file From f8572d2a4216085a7eb6c0990a03440da73dbf84 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 13 Sep 2020 17:39:38 +0800 Subject: [PATCH 02/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/io/legado/app/model/webBook/BookContent.kt | 2 +- app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt index 040ee91a4..c2ae45071 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt @@ -97,7 +97,7 @@ object BookContent { val replaceRegex = bookSource.ruleContent?.replaceRegex if (!replaceRegex.isNullOrEmpty()) { val analyzeRule = AnalyzeRule(book) - analyzeRule.setContent(body, baseUrl) + analyzeRule.setContent(contentStr, baseUrl) analyzeRule.chapter = bookChapter contentStr = analyzeRule.getString(replaceRegex) } diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 3e48c3ebf..e2834e456 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -160,6 +160,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r request.setAllowedOverRoaming(true) // 允许下载的网路类型 request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI) + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // 设置下载文件保存的路径和文件名 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager From cd8615075cc14cd89c316d3ba81212663e927235 Mon Sep 17 00:00:00 2001 From: gedoor Date: Sun, 13 Sep 2020 20:06:56 +0800 Subject: [PATCH 03/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 1 + app/src/main/java/io/legado/app/App.kt | 2 +- .../legado/app/service/CheckSourceService.kt | 35 +++++++------- .../io/legado/app/service/DownloadService.kt | 46 +++++++++++++++++++ .../book/source/manage/BookSourceActivity.kt | 5 -- 5 files changed, 66 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/io/legado/app/service/DownloadService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 34c57bb7b..ca5b1e976 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -354,6 +354,7 @@ + diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index 98f255c72..37aea552d 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -156,7 +156,7 @@ class App : MultiDexApplication() { //用唯一的ID创建渠道对象 val downloadChannel = NotificationChannel( channelIdDownload, - getString(R.string.offline_cache), + getString(R.string.action_download), NotificationManager.IMPORTANCE_LOW ) //初始化channel diff --git a/app/src/main/java/io/legado/app/service/CheckSourceService.kt b/app/src/main/java/io/legado/app/service/CheckSourceService.kt index e6cbb5966..09900d0d0 100644 --- a/app/src/main/java/io/legado/app/service/CheckSourceService.kt +++ b/app/src/main/java/io/legado/app/service/CheckSourceService.kt @@ -24,6 +24,21 @@ class CheckSourceService : BaseService() { private val allIds = ArrayList() private val checkedIds = ArrayList() private var processIndex = 0 + private val notificationBuilder by lazy { + NotificationCompat.Builder(this, AppConst.channelIdReadAloud) + .setSmallIcon(R.drawable.ic_network_check) + .setOngoing(true) + .setContentTitle(getString(R.string.check_book_source)) + .setContentIntent( + IntentHelp.activityPendingIntent(this, "activity") + ) + .addAction( + R.drawable.ic_stop_black_24dp, + getString(R.string.cancel), + IntentHelp.servicePendingIntent(this, IntentAction.stop) + ) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + } override fun onCreate() { super.onCreate() @@ -105,23 +120,9 @@ class CheckSourceService : BaseService() { * 更新通知 */ private fun updateNotification(state: Int, msg: String) { - val builder = NotificationCompat.Builder(this, AppConst.channelIdReadAloud) - .setSmallIcon(R.drawable.ic_network_check) - .setOngoing(true) - .setContentTitle(getString(R.string.check_book_source)) - .setContentText(msg) - .setContentIntent( - IntentHelp.activityPendingIntent(this, "activity") - ) - .addAction( - R.drawable.ic_stop_black_24dp, - getString(R.string.cancel), - IntentHelp.servicePendingIntent(this, IntentAction.stop) - ) - builder.setProgress(allIds.size, state, false) - builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - val notification = builder.build() - startForeground(112202, notification) + notificationBuilder.setContentText(msg) + notificationBuilder.setProgress(allIds.size, state, false) + startForeground(112202, notificationBuilder.build()) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt new file mode 100644 index 000000000..7a77bb7a5 --- /dev/null +++ b/app/src/main/java/io/legado/app/service/DownloadService.kt @@ -0,0 +1,46 @@ +package io.legado.app.service + +import android.content.Intent +import androidx.core.app.NotificationCompat +import io.legado.app.R +import io.legado.app.base.BaseService +import io.legado.app.constant.AppConst +import io.legado.app.constant.IntentAction +import io.legado.app.help.IntentHelp + +class DownloadService : BaseService() { + + private val notificationBuilder by lazy { + val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload) + .setSmallIcon(R.drawable.ic_download) + .setOngoing(true) + .setContentTitle(getString(R.string.action_download)) + builder.addAction( + R.drawable.ic_stop_black_24dp, + getString(R.string.cancel), + IntentHelp.servicePendingIntent(this, IntentAction.stop) + ) + builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + } + + override fun onCreate() { + super.onCreate() + updateNotification("准备下载") + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + when (intent?.action) { + + } + return super.onStartCommand(intent, flags, startId) + } + + /** + * 更新通知 + */ + private fun updateNotification(content: String) { + notificationBuilder.setContentText(content) + val notification = notificationBuilder.build() + startForeground(AppConst.notificationIdDownload, notification) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index 59422c186..07f959719 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -24,9 +24,6 @@ import io.legado.app.constant.AppPattern import io.legado.app.data.entities.BookSource import io.legado.app.help.IntentDataHelp import io.legado.app.lib.dialogs.* -import io.legado.app.lib.dialogs.alert -import io.legado.app.lib.dialogs.noButton -import io.legado.app.lib.dialogs.okButton import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor import io.legado.app.service.help.CheckSource @@ -47,7 +44,6 @@ import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.toast - import java.io.File import java.text.Collator @@ -212,7 +208,6 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity else -> data.reversed() } } - recycler_view.scrollToPosition(0) val diffResult = DiffUtil .calculateDiff(DiffCallBack(ArrayList(adapter.getItems()), sourceList)) adapter.setItems(sourceList, diffResult) From f48c44bb920788f91c0ac47000452cac7085c115 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Sun, 13 Sep 2020 18:14:00 -0400 Subject: [PATCH 04/21] search content be able to show the counts of query for each chapter Still don't know why Coroutine lauch will execute repeatedly --- app/src/main/AndroidManifest.xml | 4 + .../io/legado/app/data/dao/BookChapterDao.kt | 3 + .../app/ui/book/read/ReadBookActivity.kt | 3 +- .../book/searchContent/SearchListFragment.kt | 93 ++++++++++++++++--- app/src/main/res/layout/view_read_menu.xml | 9 +- 5 files changed, 97 insertions(+), 15 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 34c57bb7b..6cf447d0e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -222,6 +222,10 @@ android:name=".ui.book.chapterlist.ChapterListActivity" android:launchMode="singleTop" android:screenOrientation="behind" /> + (R.layout.activity_boo */ override fun openSearchList() { ReadBook.book?.let { - startActivityForResult( + startActivityForResult( requestCodeChapterList, Pair("bookUrl", it.bookUrl) ) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index be80cb9ac..46ec1f9d9 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Activity.RESULT_OK import android.content.Intent import android.os.Bundle +import android.util.Log import android.view.View import androidx.lifecycle.LiveData import io.legado.app.App @@ -13,19 +14,16 @@ import io.legado.app.constant.EventBus import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp -import io.legado.app.lib.theme.bottomBackground -import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.ui.widget.recycler.UpLinearLayoutManager import io.legado.app.ui.widget.recycler.VerticalDivider -import io.legado.app.utils.ColorUtils import io.legado.app.utils.getViewModelOfActivity import io.legado.app.utils.observeEvent import kotlinx.android.synthetic.main.fragment_search_list.* +import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.jetbrains.anko.sdk27.listeners.onClick +import java.util.regex.Pattern class SearchListFragment : VMBaseFragment(R.layout.fragment_search_list), SearchListAdapter.Callback, @@ -123,20 +121,91 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment override fun startContentSearch(newText: String?) { if (newText.isNullOrBlank()) { - //initDoc() + initDoc() } else { - if (isLocalBook){ + var count: Int = 0 + val beginTime = System.currentTimeMillis() + App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ + launch(IO) { + if (isLocalBook || + adapter.cacheFileNames.contains(BookHelp.formatChapterName(it)) + ) { + val value = searchChapter(newText, it) + count += value + } + } + } + //adapter.setItems(list) + val finishedTime = System.currentTimeMillis() - beginTime + Log.d("Jason", "Search finished, the total time cost is $finishedTime") + Log.d("Jason", "Search finished, the total count is $count") + //tocLiveData?.removeObservers(this) + //tocLiveData = App.db.bookChapterDao().liveDataSearch(viewModel.bookUrl, newText) + //tocLiveData?.observe(viewLifecycleOwner, { + //adapter.setItems(it) + //}) + } + + } + + + + private suspend fun searchChapter(query: String, chapter: BookChapter?): Int { + + val startTime = System.currentTimeMillis() + val searchResult: MutableList = mutableListOf() + var count = 0 + if (chapter != null){ + Log.d("Jason", "Search ${chapter.title}") + viewModel.book?.let { bookSource -> + val bookContent = BookHelp.getContent(bookSource, chapter) + /* replace content, let's focus on original content first + chapter.title = when (AppConfig.chineseConverterType) { + 1 -> HanLP.convertToSimplifiedChinese(chapter.title) + 2 -> HanLP.convertToTraditionalChinese(chapter.title) + else -> chapter.title + } + var replaceContents: List? = null + bookContent?.let { + replaceContents = BookHelp.disposeContent( + chapter.title, + bookSource.name, + bookSource.bookUrl, + it, + bookSource.useReplaceRule + ) + } + + replaceContents?.map { + async(IO) { + if (it.contains(query)) { + Log.d("targetd contents", it) + searchResult.add(it) + } + } + }?.awaitAll() + */ + count = bookContent?.let { countMatches(it, query) }!! + Log.d("Jason", "Search ${chapter.title} finished, the appeared count is $count") } + val endTime = System.currentTimeMillis() - startTime + Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime") + } + return count + } - tocLiveData?.removeObservers(this) - tocLiveData = App.db.bookChapterDao().liveDataSearch(viewModel.bookUrl, newText) - tocLiveData?.observe(viewLifecycleOwner, { - adapter.setItems(it) - }) + fun countMatches(string: String, pattern: String): Int { + val matcher = Pattern.compile(pattern).matcher(string) + + var count = 0 + while (matcher.find()) { + count++ } + return count } + override val isLocalBook: Boolean get() = viewModel.book?.isLocalBook() == true diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml index 22a3bc236..b63fba1ff 100644 --- a/app/src/main/res/layout/view_read_menu.xml +++ b/app/src/main/res/layout/view_read_menu.xml @@ -260,7 +260,12 @@ android:textSize="12sp" /> - + + + From ec9e554d0d34be407f808cbbd4fd17a5e3e7a463 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Sun, 13 Sep 2020 23:33:34 -0400 Subject: [PATCH 05/21] add and delete add data class searchList delete durchapterindex --- .../book/searchContent/SearchListActivity.kt | 3 +- .../book/searchContent/SearchListAdapter.kt | 39 ++---- .../book/searchContent/SearchListFragment.kt | 118 ++++++++++-------- .../app/ui/book/searchContent/SearchResult.kt | 22 ++++ app/src/main/res/layout/item_search_list.xml | 16 +-- 5 files changed, 107 insertions(+), 91 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt index bb8d0cb49..3474f446d 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt @@ -45,6 +45,7 @@ class SearchListActivity : VMBaseActivity(R.layout.activity searchView?.onActionViewCollapsed() searchView?.setOnCloseListener { tab_layout.visible() + //to do clean false } searchView?.setOnSearchClickListener { tab_layout.gone() } @@ -62,7 +63,7 @@ class SearchListActivity : VMBaseActivity(R.layout.activity return super.onCompatCreateOptionsMenu(menu) } - private inner class TabFragmentPageAdapter internal constructor(fm: FragmentManager) : + private inner class TabFragmentPageAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { override fun getItem(position: Int): Fragment { return SearchListFragment() diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt index 3c5d28971..e2f2f1ac4 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt @@ -1,7 +1,11 @@ package io.legado.app.ui.book.searchContent import android.content.Context +import android.os.Build +import android.text.Html import android.view.View +import androidx.annotation.RequiresApi +import androidx.core.text.HtmlCompat import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.SimpleRecyclerAdapter @@ -10,32 +14,23 @@ import io.legado.app.help.BookHelp import io.legado.app.lib.theme.accentColor import io.legado.app.utils.getCompatColor import io.legado.app.utils.visible -import kotlinx.android.synthetic.main.item_bookmark.view.tv_chapter_name -import kotlinx.android.synthetic.main.item_chapter_list.view.* +import kotlinx.android.synthetic.main.item_search_list.view.* import org.jetbrains.anko.sdk27.listeners.onClick class SearchListAdapter(context: Context, val callback: Callback) : - SimpleRecyclerAdapter(context, R.layout.item_search_list) { + SimpleRecyclerAdapter(context, R.layout.item_search_list) { val cacheFileNames = hashSetOf() - override fun convert(holder: ItemViewHolder, item: BookChapter, payloads: MutableList) { + @RequiresApi(Build.VERSION_CODES.N) + override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList) { with(holder.itemView) { - val isDur = callback.durChapterIndex() == item.index - val cached = callback.isLocalBook - || cacheFileNames.contains(BookHelp.formatChapterName(item)) if (payloads.isEmpty()) { // set search result color here - if (isDur) { - tv_chapter_name.setTextColor(context.accentColor) - } else { - tv_chapter_name.setTextColor(context.getCompatColor(R.color.primaryText)) - } - tv_chapter_name.text = item.title - - upHasCache(this, isDur, cached) + tv_search_result.text = HtmlCompat.fromHtml(item.presentText, HtmlCompat.FROM_HTML_MODE_LEGACY) } else { - upHasCache(this, isDur, cached) + //to do + } } } @@ -49,18 +44,10 @@ class SearchListAdapter(context: Context, val callback: Callback) : } private fun upHasCache(itemView: View, isDur: Boolean, cached: Boolean) = itemView.apply { - tv_chapter_name.paint.isFakeBoldText = cached - iv_checked.setImageResource(R.drawable.ic_outline_cloud_24) - iv_checked.visible(!cached) - if (isDur) { - iv_checked.setImageResource(R.drawable.ic_check) - iv_checked.visible() - } + tv_search_result.paint.isFakeBoldText = cached } interface Callback { - val isLocalBook: Boolean - fun openSearchResult(bookChapter: BookChapter) - fun durChapterIndex(): Int + fun openSearchResult(searchResult: SearchResult) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index 46ec1f9d9..c25fdd58c 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -32,10 +32,9 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment get() = getViewModelOfActivity(SearchListViewModel::class.java) lateinit var adapter: SearchListAdapter - private var durChapterIndex = 0 private lateinit var mLayoutManager: UpLinearLayoutManager private var tocLiveData: LiveData>? = null - private var scrollToDurChapter = false + private var searchResultCounts = 0 override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { viewModel.searchCallBack = this @@ -69,35 +68,28 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment mLayoutManager.scrollToPositionWithOffset(adapter.itemCount - 1, 0) } } - tv_current_search_info.onClick { - mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0) - } } @SuppressLint("SetTextI18n") private fun initBook() { launch { - initDoc() viewModel.book?.let { - durChapterIndex = it.durChapterIndex tv_current_search_info.text = - "${it.durChapterTitle}(${it.durChapterIndex + 1}/${it.totalChapterNum})" + "搜索结果:$searchResultCounts" initCacheFileNames(it) } } } + /* private fun initDoc() { tocLiveData?.removeObservers(this@SearchListFragment) tocLiveData = App.db.bookChapterDao().observeByBook(viewModel.bookUrl) tocLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) - if (!scrollToDurChapter) { - mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0) - scrollToDurChapter = true - } }) } + */ private fun initCacheFileNames(book: Book) { launch(IO) { @@ -120,46 +112,34 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } override fun startContentSearch(newText: String?) { - if (newText.isNullOrBlank()) { - initDoc() - } else { - var count: Int = 0 - val beginTime = System.currentTimeMillis() + if (!newText.isNullOrBlank()) { App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ launch(IO) { + val beginTime = System.currentTimeMillis() if (isLocalBook || adapter.cacheFileNames.contains(BookHelp.formatChapterName(it)) ) { val value = searchChapter(newText, it) - count += value + searchResultCounts += value } + val finishedTime = System.currentTimeMillis() - beginTime + Log.d("Jason", "Search finished, the total time cost is $finishedTime") + Log.d("Jason", "Search finished, the total count is $searchResultCounts") } } - //adapter.setItems(list) - val finishedTime = System.currentTimeMillis() - beginTime - Log.d("Jason", "Search finished, the total time cost is $finishedTime") - Log.d("Jason", "Search finished, the total count is $count") - //tocLiveData?.removeObservers(this) - //tocLiveData = App.db.bookChapterDao().liveDataSearch(viewModel.bookUrl, newText) - //tocLiveData?.observe(viewLifecycleOwner, { - //adapter.setItems(it) - //}) - } + } } - - private suspend fun searchChapter(query: String, chapter: BookChapter?): Int { - - val startTime = System.currentTimeMillis() - val searchResult: MutableList = mutableListOf() - var count = 0 + val searchResult: MutableList = mutableListOf() + var positions : List? = listOf() if (chapter != null){ Log.d("Jason", "Search ${chapter.title}") viewModel.book?.let { bookSource -> val bookContent = BookHelp.getContent(bookSource, chapter) + if (bookContent != null){ /* replace content, let's focus on original content first chapter.title = when (AppConfig.chineseConverterType) { 1 -> HanLP.convertToSimplifiedChinese(chapter.title) @@ -186,35 +166,73 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } }?.awaitAll() */ - count = bookContent?.let { countMatches(it, query) }!! - Log.d("Jason", "Search ${chapter.title} finished, the appeared count is $count") + positions = countMatches(bookContent, query) + positions?.map{ + searchResult.add( + SearchResult(index = 0, + text = constructText(bookContent, it), + chapterTitle = chapter.title, + query = query, + pageSize = 0, // to be finished + chapterIndex = chapter.index, // to be finished + pageIndex = 0, // to be finished + ) + ) + } + adapter.addItems(searchResult) + Log.d("Jason", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}") + } } val endTime = System.currentTimeMillis() - startTime Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime") } - return count + return positions!!.size } - fun countMatches(string: String, pattern: String): Int { - val matcher = Pattern.compile(pattern).matcher(string) - + private fun countMatches(content: String, pattern: String): List { + val position : MutableList = mutableListOf() var count = 0 - while (matcher.find()) { - count++ + var index = content.indexOf(pattern) + while(index >= 0){ + position.add(index) + index = content.indexOf(pattern, index + 1); } - return count + return position } + private fun constructText(content: String, position: Int): String{ - override val isLocalBook: Boolean - get() = viewModel.book?.isLocalBook() == true - - override fun durChapterIndex(): Int { - return durChapterIndex + val length = 10 + var po1 = position - length + var po2 = position + length + if (po1 <0) { + po1 = 0 + } + if (po2 > content.length){ + po2 = content.length + } + return content.substring(po1, po2) + /* + if (position >= length && position <= content.length - length){ + return content.substring(position - length, position + length) + } + else if (position <= length){ + return content.substring(0, position + length) + } + else if (position >= content.length - length){ + return content.substring(position - length, content.length) + } + */ } - override fun openSearchResult(bookChapter: BookChapter) { - activity?.setResult(RESULT_OK, Intent().putExtra("index", bookChapter.index)) + val isLocalBook: Boolean + get() = viewModel.book?.isLocalBook() == true + + override fun openSearchResult(searchResult: SearchResult) { + val searchData = Intent() + searchData.putExtra("index", searchResult.chapterIndex) + searchData.putExtra("pageIndex", searchResult.pageIndex) + activity?.setResult(RESULT_OK, searchData) activity?.finish() } diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt new file mode 100644 index 000000000..5b0268026 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt @@ -0,0 +1,22 @@ +package io.legado.app.ui.book.searchContent + +data class SearchResult( + var index: Int = 0, + var text: String = "", + var chapterTitle: String = "", + val query: String, + var pageSize: Int = 0, + var chapterIndex: Int = 0, + var pageIndex: Int = 0, +) { + val presentText: String + get(){ + val sub1 = text.substring(0, text.indexOf(query)) + val sub2 = text.substring(text.indexOf(query) + query.length, text.length) + return "$sub1 " + + "$query" + + "$sub2 " + + "($chapterTitle)" + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/item_search_list.xml b/app/src/main/res/layout/item_search_list.xml index 0b8bab809..3624ae1e4 100644 --- a/app/src/main/res/layout/item_search_list.xml +++ b/app/src/main/res/layout/item_search_list.xml @@ -8,24 +8,12 @@ android:padding="12dp"> - - + app:layout_constraintRight_toLeftOf="parent" /> \ No newline at end of file From 56edd5a3a7064f438044b70b838845ba66d27729 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 00:19:01 -0400 Subject: [PATCH 06/21] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/searchContent/SearchListActivity.kt | 5 +- .../book/searchContent/SearchListAdapter.kt | 10 +-- .../book/searchContent/SearchListFragment.kt | 73 ++++++++----------- .../book/searchContent/SearchListViewModel.kt | 1 + app/src/main/res/layout/item_search_list.xml | 4 +- 5 files changed, 39 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt index 3474f446d..ec1cbc0c7 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt @@ -20,6 +20,7 @@ import kotlinx.android.synthetic.main.view_tab_layout.* class SearchListActivity : VMBaseActivity(R.layout.activity_search_list) { + // todo: 完善搜索界面UI override val viewModel: SearchListViewModel get() = getViewModel(SearchListViewModel::class.java) @@ -51,7 +52,9 @@ class SearchListActivity : VMBaseActivity(R.layout.activity searchView?.setOnSearchClickListener { tab_layout.gone() } searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { - viewModel.startContentSearch(query) + if (viewModel.lastQuery != query){ + viewModel.startContentSearch(query) + } return false } diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt index e2f2f1ac4..8f8a83a88 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt @@ -3,6 +3,7 @@ package io.legado.app.ui.book.searchContent import android.content.Context import android.os.Build import android.text.Html +import android.util.Log import android.view.View import androidx.annotation.RequiresApi import androidx.core.text.HtmlCompat @@ -22,15 +23,10 @@ class SearchListAdapter(context: Context, val callback: Callback) : val cacheFileNames = hashSetOf() - @RequiresApi(Build.VERSION_CODES.N) override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList) { with(holder.itemView) { if (payloads.isEmpty()) { - // set search result color here tv_search_result.text = HtmlCompat.fromHtml(item.presentText, HtmlCompat.FROM_HTML_MODE_LEGACY) - } else { - //to do - } } } @@ -43,10 +39,6 @@ class SearchListAdapter(context: Context, val callback: Callback) : } } - private fun upHasCache(itemView: View, isDur: Boolean, cached: Boolean) = itemView.apply { - tv_search_result.paint.isFakeBoldText = cached - } - interface Callback { fun openSearchResult(searchResult: SearchResult) } diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index c25fdd58c..7a6915856 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -81,16 +81,6 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } } - /* - private fun initDoc() { - tocLiveData?.removeObservers(this@SearchListFragment) - tocLiveData = App.db.bookChapterDao().observeByBook(viewModel.bookUrl) - tocLiveData?.observe(viewLifecycleOwner, { - adapter.setItems(it) - }) - } - */ - private fun initCacheFileNames(book: Book) { launch(IO) { adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) @@ -113,14 +103,21 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment override fun startContentSearch(newText: String?) { if (!newText.isNullOrBlank()) { + adapter.clearItems() + viewModel.lastQuery = newText App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ launch(IO) { val beginTime = System.currentTimeMillis() - if (isLocalBook || - adapter.cacheFileNames.contains(BookHelp.formatChapterName(it)) + if (isLocalBook + || adapter.cacheFileNames.contains(BookHelp.formatChapterName(it)) ) { - val value = searchChapter(newText, it) - searchResultCounts += value + val searchResults = searchChapter(newText, it) + if (searchResults.size > 0 ){ + searchResultCounts += searchResults.size + withContext(Main){ + adapter.addItems(searchResults) + } + } } val finishedTime = System.currentTimeMillis() - beginTime Log.d("Jason", "Search finished, the total time cost is $finishedTime") @@ -131,15 +128,17 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } - private suspend fun searchChapter(query: String, chapter: BookChapter?): Int { + private fun searchChapter(query: String, chapter: BookChapter?): MutableList { val startTime = System.currentTimeMillis() - val searchResult: MutableList = mutableListOf() + val searchResults: MutableList = mutableListOf() var positions : List? = listOf() if (chapter != null){ Log.d("Jason", "Search ${chapter.title}") viewModel.book?.let { bookSource -> val bookContent = BookHelp.getContent(bookSource, chapter) if (bookContent != null){ + //todo: 搜索替换后的正文 + //todo: 计算搜索结果所在的pageIndex直接跳转 /* replace content, let's focus on original content first chapter.title = when (AppConfig.chineseConverterType) { 1 -> HanLP.convertToSimplifiedChinese(chapter.title) @@ -168,30 +167,28 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment */ positions = countMatches(bookContent, query) positions?.map{ - searchResult.add( - SearchResult(index = 0, - text = constructText(bookContent, it), - chapterTitle = chapter.title, - query = query, - pageSize = 0, // to be finished - chapterIndex = chapter.index, // to be finished - pageIndex = 0, // to be finished - ) + val result = SearchResult(index = 0, + text = constructText(bookContent, it), + chapterTitle = chapter.title, + query = query, + pageSize = 0, // to be finished + chapterIndex = chapter.index, // to be finished + pageIndex = 0, // to be finished ) + searchResults.add(result) + Log.d("Jason", result.presentText) } - adapter.addItems(searchResult) Log.d("Jason", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}") } } val endTime = System.currentTimeMillis() - startTime Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime") } - return positions!!.size + return searchResults } private fun countMatches(content: String, pattern: String): List { val position : MutableList = mutableListOf() - var count = 0 var index = content.indexOf(pattern) while(index >= 0){ position.add(index) @@ -201,8 +198,11 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } private fun constructText(content: String, position: Int): String{ - - val length = 10 + // 构建关键词周边文字,在搜索结果里显示 + // todo: 判断段落,只在关键词所在段落内分割 + // todo: 利用标点符号分割完整的句 + // todo: length和设置结合,自由调整周边文字长度 + val length = 20 var po1 = position - length var po2 = position + length if (po1 <0) { @@ -211,18 +211,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment if (po2 > content.length){ po2 = content.length } - return content.substring(po1, po2) - /* - if (position >= length && position <= content.length - length){ - return content.substring(position - length, position + length) - } - else if (position <= length){ - return content.substring(0, position + length) - } - else if (position >= content.length - length){ - return content.substring(position - length, content.length) - } - */ + return "..." + content.substring(po1, po2) } val isLocalBook: Boolean diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt index 153f61416..81c223d30 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt @@ -10,6 +10,7 @@ class SearchListViewModel(application: Application) : BaseViewModel(application) var bookUrl: String = "" var book: Book? = null var searchCallBack: SearchListCallBack? = null + var lastQuery: String = "" fun initBook(bookUrl: String, success: () -> Unit) { this.bookUrl = bookUrl diff --git a/app/src/main/res/layout/item_search_list.xml b/app/src/main/res/layout/item_search_list.xml index 3624ae1e4..2dcaf1436 100644 --- a/app/src/main/res/layout/item_search_list.xml +++ b/app/src/main/res/layout/item_search_list.xml @@ -9,9 +9,9 @@ From cddbc8f05b0b69d5429856b96eeb3b55ee5e2fd2 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 00:40:02 -0400 Subject: [PATCH 07/21] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 搜索结果数目更新,修复不同搜索结果出现在同一段落只显示第一个搜索结果 --- .../book/searchContent/SearchListFragment.kt | 92 ++++++++++--------- .../app/ui/book/searchContent/SearchResult.kt | 5 +- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index 7a6915856..792bbdb7f 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -14,8 +14,11 @@ import io.legado.app.constant.EventBus import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp +import io.legado.app.lib.theme.bottomBackground +import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.ui.widget.recycler.UpLinearLayoutManager import io.legado.app.ui.widget.recycler.VerticalDivider +import io.legado.app.utils.ColorUtils import io.legado.app.utils.getViewModelOfActivity import io.legado.app.utils.observeEvent import kotlinx.android.synthetic.main.fragment_search_list.* @@ -33,21 +36,16 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment lateinit var adapter: SearchListAdapter private lateinit var mLayoutManager: UpLinearLayoutManager - private var tocLiveData: LiveData>? = null private var searchResultCounts = 0 override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { viewModel.searchCallBack = this - - /* set color for the bottom bar val bbg = bottomBackground val btc = requireContext().getPrimaryTextColor(ColorUtils.isColorLight(bbg)) - ll_chapter_base_info.setBackgroundColor(bbg) - tv_current_chapter_info.setTextColor(btc) - iv_chapter_top.setColorFilter(btc) - iv_chapter_bottom.setColorFilter(btc) - - */ + ll_search_base_info.setBackgroundColor(bbg) + tv_current_search_info.setTextColor(btc) + iv_search_content_top.setColorFilter(btc) + iv_search_content_bottom.setColorFilter(btc) initRecyclerView() initView() initBook() @@ -73,9 +71,8 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment @SuppressLint("SetTextI18n") private fun initBook() { launch { + tv_current_search_info.text = "搜索结果:$searchResultCounts" viewModel.book?.let { - tv_current_search_info.text = - "搜索结果:$searchResultCounts" initCacheFileNames(it) } } @@ -101,6 +98,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } } + @SuppressLint("SetTextI18n") override fun startContentSearch(newText: String?) { if (!newText.isNullOrBlank()) { adapter.clearItems() @@ -114,7 +112,9 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment val searchResults = searchChapter(newText, it) if (searchResults.size > 0 ){ searchResultCounts += searchResults.size + withContext(Main){ + tv_current_search_info.text = "搜索结果:$searchResultCounts" adapter.addItems(searchResults) } } @@ -137,43 +137,43 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment viewModel.book?.let { bookSource -> val bookContent = BookHelp.getContent(bookSource, chapter) if (bookContent != null){ - //todo: 搜索替换后的正文 - //todo: 计算搜索结果所在的pageIndex直接跳转 - /* replace content, let's focus on original content first - chapter.title = when (AppConfig.chineseConverterType) { - 1 -> HanLP.convertToSimplifiedChinese(chapter.title) - 2 -> HanLP.convertToTraditionalChinese(chapter.title) - else -> chapter.title - } - var replaceContents: List? = null - bookContent?.let { - replaceContents = BookHelp.disposeContent( - chapter.title, - bookSource.name, - bookSource.bookUrl, - it, - bookSource.useReplaceRule - ) - } + //todo: 搜索替换后的正文句子列表 + /* + chapter.title = when (AppConfig.chineseConverterType) { + 1 -> HanLP.convertToSimplifiedChinese(chapter.title) + 2 -> HanLP.convertToTraditionalChinese(chapter.title) + else -> chapter.title + } + var replaceContents: List? = null + bookContent?.let { + replaceContents = BookHelp.disposeContent( + chapter.title, + bookSource.name, + bookSource.bookUrl, + it, + bookSource.useReplaceRule + ) + } - replaceContents?.map { - async(IO) { - if (it.contains(query)) { - Log.d("targetd contents", it) - searchResult.add(it) + replaceContents?.map { + async(IO) { + if (it.contains(query)) { + Log.d("targetd contents", it) + searchResult.add(it) + } } - } - }?.awaitAll() - */ - positions = countMatches(bookContent, query) + }?.awaitAll() + */ + positions = searchPosition(bookContent, query) positions?.map{ + val construct = constructText(bookContent, it) val result = SearchResult(index = 0, - text = constructText(bookContent, it), + text = construct[1] as String, chapterTitle = chapter.title, query = query, - pageSize = 0, // to be finished - chapterIndex = chapter.index, // to be finished - pageIndex = 0, // to be finished + pageIndex = 0, //todo: 计算搜索结果所在的pageIndex直接跳转 + chapterIndex = chapter.index, + newPosition = construct[0] as Int ) searchResults.add(result) Log.d("Jason", result.presentText) @@ -187,7 +187,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment return searchResults } - private fun countMatches(content: String, pattern: String): List { + private fun searchPosition(content: String, pattern: String): List { val position : MutableList = mutableListOf() var index = content.indexOf(pattern) while(index >= 0){ @@ -197,7 +197,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment return position } - private fun constructText(content: String, position: Int): String{ + private fun constructText(content: String, position: Int): Array{ // 构建关键词周边文字,在搜索结果里显示 // todo: 判断段落,只在关键词所在段落内分割 // todo: 利用标点符号分割完整的句 @@ -211,7 +211,9 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment if (po2 > content.length){ po2 = content.length } - return "..." + content.substring(po1, po2) + val newPosition = position - po1 + val newText = "..." + content.substring(po1, po2) + return arrayOf(newPosition, newText) } val isLocalBook: Boolean diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt index 5b0268026..b60aaebc4 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt @@ -8,11 +8,12 @@ data class SearchResult( var pageSize: Int = 0, var chapterIndex: Int = 0, var pageIndex: Int = 0, + var newPosition: Int = 0 ) { val presentText: String get(){ - val sub1 = text.substring(0, text.indexOf(query)) - val sub2 = text.substring(text.indexOf(query) + query.length, text.length) + val sub1 = text.substring(0, newPosition) + val sub2 = text.substring(newPosition + query.length, text.length) return "$sub1 " + "$query" + "$sub2 " + From f923317146b0c73a1287f5838a5d368825dfb528 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 01:04:08 -0400 Subject: [PATCH 08/21] fix fix icon fix constructedText searchResultCounts update for new search --- .../io/legado/app/ui/book/read/ReadBookActivity.kt | 1 + .../ui/book/searchContent/SearchListFragment.kt | 14 ++++++++------ .../ui/book/searchContent/SearchListViewModel.kt | 4 ++-- .../app/ui/book/searchContent/SearchResult.kt | 4 ++-- app/src/main/res/values-zh-rHK/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/strings.xml | 2 +- 8 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 19a01a8ec..bc6198e83 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -671,6 +671,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo /** * 打开搜索界面 */ + //todo: change request code override fun openSearchList() { ReadBook.book?.let { startActivityForResult( diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index 792bbdb7f..795ef6505 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -99,9 +99,11 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } @SuppressLint("SetTextI18n") - override fun startContentSearch(newText: String?) { - if (!newText.isNullOrBlank()) { + override fun startContentSearch(newText: String) { + // 按章节搜索内容 + if (!newText.isBlank()) { adapter.clearItems() + searchResultCounts = 0 viewModel.lastQuery = newText App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ launch(IO) { @@ -166,7 +168,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment */ positions = searchPosition(bookContent, query) positions?.map{ - val construct = constructText(bookContent, it) + val construct = constructText(bookContent, it, query) val result = SearchResult(index = 0, text = construct[1] as String, chapterTitle = chapter.title, @@ -197,14 +199,14 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment return position } - private fun constructText(content: String, position: Int): Array{ + private fun constructText(content: String, position: Int, query: String): Array{ // 构建关键词周边文字,在搜索结果里显示 // todo: 判断段落,只在关键词所在段落内分割 // todo: 利用标点符号分割完整的句 // todo: length和设置结合,自由调整周边文字长度 val length = 20 var po1 = position - length - var po2 = position + length + var po2 = position + query.length + length if (po1 <0) { po1 = 0 } @@ -212,7 +214,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment po2 = content.length } val newPosition = position - po1 - val newText = "..." + content.substring(po1, po2) + val newText = content.substring(po1, po2) return arrayOf(newPosition, newText) } diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt index 81c223d30..060d74067 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt @@ -21,13 +21,13 @@ class SearchListViewModel(application: Application) : BaseViewModel(application) } } - fun startContentSearch(newText: String?) { + fun startContentSearch(newText: String) { searchCallBack?.startContentSearch(newText) } interface SearchListCallBack { - fun startContentSearch(newText: String?) + fun startContentSearch(newText: String) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt index b60aaebc4..7682a9b5d 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt @@ -14,9 +14,9 @@ data class SearchResult( get(){ val sub1 = text.substring(0, newPosition) val sub2 = text.substring(newPosition + query.length, text.length) - return "$sub1 " + + return "$sub1" + "$query" + - "$sub2 " + + "$sub2" + "($chapterTitle)" } diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index ea85c0c29..c585ffb97 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -761,5 +761,6 @@ 切換默認主題 分享選中書源 時間排序 + 搜索 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index c14a9a485..006b4ff45 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -761,6 +761,7 @@ 切換默認主題 分享選中書源 時間排序 + 搜索 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 036bcd6f9..78487a2be 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -761,5 +761,6 @@ 使用保存主题,导入,分享主题 切换默认主题 时间排序 + 搜索 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 84a7c6c58..893230b30 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -763,6 +763,6 @@ Save, Import, Share theme Share selected sources Sort by update time - Search content + Search \ No newline at end of file From b48484936979da48ac749e8d891d93220e06ffa7 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 01:07:04 -0400 Subject: [PATCH 09/21] Update SearchListFragment.kt --- .../app/ui/book/searchContent/SearchListFragment.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index 795ef6505..1053d02cf 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -122,8 +122,8 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } } val finishedTime = System.currentTimeMillis() - beginTime - Log.d("Jason", "Search finished, the total time cost is $finishedTime") - Log.d("Jason", "Search finished, the total count is $searchResultCounts") + Log.d("h11128", "Search finished, the total time cost is $finishedTime") + Log.d("h11128", "Search finished, the total count is $searchResultCounts") } } } @@ -135,7 +135,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment val searchResults: MutableList = mutableListOf() var positions : List? = listOf() if (chapter != null){ - Log.d("Jason", "Search ${chapter.title}") + Log.d("h11128", "Search ${chapter.title}") viewModel.book?.let { bookSource -> val bookContent = BookHelp.getContent(bookSource, chapter) if (bookContent != null){ @@ -178,13 +178,13 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment newPosition = construct[0] as Int ) searchResults.add(result) - Log.d("Jason", result.presentText) + // Log.d("h11128", result.presentText) } - Log.d("Jason", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}") + Log.d("h11128", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}") } } val endTime = System.currentTimeMillis() - startTime - Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime") + Log.d("h11128", "Search ${chapter.title} finished, the time cost is $endTime") } return searchResults } From e55227a2a9119ca5d0b1aaf28aeb3245522e2f9f Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 14 Sep 2020 20:47:18 +0800 Subject: [PATCH 10/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug,=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=AE=A2=E9=98=85=E4=B8=8B=E8=BD=BD=E5=AE=8C=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=AE=89=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 1 + .../java/io/legado/app/help/IntentHelp.kt | 37 +++-- .../io/legado/app/service/DownloadService.kt | 156 ++++++++++++++++-- .../io/legado/app/service/help/Download.kt | 26 +++ .../ui/book/read/config/BgTextConfigDialog.kt | 4 +- .../legado/app/ui/rss/read/ReadRssActivity.kt | 7 +- 6 files changed, 198 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/io/legado/app/service/help/Download.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ca5b1e976..648bb7323 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ + servicePendingIntent(context: Context, action: String): PendingIntent? { - return PendingIntent.getService( - context, - 0, - Intent(context, T::class.java).apply { this.action = action }, - PendingIntent.FLAG_UPDATE_CURRENT - ) + inline fun servicePendingIntent( + context: Context, + action: String, + bundle: Bundle? = null + ): PendingIntent? { + val intent = Intent(context, T::class.java) + intent.action = action + bundle?.let { + intent.putExtras(bundle) + } + return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) } - inline fun activityPendingIntent(context: Context, action: String): PendingIntent? { - return PendingIntent.getActivity( - context, - 0, - Intent(context, T::class.java).apply { this.action = action }, - PendingIntent.FLAG_UPDATE_CURRENT - ) + inline fun activityPendingIntent( + context: Context, + action: String, + bundle: Bundle? = null + ): PendingIntent? { + val intent = Intent(context, T::class.java) + intent.action = action + bundle?.let { + intent.putExtras(bundle) + } + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt index 7a77bb7a5..5b7522869 100644 --- a/app/src/main/java/io/legado/app/service/DownloadService.kt +++ b/app/src/main/java/io/legado/app/service/DownloadService.kt @@ -1,46 +1,172 @@ package io.legado.app.service +import android.app.DownloadManager +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent +import android.content.IntentFilter +import android.os.Handler import androidx.core.app.NotificationCompat +import androidx.core.os.bundleOf import io.legado.app.R import io.legado.app.base.BaseService import io.legado.app.constant.AppConst import io.legado.app.constant.IntentAction import io.legado.app.help.IntentHelp +import org.jetbrains.anko.downloadManager +import org.jetbrains.anko.notificationManager + class DownloadService : BaseService() { - private val notificationBuilder by lazy { - val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload) - .setSmallIcon(R.drawable.ic_download) - .setOngoing(true) - .setContentTitle(getString(R.string.action_download)) - builder.addAction( - R.drawable.ic_stop_black_24dp, - getString(R.string.cancel), - IntentHelp.servicePendingIntent(this, IntentAction.stop) - ) - builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + private val downloads = hashMapOf() + private val completeDownloads = hashSetOf() + private val handler = Handler() + private val runnable = Runnable { + checkDownloadState() + } + + private val downloadReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + queryState() + } } override fun onCreate() { super.onCreate() - updateNotification("准备下载") + registerReceiver(downloadReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) + } + + override fun onTaskRemoved(rootIntent: Intent?) { + super.onTaskRemoved(rootIntent) + stopSelf() + } + + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(downloadReceiver) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { when (intent?.action) { - + IntentAction.start -> startDownload( + intent.getLongExtra("downloadId", 0), + intent.getStringExtra("fileName") ?: "未知文件" + ) + IntentAction.play -> { + val id = intent.getLongExtra("downloadId", 0) + if (downloads[id]?.endsWith(".apk") == true) { + installApk(id) + } + } + IntentAction.stop -> { + val downloadId = intent.getLongExtra("downloadId", 0) + if (downloadId > 0) { + downloadManager.remove(downloadId) + notificationManager.cancel(downloadId.toInt()) + } else { + stopSelf() + } + } } return super.onStartCommand(intent, flags, startId) } + private fun startDownload(downloadId: Long, fileName: String) { + if (downloadId > 0) { + downloads[downloadId] = fileName + queryState() + checkDownloadState() + } + } + + private fun checkDownloadState() { + handler.removeCallbacks(runnable) + queryState() + handler.postDelayed(runnable, 1000) + } + + //查询下载进度 + private fun queryState() { + val ids = downloads.keys + val query = DownloadManager.Query() + query.setFilterById(*ids.toLongArray()) + val cursor = downloadManager.query(query) + if (!cursor.moveToFirst()) return + val id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)) + val progress: Int = + cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) + val max: Int = + cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) + val status = when (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))) { + DownloadManager.STATUS_PAUSED -> "暂停" + DownloadManager.STATUS_PENDING -> "待下载" + DownloadManager.STATUS_RUNNING -> "下载中" + DownloadManager.STATUS_SUCCESSFUL -> { + if (!completeDownloads.contains(id)) { + completeDownloads.add(id) + if (downloads[id]?.endsWith(".apk") == true) { + installApk(id) + } + } + "下载完成" + } + DownloadManager.STATUS_FAILED -> "下载失败" + else -> "未知状态" + } + updateNotification(id, "${downloads[id]} $status", max, progress) + if (!cursor.isClosed) { + cursor.close() + } + } + + private fun installApk(downloadId: Long) { + val downloadFileUri = downloadManager.getUriForDownloadedFile(downloadId) + downloadFileUri?.let { + val install = Intent(Intent.ACTION_VIEW) + install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive") + install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(install) + } + } + /** * 更新通知 */ - private fun updateNotification(content: String) { + private fun updateNotification(downloadId: Long, content: String, max: Int, progress: Int) { + val notificationBuilder = NotificationCompat.Builder(this, AppConst.channelIdDownload) + .setSmallIcon(R.drawable.ic_download) + .setOngoing(true) + .setContentTitle(getString(R.string.action_download)) + notificationBuilder.setContentIntent( + IntentHelp.servicePendingIntent( + this, + IntentAction.play, + bundleOf("downloadId" to downloadId) + ) + ) + notificationBuilder.addAction( + R.drawable.ic_stop_black_24dp, + getString(R.string.cancel), + IntentHelp.servicePendingIntent( + this, + IntentAction.stop, + bundleOf("downloadId" to downloadId) + ) + ) + notificationBuilder.setDeleteIntent( + IntentHelp.servicePendingIntent( + this, + IntentAction.stop, + bundleOf("downloadId" to downloadId) + ) + ) + notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) notificationBuilder.setContentText(content) + notificationBuilder.setProgress(max, progress, false) + notificationBuilder.setAutoCancel(true) val notification = notificationBuilder.build() - startForeground(AppConst.notificationIdDownload, notification) + startForeground(downloadId.toInt(), notification) } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/service/help/Download.kt b/app/src/main/java/io/legado/app/service/help/Download.kt new file mode 100644 index 000000000..cddbf59b6 --- /dev/null +++ b/app/src/main/java/io/legado/app/service/help/Download.kt @@ -0,0 +1,26 @@ +package io.legado.app.service.help + +import android.content.Context +import android.content.Intent +import io.legado.app.constant.IntentAction +import io.legado.app.service.DownloadService + +object Download { + + fun start(context: Context, downloadId: Long, fileName: String) { + Intent(context, DownloadService::class.java).let { + it.action = IntentAction.start + it.putExtra("downloadId", downloadId) + it.putExtra("fileName", fileName) + context.startService(it) + } + } + + fun stop(context: Context) { + Intent(context, DownloadService::class.java).let { + it.action = IntentAction.stop + context.startService(it) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt index 841a5b9a9..b8d71b515 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt @@ -324,7 +324,9 @@ class BgTextConfigDialog : BaseDialogFragment(), FileChooserDialog.CallBack { val fontName = FileUtils.getName(config.textFont) val fontPath = FileUtils.getPath(requireContext().externalFilesDir, "font", fontName) - FileUtils.getFile(configDir, fontName).copyTo(File(fontPath)) + if (!FileUtils.exist(fontPath)) { + FileUtils.getFile(configDir, fontName).copyTo(File(fontPath)) + } config.textFont = fontPath } if (config.bgType() == 2) { diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index e2834e456..c66cad69f 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -15,12 +15,14 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.lib.theme.DrawableUtils import io.legado.app.lib.theme.primaryTextColor +import io.legado.app.service.help.Download import io.legado.app.ui.filechooser.FileChooserDialog import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.* import kotlinx.android.synthetic.main.activity_rss_read.* import kotlinx.coroutines.launch import org.apache.commons.text.StringEscapeUtils +import org.jetbrains.anko.downloadManager import org.jetbrains.anko.share import org.jsoup.Jsoup @@ -160,13 +162,12 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r request.setAllowedOverRoaming(true) // 允许下载的网路类型 request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI) - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN) // 设置下载文件保存的路径和文件名 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) - val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager // 添加一个下载任务 val downloadId = downloadManager.enqueue(request) - print(downloadId) + Download.start(this, downloadId, fileName) } } } From e15ae6070294e48b0f4aeb766bc7b3f72a391053 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 14 Sep 2020 20:51:42 +0800 Subject: [PATCH 11/21] update log --- app/src/main/assets/updateLog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index e9ab31cb0..1bd81e93d 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,6 +3,9 @@ * 关注合作公众号 **[小说拾遗]()** 获取好看的小说。 - 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2020/09/15** +* 修复导入排版字体重复报错的bug + **2020/09/12** * web看书同步最新章 * web写源增加图片样式等规则 From 9cb35f74fce8e3427df0b06b41fd64abc25b17bc Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 14 Sep 2020 21:22:58 +0800 Subject: [PATCH 12/21] update log --- .../io/legado/app/service/DownloadService.kt | 88 ++++++++++++------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt index 5b7522869..c069ab6c4 100644 --- a/app/src/main/java/io/legado/app/service/DownloadService.kt +++ b/app/src/main/java/io/legado/app/service/DownloadService.kt @@ -5,16 +5,24 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.net.Uri +import android.os.Build import android.os.Handler import androidx.core.app.NotificationCompat +import androidx.core.content.FileProvider import androidx.core.os.bundleOf +import io.legado.app.BuildConfig import io.legado.app.R import io.legado.app.base.BaseService import io.legado.app.constant.AppConst import io.legado.app.constant.IntentAction import io.legado.app.help.IntentHelp +import io.legado.app.utils.RealPathUtil +import io.legado.app.utils.msg import org.jetbrains.anko.downloadManager import org.jetbrains.anko.notificationManager +import org.jetbrains.anko.toast +import java.io.File class DownloadService : BaseService() { @@ -91,42 +99,62 @@ class DownloadService : BaseService() { val ids = downloads.keys val query = DownloadManager.Query() query.setFilterById(*ids.toLongArray()) - val cursor = downloadManager.query(query) - if (!cursor.moveToFirst()) return - val id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)) - val progress: Int = - cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) - val max: Int = - cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) - val status = when (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))) { - DownloadManager.STATUS_PAUSED -> "暂停" - DownloadManager.STATUS_PENDING -> "待下载" - DownloadManager.STATUS_RUNNING -> "下载中" - DownloadManager.STATUS_SUCCESSFUL -> { - if (!completeDownloads.contains(id)) { - completeDownloads.add(id) - if (downloads[id]?.endsWith(".apk") == true) { - installApk(id) + downloadManager.query(query).use { cursor -> + if (!cursor.moveToFirst()) return + val id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)) + val progress: Int = + cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) + val max: Int = + cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) + val status = + when (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))) { + DownloadManager.STATUS_PAUSED -> "暂停" + DownloadManager.STATUS_PENDING -> "待下载" + DownloadManager.STATUS_RUNNING -> "下载中" + DownloadManager.STATUS_SUCCESSFUL -> { + if (!completeDownloads.contains(id)) { + completeDownloads.add(id) + if (downloads[id]?.endsWith(".apk") == true) { + installApk(id) + } + } + "下载完成" } + DownloadManager.STATUS_FAILED -> "下载失败" + else -> "未知状态" } - "下载完成" - } - DownloadManager.STATUS_FAILED -> "下载失败" - else -> "未知状态" - } - updateNotification(id, "${downloads[id]} $status", max, progress) - if (!cursor.isClosed) { - cursor.close() + updateNotification(id, "${downloads[id]} $status", max, progress) } } private fun installApk(downloadId: Long) { - val downloadFileUri = downloadManager.getUriForDownloadedFile(downloadId) - downloadFileUri?.let { - val install = Intent(Intent.ACTION_VIEW) - install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive") - install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - startActivity(install) + downloadManager.getUriForDownloadedFile(downloadId)?.let { + val filePath = RealPathUtil.getPath(this, it) ?: return + val file = File(filePath) + //调用系统安装apk + val intent = Intent() + intent.action = Intent.ACTION_VIEW + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //7.0版本以上 + val uriForFile: Uri = + FileProvider.getUriForFile( + this, + "${BuildConfig.APPLICATION_ID}.fileProvider", + file + ) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.setDataAndType(uriForFile, "application/vnd.android.package-archive") + } else { + val uri: Uri = Uri.fromFile(file) + intent.setDataAndType(uri, "application/vnd.android.package-archive") + } + + try { + startActivity(intent) + } catch (e: Exception) { + toast(e.msg) + } } } From 56fc9d2b959092a256324f04829643d57cbdaa03 Mon Sep 17 00:00:00 2001 From: gedoor Date: Mon, 14 Sep 2020 21:52:05 +0800 Subject: [PATCH 13/21] update log --- app/src/main/java/io/legado/app/service/DownloadService.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt index c069ab6c4..53868a977 100644 --- a/app/src/main/java/io/legado/app/service/DownloadService.kt +++ b/app/src/main/java/io/legado/app/service/DownloadService.kt @@ -20,7 +20,6 @@ import io.legado.app.help.IntentHelp import io.legado.app.utils.RealPathUtil import io.legado.app.utils.msg import org.jetbrains.anko.downloadManager -import org.jetbrains.anko.notificationManager import org.jetbrains.anko.toast import java.io.File @@ -69,10 +68,8 @@ class DownloadService : BaseService() { } IntentAction.stop -> { val downloadId = intent.getLongExtra("downloadId", 0) - if (downloadId > 0) { - downloadManager.remove(downloadId) - notificationManager.cancel(downloadId.toInt()) - } else { + downloads.remove(downloadId) + if (downloads.isEmpty()) { stopSelf() } } From 435c63f732d53ae08159d048dcf53306ffd78f30 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 18:52:20 -0400 Subject: [PATCH 14/21] update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 取消并发搜索,改成顺序搜索 点击打开相应页面 --- .../io/legado/app/service/help/ReadBook.kt | 43 +++++++++++++ .../app/ui/book/read/ReadBookActivity.kt | 27 ++++++++- .../book/searchContent/SearchListFragment.kt | 60 ++++++++++--------- .../app/ui/book/searchContent/SearchResult.kt | 7 ++- 4 files changed, 107 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt index e8485261c..5bdcaa25e 100644 --- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt +++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt @@ -1,5 +1,6 @@ package io.legado.app.service.help +import android.util.Log import androidx.lifecycle.MutableLiveData import com.hankcs.hanlp.HanLP import io.legado.app.App @@ -16,6 +17,7 @@ import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.webBook.WebBook import io.legado.app.service.BaseReadAloudService import io.legado.app.ui.book.read.page.entities.TextChapter +import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ImageProvider import kotlinx.coroutines.Dispatchers @@ -324,6 +326,46 @@ object ReadBook { } } + fun searchResultPositions(pages: List, contentPosition: Int): Array{ + // + // calculate search result's pageIndex + var pageIndex = 0 + var length = pages[pageIndex].text.length + Log.d("h11128", "page size ${pages.size}") + while (length < contentPosition){ + pageIndex += 1 + if (pageIndex >pages.size){ + pageIndex = pages.size + break + } + length += pages[pageIndex].text.length + Log.d("h11128", "to next page , add length change to $length") + } + + Log.d("h11128", "at pageindex $pageIndex") + + // calculate search result's lineIndex + val currentPage = pages[pageIndex] + var lineIndex = 0 + length = length - currentPage.text.length + currentPage.textLines[lineIndex].text.length + while (length < contentPosition){ + lineIndex += 1 + if (lineIndex >currentPage.textLines.size){ + lineIndex = currentPage.textLines.size + break + } + length += currentPage.textLines[lineIndex].text.length + + } + + // charIndex + val currentLine = currentPage.textLines[lineIndex] + length -= currentLine.text.length + val charIndex = contentPosition - length + + return arrayOf(pageIndex, lineIndex, charIndex) + } + /** * 内容加载完成 */ @@ -426,4 +468,5 @@ object ReadBook { fun pageChanged() fun contentLoadFinish() } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index bc6198e83..5b4991c37 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -9,6 +9,7 @@ import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Bundle import android.os.Handler +import android.util.Log import android.view.* import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.core.view.get @@ -48,6 +49,7 @@ import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.TextPageFactory import io.legado.app.ui.book.read.page.delegate.PageDelegate import io.legado.app.ui.book.searchContent.SearchListActivity +import io.legado.app.ui.book.searchContent.SearchResult import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.login.SourceLogin import io.legado.app.ui.replacerule.ReplaceRuleActivity @@ -80,6 +82,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo private val requestCodeChapterList = 568 private val requestCodeEditSource = 111 private val requestCodeReplace = 312 + private val requestCodeSearchResult = 123 private var menu: Menu? = null private var textActionMenu: TextActionMenu? = null @@ -675,7 +678,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo override fun openSearchList() { ReadBook.book?.let { startActivityForResult( - requestCodeChapterList, + requestCodeSearchResult, Pair("bookUrl", it.bookUrl) ) } @@ -761,11 +764,31 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo requestCodeChapterList -> data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index -> if (index != ReadBook.durChapterIndex) { - viewModel.openChapter(index) + val pageIndex = data.getIntExtra("pageIndex", 0) + viewModel.openChapter(index, pageIndex) + } + } + requestCodeSearchResult -> + data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index -> + val contentPosition = data.getIntExtra("contentPosition", 0) + val query = data.getStringExtra("query") + ReadBook.durChapterIndex = index + ReadBook.saveRead() + ReadBook.loadContent(resetPageOffset = true) + val pages = ReadBook.curTextChapter?.pages + if (pages != null){ + val positions = ReadBook.searchResultPositions(pages, contentPosition) + viewModel.openChapter(index, positions[0]) + Log.d("h11128", positions[0].toString()) + Log.d("h11128", positions[1].toString()) + Log.d("h11128", positions[2].toString()) + //page_view.curPage.selectStartMoveIndex(positions[0], positions[1], 0) + //page_view.curPage.selectEndMoveIndex(positions[0], positions[1], 0 + query!!.length ) } } requestCodeReplace -> onReplaceRuleSave() } + } } diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index 1053d02cf..a52cfac6a 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -16,6 +16,9 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.getPrimaryTextColor +import io.legado.app.service.help.ReadBook +import io.legado.app.ui.book.read.page.entities.TextPage +import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.widget.recycler.UpLinearLayoutManager import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.utils.ColorUtils @@ -37,6 +40,8 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment lateinit var adapter: SearchListAdapter private lateinit var mLayoutManager: UpLinearLayoutManager private var searchResultCounts = 0 + private var pageSize = 0 + private var searchResultList: MutableList = mutableListOf() override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { viewModel.searchCallBack = this @@ -103,39 +108,36 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment // 按章节搜索内容 if (!newText.isBlank()) { adapter.clearItems() + searchResultList.clear() searchResultCounts = 0 viewModel.lastQuery = newText - App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ - launch(IO) { - val beginTime = System.currentTimeMillis() - if (isLocalBook - || adapter.cacheFileNames.contains(BookHelp.formatChapterName(it)) - ) { - val searchResults = searchChapter(newText, it) - if (searchResults.size > 0 ){ - searchResultCounts += searchResults.size - - withContext(Main){ - tv_current_search_info.text = "搜索结果:$searchResultCounts" - adapter.addItems(searchResults) - } + var searchResults = listOf() + launch(Main){ + App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ chapter -> + val job = async(IO){ + if (isLocalBook || adapter.cacheFileNames.contains(BookHelp.formatChapterName(chapter))) { + searchResults = searchChapter(newText, chapter) + Log.d("h11128", "find ${searchResults.size} results in chapter ${chapter.title}") } } - val finishedTime = System.currentTimeMillis() - beginTime - Log.d("h11128", "Search finished, the total time cost is $finishedTime") - Log.d("h11128", "Search finished, the total count is $searchResultCounts") + job.await() + if (searchResults.isNotEmpty()){ + Log.d("h11128", "load ${searchResults.size} results in chapter ${chapter.title}") + searchResultList.addAll(searchResults) + tv_current_search_info.text = "搜索结果:$searchResultCounts" + Log.d("h11128", "searchResultList size ${searchResultList.size}") + adapter.addItems(searchResults) + searchResults = listOf() + } } } } - } - private fun searchChapter(query: String, chapter: BookChapter?): MutableList { - val startTime = System.currentTimeMillis() + private fun searchChapter(query: String, chapter: BookChapter?): List { val searchResults: MutableList = mutableListOf() var positions : List? = listOf() if (chapter != null){ - Log.d("h11128", "Search ${chapter.title}") viewModel.book?.let { bookSource -> val bookContent = BookHelp.getContent(bookSource, chapter) if (bookContent != null){ @@ -169,22 +171,21 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment positions = searchPosition(bookContent, query) positions?.map{ val construct = constructText(bookContent, it, query) - val result = SearchResult(index = 0, + val result = SearchResult(index = searchResultCounts, text = construct[1] as String, chapterTitle = chapter.title, query = query, pageIndex = 0, //todo: 计算搜索结果所在的pageIndex直接跳转 chapterIndex = chapter.index, - newPosition = construct[0] as Int + newPosition = construct[0] as Int, + contentPosition = it ) + searchResultCounts += 1 searchResults.add(result) // Log.d("h11128", result.presentText) } - Log.d("h11128", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}") } } - val endTime = System.currentTimeMillis() - startTime - Log.d("h11128", "Search ${chapter.title} finished, the time cost is $endTime") } return searchResults } @@ -222,11 +223,16 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment get() = viewModel.book?.isLocalBook() == true override fun openSearchResult(searchResult: SearchResult) { + val searchData = Intent() searchData.putExtra("index", searchResult.chapterIndex) - searchData.putExtra("pageIndex", searchResult.pageIndex) + searchData.putExtra("contentPosition", searchResult.contentPosition) + searchData.putExtra("query", searchResult.query) + Log.d("h11128","current chapter index ${searchResult.chapterIndex}") activity?.setResult(RESULT_OK, searchData) activity?.finish() + + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt index 7682a9b5d..fd1873464 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt @@ -1,5 +1,8 @@ package io.legado.app.ui.book.searchContent +import android.util.Log +import io.legado.app.ui.book.read.page.entities.TextPage + data class SearchResult( var index: Int = 0, var text: String = "", @@ -8,7 +11,8 @@ data class SearchResult( var pageSize: Int = 0, var chapterIndex: Int = 0, var pageIndex: Int = 0, - var newPosition: Int = 0 + var newPosition: Int = 0, + var contentPosition: Int =0 ) { val presentText: String get(){ @@ -20,4 +24,5 @@ data class SearchResult( "($chapterTitle)" } + } \ No newline at end of file From 04088a5dc14e101856b83f47b6fc019640153ca2 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 21:10:26 -0400 Subject: [PATCH 15/21] updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 搜索替换后正文 粗体当前章节搜索结果 跳转搜索结果对应页面 --- .../io/legado/app/service/help/ReadBook.kt | 22 +++++- .../app/ui/book/read/ReadBookActivity.kt | 37 +++++++--- .../book/searchContent/SearchListAdapter.kt | 8 ++- .../book/searchContent/SearchListFragment.kt | 67 ++++++++++--------- .../app/ui/book/searchContent/SearchResult.kt | 22 ++++-- 5 files changed, 105 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt index 5bdcaa25e..dfa337ae7 100644 --- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt +++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt @@ -326,9 +326,21 @@ object ReadBook { } } - fun searchResultPositions(pages: List, contentPosition: Int): Array{ + fun searchResultPositions(pages: List, indexWithinChapter: Int, query: String): Array{ // // calculate search result's pageIndex + var content = "" + pages.map{ + content+= it.text + } + var count = 1 + var index = content.indexOf(query) + while(count != indexWithinChapter){ + index = content.indexOf(query, index + 1); + count += 1 + } + Log.d("h11128", "new index $index") + val contentPosition = index var pageIndex = 0 var length = pages[pageIndex].text.length Log.d("h11128", "page size ${pages.size}") @@ -348,6 +360,7 @@ object ReadBook { val currentPage = pages[pageIndex] var lineIndex = 0 length = length - currentPage.text.length + currentPage.textLines[lineIndex].text.length + Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}") while (length < contentPosition){ lineIndex += 1 if (lineIndex >currentPage.textLines.size){ @@ -355,14 +368,17 @@ object ReadBook { break } length += currentPage.textLines[lineIndex].text.length - + Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}") } // charIndex + Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}") + Log.d("h11128", "currentLength $length") val currentLine = currentPage.textLines[lineIndex] length -= currentLine.text.length + Log.d("h11128", "currentLength $length") val charIndex = contentPosition - length - + Log.d("h11128", "contentLength $contentPosition") return arrayOf(pageIndex, lineIndex, charIndex) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 5b4991c37..fc7e262af 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -60,6 +60,9 @@ import kotlinx.android.synthetic.main.activity_book_read.* import kotlinx.android.synthetic.main.view_read_menu.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.async +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.startActivity @@ -100,6 +103,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo override var isAutoPage = false private var screenTimeOut: Long = 0 private var timeBatteryReceiver: TimeBatteryReceiver? = null + private var loadStates: Boolean = false override val pageFactory: TextPageFactory get() = page_view.pageFactory override val headerHeight: Int get() = page_view.curPage.headerHeight @@ -536,6 +540,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo intent.removeExtra("readAloud") ReadBook.readAloud() } + loadStates = true } /** @@ -547,6 +552,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo page_view.upContent(relativePosition, resetPageOffset) seek_read_page.progress = ReadBook.durPageIndex } + loadStates = false } /** @@ -770,20 +776,29 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo } requestCodeSearchResult -> data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index -> - val contentPosition = data.getIntExtra("contentPosition", 0) - val query = data.getStringExtra("query") - ReadBook.durChapterIndex = index - ReadBook.saveRead() - ReadBook.loadContent(resetPageOffset = true) - val pages = ReadBook.curTextChapter?.pages - if (pages != null){ - val positions = ReadBook.searchResultPositions(pages, contentPosition) - viewModel.openChapter(index, positions[0]) + launch(IO){ + val indexWithinChapter = data.getIntExtra("indexWithinChapter", 0) + val query = data.getStringExtra("query") + viewModel.openChapter(index) + + // block until load correct chapter and pages + while (ReadBook.durChapterIndex != index || ReadBook.curTextChapter?.pages == null ){ + delay(100L) + } + val pages = ReadBook.curTextChapter?.pages + Log.d("Jason", "Current chapter pages ${pages!!.size}") + val positions = ReadBook.searchResultPositions(pages, indexWithinChapter, query!!) Log.d("h11128", positions[0].toString()) Log.d("h11128", positions[1].toString()) Log.d("h11128", positions[2].toString()) - //page_view.curPage.selectStartMoveIndex(positions[0], positions[1], 0) - //page_view.curPage.selectEndMoveIndex(positions[0], positions[1], 0 + query!!.length ) + //todo: show selected text + val job1 = async(Main){ + ReadBook.skipToPage(positions[0]) + page_view.curPage.selectStartMoveIndex(positions[0], positions[1], 0) + page_view.curPage.selectEndMoveIndex(positions[0], positions[1], 0 + query.length ) + page_view.isTextSelected = true + } + job1.await() } } requestCodeReplace -> onReplaceRuleSave() diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt index 8f8a83a88..d2c98b955 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt @@ -15,6 +15,7 @@ import io.legado.app.help.BookHelp import io.legado.app.lib.theme.accentColor import io.legado.app.utils.getCompatColor import io.legado.app.utils.visible +import kotlinx.android.synthetic.main.item_bookmark.view.* import kotlinx.android.synthetic.main.item_search_list.view.* import org.jetbrains.anko.sdk27.listeners.onClick @@ -25,8 +26,12 @@ class SearchListAdapter(context: Context, val callback: Callback) : override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList) { with(holder.itemView) { + val isDur = callback.durChapterIndex() == item.chapterIndex if (payloads.isEmpty()) { - tv_search_result.text = HtmlCompat.fromHtml(item.presentText, HtmlCompat.FROM_HTML_MODE_LEGACY) + tv_search_result.text = item.parseText(item.presentText) + if (isDur){ + tv_search_result.paint.isFakeBoldText = true + } } } } @@ -41,5 +46,6 @@ class SearchListAdapter(context: Context, val callback: Callback) : interface Callback { fun openSearchResult(searchResult: SearchResult) + fun durChapterIndex(): Int } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index a52cfac6a..646153b6c 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -7,12 +7,14 @@ import android.os.Bundle import android.util.Log import android.view.View import androidx.lifecycle.LiveData +import com.hankcs.hanlp.HanLP import io.legado.app.App import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.EventBus import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter +import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.getPrimaryTextColor @@ -40,7 +42,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment lateinit var adapter: SearchListAdapter private lateinit var mLayoutManager: UpLinearLayoutManager private var searchResultCounts = 0 - private var pageSize = 0 + private var durChapterIndex = 0 private var searchResultList: MutableList = mutableListOf() override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { @@ -76,9 +78,11 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment @SuppressLint("SetTextI18n") private fun initBook() { launch { + tv_current_search_info.text = "搜索结果:$searchResultCounts" viewModel.book?.let { initCacheFileNames(it) + durChapterIndex = it.durChapterIndex } } } @@ -117,15 +121,15 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment val job = async(IO){ if (isLocalBook || adapter.cacheFileNames.contains(BookHelp.formatChapterName(chapter))) { searchResults = searchChapter(newText, chapter) - Log.d("h11128", "find ${searchResults.size} results in chapter ${chapter.title}") + //Log.d("h11128", "find ${searchResults.size} results in chapter ${chapter.title}") } } job.await() if (searchResults.isNotEmpty()){ - Log.d("h11128", "load ${searchResults.size} results in chapter ${chapter.title}") + //Log.d("h11128", "load ${searchResults.size} results in chapter ${chapter.title}") searchResultList.addAll(searchResults) tv_current_search_info.text = "搜索结果:$searchResultCounts" - Log.d("h11128", "searchResultList size ${searchResultList.size}") + //Log.d("h11128", "searchResultList size ${searchResultList.size}") adapter.addItems(searchResults) searchResults = listOf() } @@ -134,55 +138,51 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } } - private fun searchChapter(query: String, chapter: BookChapter?): List { + private suspend fun searchChapter(query: String, chapter: BookChapter?): List { val searchResults: MutableList = mutableListOf() - var positions : List? = listOf() + var positions : List = listOf() + var replaceContents: List? = null + var totalContents = "" if (chapter != null){ viewModel.book?.let { bookSource -> val bookContent = BookHelp.getContent(bookSource, chapter) if (bookContent != null){ - //todo: 搜索替换后的正文句子列表 - /* - chapter.title = when (AppConfig.chineseConverterType) { - 1 -> HanLP.convertToSimplifiedChinese(chapter.title) - 2 -> HanLP.convertToTraditionalChinese(chapter.title) - else -> chapter.title - } - var replaceContents: List? = null - bookContent?.let { + //搜索替换后的正文 + val job = async(IO) { + chapter.title = when (AppConfig.chineseConverterType) { + 1 -> HanLP.convertToSimplifiedChinese(chapter.title) + 2 -> HanLP.convertToTraditionalChinese(chapter.title) + else -> chapter.title + } replaceContents = BookHelp.disposeContent( chapter.title, bookSource.name, bookSource.bookUrl, - it, + bookContent, bookSource.useReplaceRule ) } - - replaceContents?.map { - async(IO) { - if (it.contains(query)) { - Log.d("targetd contents", it) - searchResult.add(it) - } - } - }?.awaitAll() - */ - positions = searchPosition(bookContent, query) - positions?.map{ - val construct = constructText(bookContent, it, query) + job.await() + while (replaceContents == null){ + delay(100L) + } + totalContents = replaceContents!!.joinToString("") + positions = searchPosition(totalContents, query) + var count = 1 + positions.map{ + val construct = constructText(totalContents, it, query) val result = SearchResult(index = searchResultCounts, + indexWithinChapter = count, text = construct[1] as String, chapterTitle = chapter.title, query = query, - pageIndex = 0, //todo: 计算搜索结果所在的pageIndex直接跳转 chapterIndex = chapter.index, newPosition = construct[0] as Int, contentPosition = it ) + count += 1 searchResultCounts += 1 searchResults.add(result) - // Log.d("h11128", result.presentText) } } } @@ -228,6 +228,7 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment searchData.putExtra("index", searchResult.chapterIndex) searchData.putExtra("contentPosition", searchResult.contentPosition) searchData.putExtra("query", searchResult.query) + searchData.putExtra("indexWithinChapter", searchResult.indexWithinChapter) Log.d("h11128","current chapter index ${searchResult.chapterIndex}") activity?.setResult(RESULT_OK, searchData) activity?.finish() @@ -235,4 +236,8 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment } + override fun durChapterIndex(): Int { + return durChapterIndex + } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt index fd1873464..6b0d9df5b 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt @@ -1,10 +1,13 @@ package io.legado.app.ui.book.searchContent +import android.text.Spanned import android.util.Log +import androidx.core.text.HtmlCompat import io.legado.app.ui.book.read.page.entities.TextPage data class SearchResult( var index: Int = 0, + var indexWithinChapter: Int = 0, var text: String = "", var chapterTitle: String = "", val query: String, @@ -16,13 +19,22 @@ data class SearchResult( ) { val presentText: String get(){ - val sub1 = text.substring(0, newPosition) - val sub2 = text.substring(newPosition + query.length, text.length) - return "$sub1" + - "$query" + - "$sub2" + + return colorPresentText(newPosition, query, text) + "($chapterTitle)" } + fun colorPresentText(position: Int, center: String, targetText: String): String{ + val sub1 = text.substring(0, position) + val sub2 = text.substring(position + center.length, targetText.length) + return "$sub1" + + "$center" + + "$sub2" + } + + fun parseText(targetText: String): Spanned { + return HtmlCompat.fromHtml(targetText, HtmlCompat.FROM_HTML_MODE_LEGACY) + } + + } \ No newline at end of file From ce09240e4f71fc1aba11e124c75fe6c2458e38da Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 21:12:13 -0400 Subject: [PATCH 16/21] delete log --- .../main/java/io/legado/app/service/help/ReadBook.kt | 11 ----------- .../io/legado/app/ui/book/read/ReadBookActivity.kt | 5 ----- .../app/ui/book/searchContent/SearchListFragment.kt | 4 ---- 3 files changed, 20 deletions(-) diff --git a/app/src/main/java/io/legado/app/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt index dfa337ae7..2e96221f3 100644 --- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt +++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt @@ -339,11 +339,9 @@ object ReadBook { index = content.indexOf(query, index + 1); count += 1 } - Log.d("h11128", "new index $index") val contentPosition = index var pageIndex = 0 var length = pages[pageIndex].text.length - Log.d("h11128", "page size ${pages.size}") while (length < contentPosition){ pageIndex += 1 if (pageIndex >pages.size){ @@ -351,16 +349,12 @@ object ReadBook { break } length += pages[pageIndex].text.length - Log.d("h11128", "to next page , add length change to $length") } - Log.d("h11128", "at pageindex $pageIndex") - // calculate search result's lineIndex val currentPage = pages[pageIndex] var lineIndex = 0 length = length - currentPage.text.length + currentPage.textLines[lineIndex].text.length - Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}") while (length < contentPosition){ lineIndex += 1 if (lineIndex >currentPage.textLines.size){ @@ -368,17 +362,12 @@ object ReadBook { break } length += currentPage.textLines[lineIndex].text.length - Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}") } // charIndex - Log.d("h11128", "currentLine ${currentPage.textLines[lineIndex].text}") - Log.d("h11128", "currentLength $length") val currentLine = currentPage.textLines[lineIndex] length -= currentLine.text.length - Log.d("h11128", "currentLength $length") val charIndex = contentPosition - length - Log.d("h11128", "contentLength $contentPosition") return arrayOf(pageIndex, lineIndex, charIndex) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index fc7e262af..563a6f27c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -780,17 +780,12 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo val indexWithinChapter = data.getIntExtra("indexWithinChapter", 0) val query = data.getStringExtra("query") viewModel.openChapter(index) - // block until load correct chapter and pages while (ReadBook.durChapterIndex != index || ReadBook.curTextChapter?.pages == null ){ delay(100L) } val pages = ReadBook.curTextChapter?.pages - Log.d("Jason", "Current chapter pages ${pages!!.size}") val positions = ReadBook.searchResultPositions(pages, indexWithinChapter, query!!) - Log.d("h11128", positions[0].toString()) - Log.d("h11128", positions[1].toString()) - Log.d("h11128", positions[2].toString()) //todo: show selected text val job1 = async(Main){ ReadBook.skipToPage(positions[0]) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt index 646153b6c..38ac013df 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt @@ -121,15 +121,12 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment val job = async(IO){ if (isLocalBook || adapter.cacheFileNames.contains(BookHelp.formatChapterName(chapter))) { searchResults = searchChapter(newText, chapter) - //Log.d("h11128", "find ${searchResults.size} results in chapter ${chapter.title}") } } job.await() if (searchResults.isNotEmpty()){ - //Log.d("h11128", "load ${searchResults.size} results in chapter ${chapter.title}") searchResultList.addAll(searchResults) tv_current_search_info.text = "搜索结果:$searchResultCounts" - //Log.d("h11128", "searchResultList size ${searchResultList.size}") adapter.addItems(searchResults) searchResults = listOf() } @@ -229,7 +226,6 @@ class SearchListFragment : VMBaseFragment(R.layout.fragment searchData.putExtra("contentPosition", searchResult.contentPosition) searchData.putExtra("query", searchResult.query) searchData.putExtra("indexWithinChapter", searchResult.indexWithinChapter) - Log.d("h11128","current chapter index ${searchResult.chapterIndex}") activity?.setResult(RESULT_OK, searchData) activity?.finish() From c32e02e56a48511a59726a19169d50490659d908 Mon Sep 17 00:00:00 2001 From: Jason Yao Date: Mon, 14 Sep 2020 21:32:05 -0400 Subject: [PATCH 17/21] Update ReadBookActivity.kt --- .../main/java/io/legado/app/ui/book/read/ReadBookActivity.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 563a6f27c..7120e047a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -781,10 +781,11 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo val query = data.getStringExtra("query") viewModel.openChapter(index) // block until load correct chapter and pages - while (ReadBook.durChapterIndex != index || ReadBook.curTextChapter?.pages == null ){ + var pages = ReadBook.curTextChapter?.pages + while (ReadBook.durChapterIndex != index || pages == null ){ delay(100L) + pages = ReadBook.curTextChapter?.pages } - val pages = ReadBook.curTextChapter?.pages val positions = ReadBook.searchResultPositions(pages, indexWithinChapter, query!!) //todo: show selected text val job1 = async(Main){ From 112b7b081ecfc178c45036ba181422e4623b0c4e Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Sep 2020 09:49:31 +0800 Subject: [PATCH 18/21] update log --- app/src/main/assets/updateLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 1bd81e93d..40348af60 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -5,6 +5,7 @@ **2020/09/15** * 修复导入排版字体重复报错的bug +* 添加正文搜索 by [h11128](https://github.com/h11128) **2020/09/12** * web看书同步最新章 From aabb7749e1a2b1b09de7b9842bc77b9a64f507df Mon Sep 17 00:00:00 2001 From: fisher Date: Tue, 15 Sep 2020 12:06:14 +0800 Subject: [PATCH 19/21] =?UTF-8?q?=E4=BC=98=E5=8C=96txt=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/txtTocRule.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/assets/txtTocRule.json b/app/src/main/assets/txtTocRule.json index 11131b334..127f5648f 100644 --- a/app/src/main/assets/txtTocRule.json +++ b/app/src/main/assets/txtTocRule.json @@ -2,15 +2,15 @@ { "id": -1, "enable": true, - "name": "目录", - "rule": "^[  \\t]{0,4}(?:序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?![分赛游])|篇(?!张))).{0,30}$", + "name": "目录(去空白)", + "rule": "(?<=[ \\s])(?:序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?![分赛游])|篇(?!张))).{0,30}$", "serialNumber": 0 }, { "id": -2, "enable": true, - "name": "目录(去空白)", - "rule": "(?<=[ \\s])(?:序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?![分赛游])|篇(?!张))).{0,30}$", + "name": "目录", + "rule": "^[  \\t]{0,4}(?:序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?![分赛游])|篇(?!张))).{0,30}$", "serialNumber": 1 }, { From 65190d6fceb3ee7e72a5e8606e59f679d2eed6c0 Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Sep 2020 22:26:56 +0800 Subject: [PATCH 20/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E9=87=8C=E7=9A=84@get:{key}=E5=92=8C{{js}}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/model/analyzeRule/AnalyzeRule.kt | 32 ++++++++++++++++--- .../app/model/analyzeRule/AnalyzeUrl.kt | 2 +- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index d196e18fa..f9ed9230f 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -324,17 +324,37 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions { */ private fun replaceRegex(result: String, rule: SourceRule): String { var vResult = result - if (rule.replaceRegex.isNotEmpty()) { + val stringBuffer = StringBuffer() + val evalMatcher = replacePattern.matcher(rule.replaceRegex) + while (evalMatcher.find()) { + val jsEval = evalMatcher.group().let { + if (it.startsWith("@get:", true)) { + get(it.substring(6, it.lastIndex)) + } else { + evalJS(it.substring(2, it.length - 2), result) + } + } ?: "" + if (jsEval is String) { + evalMatcher.appendReplacement(stringBuffer, jsEval) + } else if (jsEval is Double && jsEval % 1.0 == 0.0) { + evalMatcher.appendReplacement(stringBuffer, String.format("%.0f", jsEval)) + } else { + evalMatcher.appendReplacement(stringBuffer, jsEval.toString()) + } + } + evalMatcher.appendTail(stringBuffer) + val replaceRegex = stringBuffer.toString() + if (replaceRegex.isNotEmpty()) { vResult = if (rule.replaceFirst) { - val pattern = Pattern.compile(rule.replaceRegex) + val pattern = Pattern.compile(replaceRegex) val matcher = pattern.matcher(vResult) if (matcher.find()) { - matcher.group(0)!!.replaceFirst(rule.replaceRegex.toRegex(), rule.replacement) + matcher.group(0)!!.replaceFirst(replaceRegex.toRegex(), rule.replacement) } else { "" } } else { - vResult.replace(rule.replaceRegex.toRegex(), rule.replacement) + vResult.replace(replaceRegex.toRegex(), rule.replacement) } } return vResult @@ -644,6 +664,10 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions { "@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}|\\$\\d{1,2}", Pattern.CASE_INSENSITIVE ) + private val replacePattern = Pattern.compile( + "@get:\\{[^}]+?\\}|\\{\\{[\\w\\W]*?\\}\\}", + Pattern.CASE_INSENSITIVE + ) } } diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 396df397e..067690afb 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -150,7 +150,7 @@ class AnalyzeUrl( //js if (ruleUrl.contains("{{") && ruleUrl.contains("}}")) { var jsEval: Any - val sb = StringBuffer(ruleUrl.length) + val sb = StringBuffer() val simpleBindings = SimpleBindings() simpleBindings["java"] = this simpleBindings["baseUrl"] = baseUrl From 0bffc3e4f32d49bd95ebc849c3244fe144d48b3d Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 15 Sep 2020 22:38:50 +0800 Subject: [PATCH 21/21] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=96=87=E5=AD=97=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/searchContent/SearchResult.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt index 6b0d9df5b..3a629839b 100644 --- a/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt +++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt @@ -1,9 +1,11 @@ package io.legado.app.ui.book.searchContent import android.text.Spanned -import android.util.Log import androidx.core.text.HtmlCompat -import io.legado.app.ui.book.read.page.entities.TextPage +import io.legado.app.App +import io.legado.app.R +import io.legado.app.utils.getCompatColor +import io.legado.app.utils.hexString data class SearchResult( var index: Int = 0, @@ -23,12 +25,13 @@ data class SearchResult( "($chapterTitle)" } - fun colorPresentText(position: Int, center: String, targetText: String): String{ + fun colorPresentText(position: Int, center: String, targetText: String): String { val sub1 = text.substring(0, position) val sub2 = text.substring(position + center.length, targetText.length) - return "$sub1" + + val textColor = App.INSTANCE.getCompatColor(R.color.primaryText).hexString + return "$sub1" + "$center" + - "$sub2" + "$sub2" } fun parseText(targetText: String): Spanned {