diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 34c57bb7b..ef2c71ab2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -18,6 +18,7 @@
+
+
+
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
},
{
diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md
index e9ab31cb0..40348af60 100644
--- a/app/src/main/assets/updateLog.md
+++ b/app/src/main/assets/updateLog.md
@@ -3,6 +3,10 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
- 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
+**2020/09/15**
+* 修复导入排版字体重复报错的bug
+* 添加正文搜索 by [h11128](https://github.com/h11128)
+
**2020/09/12**
* web看书同步最新章
* web写源增加图片样式等规则
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/data/dao/BookChapterDao.kt b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt
index d0588bec8..0041591e3 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookChapterDao.kt
@@ -25,6 +25,9 @@ interface BookChapterDao {
@Query("select * from chapters where bookUrl = :bookUrl and `index` = :index")
fun getChapter(bookUrl: String, index: Int): BookChapter?
+ @Query("select * from chapters where bookUrl = :bookUrl and `title` = :title")
+ fun getChapter(bookUrl: String, title: String): BookChapter?
+
@Query("select count(url) from chapters where bookUrl = :bookUrl")
fun getChapterCount(bookUrl: String): Int
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/help/IntentHelp.kt b/app/src/main/java/io/legado/app/help/IntentHelp.kt
index d02e13392..d7ed03af1 100644
--- a/app/src/main/java/io/legado/app/help/IntentHelp.kt
+++ b/app/src/main/java/io/legado/app/help/IntentHelp.kt
@@ -3,6 +3,7 @@ package io.legado.app.help
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
+import android.os.Bundle
import io.legado.app.R
import org.jetbrains.anko.toast
@@ -32,21 +33,29 @@ object IntentHelp {
}
}
- inline fun 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/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
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..53868a977
--- /dev/null
+++ b/app/src/main/java/io/legado/app/service/DownloadService.kt
@@ -0,0 +1,197 @@
+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.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.toast
+import java.io.File
+
+
+class DownloadService : BaseService() {
+
+ 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()
+ 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)
+ downloads.remove(downloadId)
+ if (downloads.isEmpty()) {
+ 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())
+ 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 -> "未知状态"
+ }
+ updateNotification(id, "${downloads[id]} $status", max, progress)
+ }
+ }
+
+ private fun installApk(downloadId: Long) {
+ 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)
+ }
+ }
+ }
+
+ /**
+ * 更新通知
+ */
+ 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(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/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt
index e8485261c..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
@@ -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,51 @@ object ReadBook {
}
}
+ 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
+ }
+ val contentPosition = index
+ var pageIndex = 0
+ var length = pages[pageIndex].text.length
+ while (length < contentPosition){
+ pageIndex += 1
+ if (pageIndex >pages.size){
+ pageIndex = pages.size
+ break
+ }
+ length += pages[pageIndex].text.length
+ }
+
+ // 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 +473,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 7840123db..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
@@ -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
@@ -47,6 +48,8 @@ import io.legado.app.ui.book.read.page.ContentTextView
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
@@ -57,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
@@ -79,6 +85,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
@@ -96,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
@@ -532,6 +540,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
intent.removeExtra("readAloud")
ReadBook.readAloud()
}
+ loadStates = true
}
/**
@@ -543,6 +552,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
page_view.upContent(relativePosition, resetPageOffset)
seek_read_page.progress = ReadBook.durPageIndex
}
+ loadStates = false
}
/**
@@ -667,6 +677,19 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
}
}
+ /**
+ * 打开搜索界面
+ */
+ //todo: change request code
+ override fun openSearchList() {
+ ReadBook.book?.let {
+ startActivityForResult(
+ requestCodeSearchResult,
+ Pair("bookUrl", it.bookUrl)
+ )
+ }
+ }
+
/**
* 替换规则变化
*/
@@ -747,11 +770,36 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
requestCodeChapterList ->
data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index ->
if (index != ReadBook.durChapterIndex) {
+ val pageIndex = data.getIntExtra("pageIndex", 0)
+ viewModel.openChapter(index, pageIndex)
+ }
+ }
+ requestCodeSearchResult ->
+ data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index ->
+ launch(IO){
+ val indexWithinChapter = data.getIntExtra("indexWithinChapter", 0)
+ val query = data.getStringExtra("query")
viewModel.openChapter(index)
+ // block until load correct chapter and pages
+ var pages = ReadBook.curTextChapter?.pages
+ while (ReadBook.durChapterIndex != index || pages == null ){
+ delay(100L)
+ pages = ReadBook.curTextChapter?.pages
+ }
+ val positions = ReadBook.searchResultPositions(pages, indexWithinChapter, query!!)
+ //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/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/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/book/searchContent/SearchListActivity.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt
new file mode 100644
index 000000000..ec1cbc0c7
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt
@@ -0,0 +1,93 @@
+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) {
+ // todo: 完善搜索界面UI
+ 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()
+ //to do clean
+ false
+ }
+ searchView?.setOnSearchClickListener { tab_layout.gone() }
+ searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String): Boolean {
+ if (viewModel.lastQuery != query){
+ viewModel.startContentSearch(query)
+ }
+ return false
+ }
+
+ override fun onQueryTextChange(newText: String): Boolean {
+
+ return false
+ }
+ })
+ return super.onCompatCreateOptionsMenu(menu)
+ }
+
+ private inner class TabFragmentPageAdapter(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..d2c98b955
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt
@@ -0,0 +1,51 @@
+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
+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.*
+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) {
+
+ val cacheFileNames = hashSetOf()
+
+ 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 = item.parseText(item.presentText)
+ if (isDur){
+ tv_search_result.paint.isFakeBoldText = true
+ }
+ }
+ }
+ }
+
+ override fun registerListener(holder: ItemViewHolder) {
+ holder.itemView.onClick {
+ getItem(holder.layoutPosition)?.let {
+ callback.openSearchResult(it)
+ }
+ }
+ }
+
+ 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
new file mode 100644
index 000000000..38ac013df
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt
@@ -0,0 +1,239 @@
+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.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
+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
+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 org.jetbrains.anko.sdk27.listeners.onClick
+import java.util.regex.Pattern
+
+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 lateinit var mLayoutManager: UpLinearLayoutManager
+ private var searchResultCounts = 0
+ private var durChapterIndex = 0
+ private var searchResultList: MutableList = mutableListOf()
+
+ override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
+ viewModel.searchCallBack = this
+ val bbg = bottomBackground
+ val btc = requireContext().getPrimaryTextColor(ColorUtils.isColorLight(bbg))
+ 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()
+ }
+
+ 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)
+ }
+ }
+ }
+
+ @SuppressLint("SetTextI18n")
+ private fun initBook() {
+ launch {
+
+ tv_current_search_info.text = "搜索结果:$searchResultCounts"
+ viewModel.book?.let {
+ initCacheFileNames(it)
+ durChapterIndex = it.durChapterIndex
+ }
+ }
+ }
+
+ 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)
+ }
+ }
+ }
+ }
+
+ @SuppressLint("SetTextI18n")
+ override fun startContentSearch(newText: String) {
+ // 按章节搜索内容
+ if (!newText.isBlank()) {
+ adapter.clearItems()
+ searchResultList.clear()
+ searchResultCounts = 0
+ viewModel.lastQuery = newText
+ 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)
+ }
+ }
+ job.await()
+ if (searchResults.isNotEmpty()){
+ searchResultList.addAll(searchResults)
+ tv_current_search_info.text = "搜索结果:$searchResultCounts"
+ adapter.addItems(searchResults)
+ searchResults = listOf()
+ }
+ }
+ }
+ }
+ }
+
+ private suspend fun searchChapter(query: String, chapter: BookChapter?): List {
+ val searchResults: MutableList = mutableListOf()
+ 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){
+ //搜索替换后的正文
+ 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,
+ bookContent,
+ bookSource.useReplaceRule
+ )
+ }
+ 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,
+ chapterIndex = chapter.index,
+ newPosition = construct[0] as Int,
+ contentPosition = it
+ )
+ count += 1
+ searchResultCounts += 1
+ searchResults.add(result)
+ }
+ }
+ }
+ }
+ return searchResults
+ }
+
+ private fun searchPosition(content: String, pattern: String): List {
+ val position : MutableList = mutableListOf()
+ var index = content.indexOf(pattern)
+ while(index >= 0){
+ position.add(index)
+ index = content.indexOf(pattern, index + 1);
+ }
+ return position
+ }
+
+ private fun constructText(content: String, position: Int, query: String): Array{
+ // 构建关键词周边文字,在搜索结果里显示
+ // todo: 判断段落,只在关键词所在段落内分割
+ // todo: 利用标点符号分割完整的句
+ // todo: length和设置结合,自由调整周边文字长度
+ val length = 20
+ var po1 = position - length
+ var po2 = position + query.length + length
+ if (po1 <0) {
+ po1 = 0
+ }
+ if (po2 > content.length){
+ po2 = content.length
+ }
+ val newPosition = position - po1
+ val newText = content.substring(po1, po2)
+ return arrayOf(newPosition, newText)
+ }
+
+ val isLocalBook: Boolean
+ get() = viewModel.book?.isLocalBook() == true
+
+ override fun openSearchResult(searchResult: SearchResult) {
+
+ val searchData = Intent()
+ searchData.putExtra("index", searchResult.chapterIndex)
+ searchData.putExtra("contentPosition", searchResult.contentPosition)
+ searchData.putExtra("query", searchResult.query)
+ searchData.putExtra("indexWithinChapter", searchResult.indexWithinChapter)
+ activity?.setResult(RESULT_OK, searchData)
+ activity?.finish()
+
+
+ }
+
+ 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/SearchListViewModel.kt b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt
new file mode 100644
index 000000000..060d74067
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt
@@ -0,0 +1,33 @@
+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
+ var lastQuery: String = ""
+
+ 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/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..3a629839b
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt
@@ -0,0 +1,43 @@
+package io.legado.app.ui.book.searchContent
+
+import android.text.Spanned
+import androidx.core.text.HtmlCompat
+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,
+ var indexWithinChapter: Int = 0,
+ var text: String = "",
+ var chapterTitle: String = "",
+ val query: String,
+ var pageSize: Int = 0,
+ var chapterIndex: Int = 0,
+ var pageIndex: Int = 0,
+ var newPosition: Int = 0,
+ var contentPosition: Int =0
+) {
+ val presentText: String
+ get(){
+ 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)
+ val textColor = App.INSTANCE.getCompatColor(R.color.primaryText).hexString
+ return "$sub1" +
+ "$center" +
+ "$sub2"
+ }
+
+ fun parseText(targetText: String): Spanned {
+ return HtmlCompat.fromHtml(targetText, HtmlCompat.FROM_HTML_MODE_LEGACY)
+ }
+
+
+
+}
\ 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)
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..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,12 +162,12 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
request.setAllowedOverRoaming(true)
// 允许下载的网路类型
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
+ 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)
}
}
}
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..2dcaf1436
--- /dev/null
+++ b/app/src/main/res/layout/item_search_list.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ 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..b63fba1ff 100644
--- a/app/src/main/res/layout/view_read_menu.xml
+++ b/app/src/main/res/layout/view_read_menu.xml
@@ -260,6 +260,45 @@
android:textSize="12sp" />
+
+
+
+
+
+
+
+
+
+
切換默認主題
分享選中書源
時間排序
+ 搜索
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 92888ae19..893230b30 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -763,5 +763,6 @@
Save, Import, Share theme
Share selected sources
Sort by update time
+ Search
\ No newline at end of file