Merge remote-tracking branch 'upstream/master' into h11128dev

pull/393/head
Jason Yao 4 years ago
commit bd3bed36ee
  1. 1
      app/src/main/AndroidManifest.xml
  2. 8
      app/src/main/assets/txtTocRule.json
  3. 4
      app/src/main/assets/updateLog.md
  4. 3
      app/src/main/java/io/legado/app/constant/EventBus.kt
  5. 3
      app/src/main/java/io/legado/app/help/AppConfig.kt
  6. 37
      app/src/main/java/io/legado/app/help/IntentHelp.kt
  7. 32
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  8. 2
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  9. 14
      app/src/main/java/io/legado/app/service/CheckSourceService.kt
  10. 181
      app/src/main/java/io/legado/app/service/DownloadService.kt
  11. 26
      app/src/main/java/io/legado/app/service/help/Download.kt
  12. 4
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowAdapter.kt
  13. 15
      app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
  14. 2
      app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
  15. 17
      app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt
  16. 19
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt
  17. 31
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchResult.kt
  18. 31
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  19. 9
      app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
  20. 7
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  21. 58
      app/src/main/res/layout/view_read_menu.xml
  22. 3
      app/src/main/res/values-zh-rHK/strings.xml
  23. 3
      app/src/main/res/values-zh-rTW/strings.xml
  24. 3
      app/src/main/res/values-zh/strings.xml
  25. 1
      app/src/main/res/values/pref_key_value.xml
  26. 3
      app/src/main/res/values/strings.xml
  27. 7
      app/src/main/res/xml/pref_config_other.xml

@ -18,6 +18,7 @@
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<application
android:name=".App"

@ -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
},
{

@ -3,6 +3,10 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
- 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/09/15**
* 修复导入排版字体重复报错的bug
* 添加正文搜索 by [h11128](https://github.com/h11128)
**2020/09/12**
* web看书同步最新章
* web写源增加图片样式等规则

@ -20,4 +20,7 @@ object EventBus {
const val WEB_SERVICE_STOP = "webServiceStop"
const val UP_DOWNLOAD = "upDownload"
const val SAVE_CONTENT = "saveContent"
const val CHECK_INIT = "checkInit"
const val CHECK_UP_PROGRESS = "checkProgress"
const val CHECK_DONE = "checkDone"
}

@ -58,6 +58,9 @@ object AppConfig {
App.INSTANCE.putPrefBoolean(PreferKey.showRss, value)
}
val backgroundVerification: Boolean
get() = App.INSTANCE.getPrefBoolean(R.string.pk_background_verification)
val autoRefreshBook: Boolean
get() = App.INSTANCE.getPrefBoolean(R.string.pk_auto_refresh)

@ -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 <reified T> 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 <reified T> 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 <reified T> 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 <reified T> 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)
}
}

@ -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
)
}
}

@ -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

@ -6,12 +6,15 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.help.AppConfig
import io.legado.app.help.AppConfig.backgroundVerification
import io.legado.app.help.IntentHelp
import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.service.help.CheckSource
import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.utils.postEvent
import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.toast
import java.util.concurrent.Executors
@ -42,8 +45,10 @@ class CheckSourceService : BaseService() {
override fun onCreate() {
super.onCreate()
if (backgroundVerification) {
updateNotification(0, getString(R.string.start))
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
@ -59,6 +64,7 @@ class CheckSourceService : BaseService() {
super.onDestroy()
tasks.clear()
searchPool.close()
postEvent(EventBus.CHECK_DONE, 0)
}
private fun check(ids: List<String>) {
@ -72,7 +78,11 @@ class CheckSourceService : BaseService() {
allIds.addAll(ids)
processIndex = 0
threadCount = min(allIds.size, threadCount)
if (backgroundVerification) {
updateNotification(0, getString(R.string.progress_show, 0, allIds.size))
} else {
postEvent(EventBus.CHECK_INIT, allIds.size)
}
for (i in 0 until threadCount) {
check()
}
@ -106,10 +116,14 @@ class CheckSourceService : BaseService() {
synchronized(this) {
check()
checkedIds.add(sourceUrl)
if (backgroundVerification) {
updateNotification(
checkedIds.size,
getString(R.string.progress_show, checkedIds.size, allIds.size)
)
} else {
postEvent(EventBus.CHECK_UP_PROGRESS, checkedIds.size)
}
if (processIndex >= allIds.size + threadCount - 1) {
stopSelf()
}

@ -1,46 +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 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<DownloadService>(this, IntentAction.stop)
)
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
private val downloads = hashMapOf<Long, String>()
private val completeDownloads = hashSetOf<Long>()
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)
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(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<DownloadService>(
this,
IntentAction.play,
bundleOf("downloadId" to downloadId)
)
)
notificationBuilder.addAction(
R.drawable.ic_stop_black_24dp,
getString(R.string.cancel),
IntentHelp.servicePendingIntent<DownloadService>(
this,
IntentAction.stop,
bundleOf("downloadId" to downloadId)
)
)
notificationBuilder.setDeleteIntent(
IntentHelp.servicePendingIntent<DownloadService>(
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)
}
}

@ -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)
}
}
}

@ -26,7 +26,11 @@ class ExploreShowAdapter(context: Context, val callBack: CallBack) :
tv_lasted.text = context.getString(R.string.lasted_show, item.latestChapterTitle)
tv_lasted.visible()
}
if (item.intro.isNullOrEmpty()) {
tv_introduce.text = context.getString(R.string.intro_show_null)
} else {
tv_introduce.text = context.getString(R.string.intro_show, item.intro)
}
val kinds = item.getKindList()
if (kinds.isEmpty()) {
ll_kind.gone()

@ -65,6 +65,8 @@ class ReadMenu : FrameLayout {
brightnessBackground.setColor(ColorUtils.adjustAlpha(bgColor, 0.5f))
ll_brightness.background = brightnessBackground
ll_bottom_bg.setBackgroundColor(bgColor)
fabSearch.backgroundTintList = bottomBackgroundList
fabSearch.setColorFilter(textColor)
fabAutoPage.backgroundTintList = bottomBackgroundList
fabAutoPage.setColorFilter(textColor)
fabReplaceRule.backgroundTintList = bottomBackgroundList
@ -170,6 +172,13 @@ class ReadMenu : FrameLayout {
}
})
//搜索
fabSearch.onClick {
runMenuOut {
callBack?.openSearchList()
}
}
//自动翻页
fabAutoPage.onClick {
runMenuOut {
@ -200,12 +209,6 @@ class ReadMenu : FrameLayout {
}
}
ll_search.onClick {
runMenuOut {
callBack?.openSearchList()
}
}
//朗读
ll_read_aloud.onClick {
runMenuOut {

@ -324,7 +324,9 @@ class BgTextConfigDialog : BaseDialogFragment(), FileChooserDialog.CallBack {
val fontName = FileUtils.getName(config.textFont)
val fontPath =
FileUtils.getPath(requireContext().externalFilesDir, "font", fontName)
if (!FileUtils.exist(fontPath)) {
FileUtils.getFile(configDir, fontName).copyTo(File(fontPath))
}
config.textFont = fontPath
}
if (config.bgType() == 2) {

@ -42,7 +42,13 @@ class SearchAdapter(context: Context, val callBack: CallBack) :
tv_author.text = context.getString(R.string.author_show, searchBook.author)
bv_originCount.setBadgeCount(searchBook.origins.size)
upLasted(itemView, searchBook.latestChapterTitle)
tv_introduce.text = context.getString(R.string.intro_show, searchBook.intro)
if (searchBook.intro.isNullOrEmpty()) {
tv_introduce.text =
context.getString(R.string.intro_show_null)
} else {
tv_introduce.text =
context.getString(R.string.intro_show, searchBook.intro)
}
upKind(itemView, searchBook.getKindList())
iv_cover.load(searchBook.coverUrl, searchBook.name, searchBook.author)
@ -58,8 +64,15 @@ class SearchAdapter(context: Context, val callBack: CallBack) :
context.getString(R.string.author_show, searchBook.author)
"origins" -> bv_originCount.setBadgeCount(searchBook.origins.size)
"last" -> upLasted(itemView, searchBook.latestChapterTitle)
"intro" -> tv_introduce.text =
"intro" -> {
if (searchBook.intro.isNullOrEmpty()) {
tv_introduce.text =
context.getString(R.string.intro_show_null)
} else {
tv_introduce.text =
context.getString(R.string.intro_show, searchBook.intro)
}
}
"kind" -> upKind(itemView, searchBook.getKindList())
"cover" -> iv_cover.load(
searchBook.coverUrl,

@ -1,21 +1,12 @@
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 io.legado.app.utils.hexString
import kotlinx.android.synthetic.main.item_search_list.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
@ -23,15 +14,15 @@ class SearchListAdapter(context: Context, val callback: Callback) :
SimpleRecyclerAdapter<SearchResult>(context, R.layout.item_search_list) {
val cacheFileNames = hashSetOf<String>()
val textColor = context.getCompatColor(R.color.primaryText).hexString.substring(2)
val accentColor = context.accentColor.hexString.substring(2)
override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList<Any>) {
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
}
tv_search_result.text = item.getHtmlCompat(textColor, accentColor)
tv_search_result.paint.isFakeBoldText = isDur
}
}
}

@ -1,9 +1,7 @@
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,
@ -17,24 +15,25 @@ data class SearchResult(
var newPosition: Int = 0,
var contentPosition: Int =0
) {
val presentText: String
get(){
return colorPresentText(newPosition, query, text) +
"<font color=#0000ff>($chapterTitle)</font>"
fun getHtmlCompat(textColor: String, accentColor: String): Spanned {
val html = colorPresentText(newPosition, query, text, textColor, accentColor) +
"<font color=#${accentColor}>($chapterTitle)</font>"
return HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
fun colorPresentText(position: Int, center: String, targetText: String): String{
private fun colorPresentText(
position: Int,
center: String,
targetText: String,
textColor: String,
accentColor: String
): String {
val sub1 = text.substring(0, position)
val sub2 = text.substring(position + center.length, targetText.length)
return "<font color=#000000>$sub1</font>" +
"<font color=#ff0000>$center</font>" +
"<font color=#000000>$sub2</font>"
}
fun parseText(targetText: String): Spanned {
return HtmlCompat.fromHtml(targetText, HtmlCompat.FROM_HTML_MODE_LEGACY)
return "<font color=#${textColor}>$sub1</font>" +
"<font color=#${accentColor}>$center</font>" +
"<font color=#${textColor}>$sub2</font>"
}
}

@ -21,6 +21,7 @@ import io.legado.app.BuildConfig
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppPattern
import io.legado.app.constant.EventBus
import io.legado.app.data.entities.BookSource
import io.legado.app.help.IntentDataHelp
import io.legado.app.lib.dialogs.*
@ -65,6 +66,12 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
private var sort = 0
private var sortAscending = 0
private val progressDialogBuilder by lazy {
progressDialog("校验书源") {
setCancelable(false)
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
initRecyclerView()
initSearchView()
@ -263,6 +270,30 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}
override fun observeLiveBus() {
observeEvent<Int>(EventBus.CHECK_INIT) { max->
progressDialogBuilder.max = max
progressDialogBuilder.show()
}
observeEvent<Int>(EventBus.CHECK_UP_PROGRESS) { progress->
progressDialogBuilder.progress = progress
}
observeEvent<Int>(EventBus.CHECK_DONE) {
if (progressDialogBuilder.isShowing) {
progressDialogBuilder.progress = it
progressDialogBuilder.max = it
progressDialogBuilder.dismiss()
}
toast("校验完成!")
groups.map { group->
if (group.contains("失效")) {
search_view.setQuery("失效", true)
toast("发现有失效书源,已为您自动筛选!")
}
}
}
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_enable_selection -> viewModel.enableSelection(adapter.getSelection())

@ -11,10 +11,7 @@ import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.BookSource
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.ACache
import io.legado.app.utils.dp
import io.legado.app.utils.gone
import io.legado.app.utils.visible
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.item_fillet_text.view.*
import kotlinx.android.synthetic.main.item_find_book.view.*
import kotlinx.coroutines.CoroutineScope
@ -55,12 +52,12 @@ class ExploreAdapter(context: Context, private val scope: CoroutineScope, val ca
.inflate(R.layout.item_fillet_text, gl_child, false)
gl_child.addView(tv)
tv.text_view.text = kind.title
if (!kind.url.isNullOrEmpty()) {
tv.text_view.onClick {
kind.url?.let { kindUrl ->
callBack.openExplore(
item.bookSourceUrl,
kind.title,
kindUrl
kind.url.toString()
)
}
}

@ -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<ReadRssViewModel>(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)
}
}
}

@ -92,6 +92,26 @@
android:paddingStart="32dp"
android:paddingEnd="32dp">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/search_content"
android:src="@drawable/ic_search"
android:tint="@color/primaryText"
android:tooltipText="@string/search_content"
app:backgroundTint="@color/background_menu"
app:elevation="2dp"
app:fabSize="mini"
app:pressedTranslationZ="2dp"
tools:ignore="UnusedAttribute" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAutoPage"
android:layout_width="wrap_content"
@ -265,44 +285,6 @@
android:layout_height="match_parent"
android:layout_weight="2" />
<!--搜索按钮-->
<LinearLayout
android:id="@+id/ll_search"
android:layout_width="60dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/search_content"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_search"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="@string/search_content"
android:src="@drawable/ic_search"
app:tint="@color/primaryText"
tools:ignore="NestedWeights" />
<TextView
android:id="@+id/tv_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:maxLines="1"
android:text="@string/search_content"
android:textColor="@color/primaryText"
android:textSize="12sp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<!--调节按钮-->
<LinearLayout
android:id="@+id/ll_read_aloud"

@ -94,6 +94,8 @@
閲讀3.0下載地址:\nhttps://play.google.com/store/apps/details?id=io.legado.play.release
</string>
<string name="version_name">Version %s</string>
<string name="pt_background_verification">後臺校驗書源</string>
<string name="ps_background_verification">打開后可以在校驗書源時自由操作</string>
<string name="pt_auto_refresh">自動刷新</string>
<string name="ps_auto_refresh">打開程式時自動更新書輯</string>
<string name="pt_auto_download">自動下載最新章節</string>
@ -223,6 +225,7 @@
<string name="load_error_retry">載入失敗,點擊重試</string>
<string name="book_intro">內容簡介</string>
<string name="intro_show">簡介: %s</string>
<string name="intro_show_null">簡介: 暫無簡介</string>
<string name="open_from_other">打開外部書籍</string>
<string name="origin_show">來源: %s</string>
<string name="import_replace_rule">本地導入</string>

@ -94,6 +94,8 @@
閱讀3.0下載網址:\nhttps://play.google.com/store/apps/details?id=io.legado.play.release
</string>
<string name="version_name">Version %s</string>
<string name="pt_background_verification">後臺校驗書源</string>
<string name="ps_background_verification">打開后可以在校驗書源時自由操作</string>
<string name="pt_auto_refresh">自動重新整理</string>
<string name="ps_auto_refresh">打開軟體時自動更新書籍</string>
<string name="pt_auto_download">自動下載最新章節</string>
@ -223,6 +225,7 @@
<string name="load_error_retry">載入失敗,點擊重試</string>
<string name="book_intro">內容簡介</string>
<string name="intro_show">簡介:%s</string>
<string name="intro_show_null">簡介: 暫無簡介</string>
<string name="open_from_other">打開外部書籍</string>
<string name="origin_show">來源: %s</string>
<string name="import_replace_rule">本機匯入</string>

@ -96,6 +96,8 @@
阅读3.0下载地址:\nhttps://www.coolapk.com/apk/256030
</string>
<string name="version_name">Version %s</string>
<string name="pt_background_verification">后台校验书源</string>
<string name="ps_background_verification">打开后可以在校验书源时自由操作</string>
<string name="pt_auto_refresh">自动刷新</string>
<string name="ps_auto_refresh">打开软件时自动更新书籍</string>
<string name="pt_auto_download">自动下载最新章节</string>
@ -225,6 +227,7 @@
<string name="load_error_retry">加载失败,点击重试</string>
<string name="book_intro">内容简介</string>
<string name="intro_show">简介:%s</string>
<string name="intro_show_null">简介: 暂无简介</string>
<string name="open_from_other">打开外部书籍</string>
<string name="origin_show">来源: %s</string>
<string name="import_replace_rule">本地导入</string>

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pk_background_verification" translatable="false">background_verification</string>
<string name="pk_auto_refresh" translatable="false">auto_refresh</string>
<string name="pk_requested_direction" translatable="false">list_screen_direction</string>
<string name="pk_full_screen" translatable="false">full_screen</string>

@ -96,6 +96,8 @@
Legado (YueDu 3.0) download link:\n https://play.google.com/store/apps/details?id=io.legado.play.release
</string>
<string name="version_name">Version %s</string>
<string name="pt_background_verification">Background-verification</string>
<string name="ps_background_verification">you can operate freely when verifying the book source</string>
<string name="pt_auto_refresh">Auto-refresh</string>
<string name="ps_auto_refresh">Update books automatically when opening the software</string>
<string name="pt_auto_download">Auto-download</string>
@ -225,6 +227,7 @@
<string name="load_error_retry">Load failed, tap to retry</string>
<string name="book_intro">Book description</string>
<string name="intro_show">Description:%s</string>
<string name="intro_show_null">Description: no introduction</string>
<string name="open_from_other">Open external book</string>
<string name="origin_show">Origin: %s</string>
<string name="import_replace_rule">Import local rules</string>

@ -51,6 +51,13 @@
app:iconSpaceReserved="false"
app:layout="@layout/view_preference_category">
<io.legado.app.ui.widget.prefs.SwitchPreference
android:defaultValue="false"
android:key="@string/pk_background_verification"
android:summary="@string/ps_background_verification"
android:title="@string/pt_background_verification"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference
android:defaultValue="true"
android:key="replaceEnableDefault"

Loading…
Cancel
Save