# Conflicts:
#	app/src/main/res/values/colors.xml
pull/169/head
yangyxd 5 years ago
commit 78573d8e77
  1. 4
      app/build.gradle
  2. 14
      app/src/main/AndroidManifest.xml
  3. 24
      app/src/main/assets/updateLog.md
  4. 5
      app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt
  5. 14
      app/src/main/java/io/legado/app/data/dao/BookDao.kt
  6. 12
      app/src/main/java/io/legado/app/data/entities/RssStar.kt
  7. 7
      app/src/main/java/io/legado/app/help/ItemTouchCallback.kt
  8. 45
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  9. 6
      app/src/main/java/io/legado/app/model/Debug.kt
  10. 12
      app/src/main/java/io/legado/app/model/Rss.kt
  11. 6
      app/src/main/java/io/legado/app/model/webBook/BookContent.kt
  12. 8
      app/src/main/java/io/legado/app/service/BaseReadAloudService.kt
  13. 2
      app/src/main/java/io/legado/app/ui/README.md
  14. 5
      app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
  15. 1
      app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
  16. 18
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
  17. 35
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt
  18. 13
      app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt
  19. 2
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  20. 2
      app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
  21. 7
      app/src/main/java/io/legado/app/ui/book/group/GroupViewModel.kt
  22. 6
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  23. 8
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  24. 3
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  25. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  26. 4
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  27. 41
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt
  28. 6
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  29. 2
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceAdapter.kt
  30. 5
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
  31. 12
      app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt
  32. 39
      app/src/main/java/io/legado/app/ui/config/FileAssociationActivity.kt
  33. 69
      app/src/main/java/io/legado/app/ui/config/FileAssociationViewModel.kt
  34. 1
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
  35. 7
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
  36. 2
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleAdapter.kt
  37. 22
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt
  38. 2
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  39. 31
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
  40. 7
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  41. 2
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
  42. 5
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
  43. 6
      app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
  44. 8
      app/src/main/java/io/legado/app/utils/EncodingDetect.java
  45. 4
      app/src/main/res/drawable/ic_help.xml
  46. 26
      app/src/main/res/layout-land/activity_book_info.xml
  47. 11
      app/src/main/res/layout/activity_audio_play.xml
  48. 7
      app/src/main/res/layout/activity_book_info.xml
  49. 8
      app/src/main/res/layout/activity_file_association.xml
  50. 2
      app/src/main/res/menu/change_cover.xml
  51. 2
      app/src/main/res/menu/change_source.xml

@ -136,12 +136,12 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
//room
def room_version = '2.2.4'
def room_version = '2.2.5'
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
//paging
implementation 'androidx.paging:paging-runtime:2.1.1'
implementation 'androidx.paging:paging-runtime:2.1.2'
//anko
def anko_version = '0.10.8'

@ -287,7 +287,19 @@
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity
android:name=".ui.config.FileAssociationActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="text/plain" />
<data android:mimeType="application/json" />
</intent-filter>
</activity>
<service android:name=".service.CheckSourceService" />
<service android:name=".service.DownloadService" />
<service android:name=".service.WebService" />

@ -3,6 +3,21 @@
* 请关注[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
* 弄了个企业公众号[开源阅读](),后面弄好后会把原来的[开源阅读软件]()迁移过来
**2020/03/22**
* 添加文件关联 by wqfantexi
* 手动排序可以了,在书架整理里面拖动排序
* 删除分组时同时删除书籍里的分组信息,下次添加新分组时不会自动出现在分组内
* 修复换源丢失分组信息的bug
* 修复部分朗读引擎不自动朗读下一章的bug
**2020/03/21**
* 详情页点击书名搜索
**2020/03/20**
* 自动备份文件和手动备份文件分开
* 修复一些rss收藏取消不了的bug
* 修复rss请求头无效bug
**2020/03/19**
* 美化界面我的 by yangyxd
* 优化搜索
@ -258,14 +273,7 @@
- 调试正文页>>输入正文页URL,如:`--https://www.zhaishuyuan.com/chapter/30394/20940996`
* 修改订阅中自动添加style的情景
订阅源的内容规则中存在`<style>``style=`,
```
<style>
img{max-width:100% !important; width:auto; height:auto;}
video{object-fit:fill; max-width:100% !important; width:auto; height:auto;}
body{word-wrap:break-word; height:auto;max-width: 100%; width:auto;}
</style>
```
订阅源的内容规则中存在`<style>``style=`
**2019/12/28**
* 添加下载界面

@ -290,12 +290,11 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) :
return footerItems?.size() ?: 0
}
fun getItem(position: Int): ITEM? =
if (position in 0 until items.size) items[position] else null
fun getItem(position: Int): ITEM? = items.getOrNull(position)
fun getItemByLayoutPosition(position: Int): ITEM? {
val pos = position - getHeaderCount()
return if (pos in 0 until items.size) items[pos] else null
return items.getOrNull(pos)
}
fun getItems(): List<ITEM> = items

@ -12,16 +12,16 @@ interface BookDao {
@Query("SELECT * FROM books order by durChapterTime desc")
fun observeAll(): LiveData<List<Book>>
@Query("SELECT * FROM books WHERE type = ${BookType.audio} order by durChapterTime desc")
@Query("SELECT * FROM books WHERE type = ${BookType.audio}")
fun observeAudio(): LiveData<List<Book>>
@Query("SELECT * FROM books WHERE origin = '${BookType.local}' order by durChapterTime desc")
@Query("SELECT * FROM books WHERE origin = '${BookType.local}'")
fun observeLocal(): LiveData<List<Book>>
@Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}' order by durChapterTime desc")
@Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'")
fun observeLocalUri(): LiveData<List<String>>
@Query("SELECT * FROM books WHERE origin <> '${BookType.local}' and type = 0 order by durChapterTime desc")
@Query("SELECT * FROM books WHERE origin <> '${BookType.local}' and type = 0")
fun observeDownload(): LiveData<List<Book>>
@Query("SELECT * FROM books WHERE (`group` & :group) > 0")
@ -33,6 +33,9 @@ interface BookDao {
@Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'")
fun liveDataSearch(key: String): LiveData<List<Book>>
@Query("SELECT * FROM books WHERE (`group` & :group) > 0")
fun getBooksByGroup(group: Int): List<Book>
@Query("SELECT * FROM books WHERE `name` in (:names)")
fun findByName(vararg names: String): List<Book>
@ -57,6 +60,9 @@ interface BookDao {
@get:Query("SELECT COUNT(*) FROM books")
val allBookCount: Int
@get:Query("select max(`order`) from books")
val maxOrder: Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg book: Book)

@ -16,4 +16,16 @@ data class RssStar(
var description: String? = null,
var content: String? = null,
var image: String? = null
) {
fun toRssArticle(): RssArticle {
return RssArticle(
origin = origin,
title = title,
link = link,
pubDate = pubDate,
description = description,
content = content,
image = image
)
}
}

@ -106,7 +106,7 @@ class ItemTouchCallback : ItemTouchHelper.Callback() {
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
onItemTouchCallbackListener?.clearView(recyclerView, viewHolder)
onItemTouchCallbackListener?.onClearView(recyclerView, viewHolder)
}
interface OnItemTouchCallbackListener {
@ -131,7 +131,10 @@ class ItemTouchCallback : ItemTouchHelper.Callback() {
return true
}
fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
/**
* 手指松开
*/
fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
}

@ -21,14 +21,6 @@ object Backup {
FileUtils.getDirFile(App.INSTANCE.filesDir, "backup").absolutePath
}
val legadoPath by lazy {
FileUtils.getSdCardPath() + File.separator + "YueDu3.0"
}
val exportPath by lazy {
legadoPath + File.separator + "Export"
}
val backupFileNames by lazy {
arrayOf(
"bookshelf.json", "bookGroup.json", "bookSource.json", "rssSource.json",
@ -39,19 +31,15 @@ object Backup {
fun autoBack(context: Context) {
val lastBackup = context.getPrefLong(PreferKey.lastBackup)
if (lastBackup + TimeUnit.DAYS.toMillis(1) < System.currentTimeMillis()) {
return
}
Coroutine.async {
val backupPath = context.getPrefString(PreferKey.backupPath)
if (backupPath.isNullOrEmpty()) {
backup(context)
} else {
backup(context, backupPath)
context.getPrefString(PreferKey.backupPath)?.let {
backup(context, it, true)
}
}
}
}
suspend fun backup(context: Context, path: String = legadoPath) {
suspend fun backup(context: Context, path: String, isAuto: Boolean = false) {
context.putPrefLong(PreferKey.lastBackup, System.currentTimeMillis())
withContext(IO) {
synchronized(this@Backup) {
@ -81,9 +69,9 @@ object Backup {
}
WebDavHelp.backUpWebDav(backupPath)
if (path.isContentPath()) {
copyBackup(context, Uri.parse(path))
copyBackup(context, Uri.parse(path), isAuto)
} else {
copyBackup(File(path))
copyBackup(File(path), isAuto)
}
}
}
@ -97,12 +85,19 @@ object Backup {
}
@Throws(java.lang.Exception::class)
private fun copyBackup(context: Context, uri: Uri) {
private fun copyBackup(context: Context, uri: Uri, isAuto: Boolean) {
DocumentFile.fromTreeUri(context, uri)?.let { treeDoc ->
for (fileName in backupFileNames) {
val file = File(backupPath + File.separator + fileName)
if (file.exists()) {
if (isAuto) {
treeDoc.findFile("auto")?.findFile(fileName)?.delete()
DocumentUtils.createFileIfNotExist(
treeDoc,
fileName,
subDirs = *arrayOf("auto")
)?.writeBytes(context, file.readBytes())
} else {
treeDoc.findFile(fileName)?.delete()
treeDoc.createFile("", fileName)
?.writeBytes(context, file.readBytes())
@ -110,15 +105,19 @@ object Backup {
}
}
}
}
@Throws(java.lang.Exception::class)
private fun copyBackup(rootFile: File) {
private fun copyBackup(rootFile: File, isAuto: Boolean) {
for (fileName in backupFileNames) {
val file = File(backupPath + File.separator + fileName)
if (file.exists()) {
file.copyTo(
FileUtils.createFileIfNotExist(rootFile, fileName),
true
if (isAuto) {
FileUtils.createFileIfNotExist(rootFile, fileName, "auto")
} else {
FileUtils.createFileIfNotExist(rootFile, fileName)
}, true
)
}
}

@ -67,7 +67,7 @@ object Debug {
if (ruleContent.isNullOrEmpty()) {
log(debugSource, "⇒内容规则为空,默认获取整个网页", state = 1000)
} else {
rssContentDebug(it.articles[0], ruleContent)
rssContentDebug(it.articles[0], ruleContent, rssSource)
}
} else {
log(debugSource, "⇒存在描述规则,不解析内容页")
@ -80,9 +80,9 @@ object Debug {
}
}
private fun rssContentDebug(rssArticle: RssArticle, ruleContent: String) {
private fun rssContentDebug(rssArticle: RssArticle, ruleContent: String, rssSource: RssSource) {
log(debugSource, "︾开始解析内容页")
Rss.getContent(rssArticle, ruleContent)
Rss.getContent(rssArticle, ruleContent, rssSource)
.onSuccess {
log(debugSource, it)
log(debugSource, "︽内容页解析完成", state = 1000)

@ -21,7 +21,10 @@ object Rss {
context: CoroutineContext = Dispatchers.IO
): Coroutine<Result> {
return Coroutine.async(scope, context) {
val analyzeUrl = AnalyzeUrl(pageUrl ?: rssSource.sourceUrl)
val analyzeUrl = AnalyzeUrl(
pageUrl ?: rssSource.sourceUrl,
headerMapF = rssSource.getHeaderMap()
)
val body = analyzeUrl.getResponseAwait(rssSource.sourceUrl).body
RssParserByRule.parseXML(body, rssSource)
}
@ -30,12 +33,15 @@ object Rss {
fun getContent(
rssArticle: RssArticle,
ruleContent: String,
rssSource: RssSource?,
scope: CoroutineScope = Coroutine.DEFAULT,
context: CoroutineContext = Dispatchers.IO
): Coroutine<String> {
return Coroutine.async(scope, context) {
val body = AnalyzeUrl(rssArticle.link, baseUrl = rssArticle.origin)
.getResponseAwait(rssArticle.origin)
val body = AnalyzeUrl(
rssArticle.link, baseUrl = rssArticle.origin,
headerMapF = rssSource?.getHeaderMap()
).getResponseAwait(rssArticle.origin)
.body
val analyzeRule = AnalyzeRule()
analyzeRule.setContent(

@ -2,6 +2,7 @@ package io.legado.app.model.webBook
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
@ -39,7 +40,12 @@ object BookContent {
var contentData = analyzeContent(
book, baseUrl, body, contentRule, bookChapter, bookSource
)
if (bookSource.bookSourceType == BookType.default) {
content.append(contentData.content.replace(bookChapter.title, "")).append("\n")
} else {
content.append(contentData.content).append("\n")
}
if (contentData.nextUrl.size == 1) {
var nextUrl = contentData.nextUrl[0]
val nextChapterUrl = if (!nextChapterUrlF.isNullOrEmpty())

@ -111,11 +111,17 @@ abstract class BaseReadAloudService : BaseService(),
if (getPrefBoolean(PreferKey.readAloudByPage)) {
for (index in pageIndex..textChapter.lastIndex()) {
textChapter.page(index)?.text?.split("\n")?.let {
if (it.isNotEmpty()) {
contentList.addAll(it)
}
}
}
} else {
contentList.addAll(textChapter.getUnRead(pageIndex).split("\n"))
textChapter.getUnRead(pageIndex).split("\n").forEach {
if (it.isNotEmpty()) {
contentList.add(it)
}
}
}
if (play) play()
} ?: stopSelf()

@ -6,7 +6,7 @@
* book\info 书籍信息查看
* book\read 书籍阅读界面
* book\search 搜索书籍界面
* book\source 搜索书源界面
* book\source 书源界面
* book\changeCover 封面换源界面
* book\changeSource 换源界面
* book\chapterList 目录界面

@ -46,9 +46,8 @@ class AudioPlayActivity :
private var adjustProgress = false
override fun onActivityCreated(savedInstanceState: Bundle?) {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
AudioPlay.titleData.observe(this, Observer { toolbar.title = it })
title_bar.transparent()
AudioPlay.titleData.observe(this, Observer { title_bar.title = it })
AudioPlay.coverData.observe(this, Observer { upCover(it) })
viewModel.initData(intent)
initView()

@ -91,6 +91,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
fun changeTo(book1: Book) {
execute {
AudioPlay.book?.let {
book1.order = it.order
App.db.bookDao().delete(it)
}
withContext(Dispatchers.Main) {

@ -11,6 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup
import io.legado.app.help.ItemTouchCallback
@ -23,6 +24,7 @@ import io.legado.app.ui.book.group.GroupSelectDialog
import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getPrefInt
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_arrange_book.*
@ -68,7 +70,7 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
itemTouchCallback.isCanDrag = true
itemTouchCallback.isCanDrag = getPrefInt(PreferKey.bookshelfSort) == 3
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
select_action_bar.setMainActionText(R.string.move_to_group)
select_action_bar.inflateMenu(R.menu.arrange_book_sel)
@ -109,8 +111,14 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
-11 -> App.db.bookDao().observeNoGroup()
else -> App.db.bookDao().observeByGroup(groupId)
}
booksLiveData?.observe(this, Observer {
adapter.setItems(it)
booksLiveData?.observe(this, Observer { list ->
val books = when (getPrefInt(PreferKey.bookshelfSort)) {
1 -> list.sortedByDescending { it.latestChapterTime }
2 -> list.sortedBy { it.name }
3 -> list.sortedBy { it.order }
else -> list.sortedByDescending { it.durChapterTime }
}
adapter.setItems(books)
upSelectCount()
})
}
@ -205,6 +213,10 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
select_action_bar.upCountView(adapter.selectedBooks().size, adapter.getItems().size)
}
override fun updateBook(vararg book: Book) {
viewModel.updateBook(*book)
}
override fun deleteBook(book: Book) {
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton {

@ -2,14 +2,18 @@ package io.legado.app.ui.book.arrange
import android.content.Context
import android.view.View
import androidx.recyclerview.widget.RecyclerView
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.Book
import io.legado.app.data.entities.BookGroup
import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.theme.backgroundColor
import kotlinx.android.synthetic.main.item_arrange_book.view.*
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.*
class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<Book>(context, R.layout.item_arrange_book),
@ -54,6 +58,7 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
override fun convert(holder: ItemViewHolder, item: Book, payloads: MutableList<Any>) {
with(holder.itemView) {
backgroundColor = context.backgroundColor
tv_name.text = item.name
tv_author.text = item.author
tv_author.visibility = if (item.author.isEmpty()) View.GONE else View.VISIBLE
@ -120,9 +125,39 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
return groupNames.joinToString(",")
}
private var isMoved = false
override fun onMove(srcPosition: Int, targetPosition: Int): Boolean {
val srcItem = getItem(srcPosition)
val targetItem = getItem(targetPosition)
Collections.swap(getItems(), srcPosition, targetPosition)
notifyItemMoved(srcPosition, targetPosition)
if (srcItem != null && targetItem != null) {
if (srcItem.order == targetItem.order) {
for ((index, item) in getItems().withIndex()) {
item.order = index + 1
}
} else {
val pos = srcItem.order
srcItem.order = targetItem.order
targetItem.order = pos
}
}
isMoved = true
return true
}
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (isMoved) {
callBack.updateBook(*getItems().toTypedArray())
}
isMoved = false
}
interface CallBack {
val groupList: List<BookGroup>
fun upSelectCount()
fun updateBook(vararg book: Book)
fun deleteBook(book: Book)
fun selectGroup(groupId: Int, requestCode: Int)
}

@ -13,6 +13,7 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
@ -81,9 +82,15 @@ class DownloadActivity : VMBaseActivity<DownloadViewModel>(R.layout.activity_dow
private fun initLiveData() {
bookshelfLiveData?.removeObservers(this)
bookshelfLiveData = App.db.bookDao().observeDownload()
bookshelfLiveData?.observe(this, Observer {
adapter.setItems(it)
initCacheSize(it)
bookshelfLiveData?.observe(this, Observer { list ->
val books = when (getPrefInt(PreferKey.bookshelfSort)) {
1 -> list.sortedByDescending { it.latestChapterTime }
2 -> list.sortedBy { it.name }
3 -> list.sortedBy { it.order }
else -> list.sortedByDescending { it.durChapterTime }
}
adapter.setItems(books)
initCacheSize(books)
})
}

@ -217,7 +217,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
return true
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (isMoved) {
for ((index, item) in getItems().withIndex()) {
item.order = index + 1

@ -203,7 +203,7 @@ class GroupSelectDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
return true
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (isMoved) {
for ((index, item) in getItems().withIndex()) {
item.order = index + 1

@ -32,6 +32,13 @@ class GroupViewModel(application: Application) : BaseViewModel(application) {
fun delGroup(vararg bookGroup: BookGroup) {
execute {
App.db.bookGroupDao().delete(*bookGroup)
bookGroup.forEach { group ->
val books = App.db.bookDao().getBooksByGroup(group.groupId)
books.forEach {
it.group = it.group - group.groupId
}
App.db.bookDao().update(*books.toTypedArray())
}
}
}

@ -57,8 +57,7 @@ class BookInfoActivity :
get() = getViewModel(BookInfoViewModel::class.java)
override fun onActivityCreated(savedInstanceState: Bundle?) {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
title_bar.transparent()
viewModel.bookData.observe(this, Observer { showBook(it) })
viewModel.chapterListData.observe(this, Observer { upLoading(false, it) })
viewModel.initData(intent)
@ -231,6 +230,9 @@ class BookInfoActivity :
tv_author.onClick {
startActivity<SearchActivity>(Pair("key", viewModel.bookData.value?.author))
}
tv_name.onClick {
startActivity<SearchActivity>(Pair("key", viewModel.bookData.value?.name))
}
}
@SuppressLint("InflateParams")

@ -133,6 +133,8 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
execute {
if (inBookshelf) {
bookData.value?.let {
book.group = it.group
book.order = it.order
App.db.bookDao().delete(it)
}
App.db.bookDao().insert(book)
@ -164,6 +166,9 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
fun saveBook(success: (() -> Unit)? = null) {
execute {
bookData.value?.let { book ->
if (book.order == 0) {
book.order = App.db.bookDao().maxOrder + 1
}
App.db.bookDao().insert(book)
}
}.onSuccess {
@ -184,6 +189,9 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
fun addToBookshelf(success: (() -> Unit)?) {
execute {
bookData.value?.let { book ->
if (book.order == 0) {
book.order = App.db.bookDao().maxOrder + 1
}
App.db.bookDao().insert(book)
}
chapterListData.value?.let {

@ -609,9 +609,6 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
* 朗读按钮
*/
override fun onClickReadAloud() {
if (!BaseReadAloudService.isRun) {
SystemUtils.ignoreBatteryOptimization(this)
}
when {
!BaseReadAloudService.isRun -> ReadBook.readAloud()
BaseReadAloudService.pause -> ReadAloud.resume(this)

@ -136,6 +136,8 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
execute {
ReadBook.upMsg(null)
ReadBook.book?.let {
book1.group = it.group
book1.order = it.order
App.db.bookDao().delete(it)
}
ReadBook.prevTextChapter = null

@ -252,8 +252,8 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
return super.onMove(srcPosition, targetPosition)
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.onClearView(recyclerView, viewHolder)
if (isMoved) {
for ((index, item) in getItems().withIndex()) {
item.serialNumber = index + 1

@ -12,10 +12,7 @@ data class TextChapter(
val chaptersSize: Int
) {
fun page(index: Int): TextPage? {
if (index >= 0 && index < pages.size) {
return pages[index]
}
return null
return pages.getOrNull(index)
}
fun lastPage(): TextPage? {
@ -25,20 +22,6 @@ data class TextChapter(
return null
}
fun scrollPage(): TextPage? {
if (pages.isNotEmpty()) {
val stringBuilder = StringBuilder()
pages.forEach {
stringBuilder.append(it.text)
}
return TextPage(
index = 0, text = stringBuilder.toString(), title = title,
pageSize = pages.size, chapterSize = chaptersSize, chapterIndex = position
)
}
return null
}
fun lastIndex(): Int {
return pages.size - 1
}
@ -70,28 +53,6 @@ data class TextChapter(
return stringBuilder.toString()
}
fun getStartLine(pageIndex: Int): Int {
if (pageLines.size > pageIndex) {
var lines = 0
for (index: Int in 0 until pageIndex) {
lines += pageLines[index] + 1
}
return lines
}
return 0
}
fun getPageIndex(line: Int): Int {
var lines = 0
for (pageIndex in pageLines.indices) {
lines += pageLines[pageIndex] + 1
if (line < lines) {
return pageIndex
}
}
return 0
}
fun getContent(): String {
val stringBuilder = StringBuilder()
pages.forEach {

@ -104,10 +104,16 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
when (it.path) {
"/importonline" -> it.getQueryParameter("src")?.let { url ->
Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
if (url.startsWith("http", false)){
viewModel.importSource(url) { msg ->
title_bar.snackbar(msg)
}
}
else{
viewModel.importSourceFromFilePath(url){msg ->
title_bar.snackbar(msg)}
}
}
else -> {
toast("格式不对")
}

@ -185,7 +185,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
private val movedItems = hashSetOf<BookSource>()
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (movedItems.isNotEmpty()) {
callBack.update(*movedItems.toTypedArray())
movedItems.clear()

@ -8,7 +8,6 @@ import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.BookSource
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.OldRule
import io.legado.app.help.storage.Restore.jsonPath
import io.legado.app.utils.*
@ -96,7 +95,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
FileUtils.createFileIfNotExist(file, "exportBookSource.json")
.writeText(json)
}.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}")
context.toast("成功导出至\n${file.absolutePath}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@ -109,7 +108,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
doc.createFile("", "exportBookSource.json")
?.writeText(context, json)
}.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}")
context.toast("成功导出至\n${doc.uri.path}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}

@ -48,23 +48,19 @@ object BackupRestoreUi {
selectBackupFolder(fragment, backupSelectRequestCode)
}
} else {
backupUsePermission(fragment, requestCode = backupSelectRequestCode)
backupUsePermission(fragment, backupPath)
}
}
}
private fun backupUsePermission(
fragment: Fragment,
path: String = Backup.legadoPath,
requestCode: Int = selectFolderRequestCode
path: String
) {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
when (requestCode) {
selectFolderRequestCode -> AppConfig.backupPath = Backup.legadoPath
else -> {
Coroutine.async {
AppConfig.backupPath = path
Backup.backup(fragment.requireContext(), path)
@ -72,8 +68,6 @@ object BackupRestoreUi {
fragment.toast(R.string.backup_success)
}
}
}
}
.request()
}
@ -107,7 +101,7 @@ object BackupRestoreUi {
}
}
private fun restoreUsePermission(fragment: Fragment, path: String = Backup.legadoPath) {
private fun restoreUsePermission(fragment: Fragment, path: String) {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)

@ -0,0 +1,39 @@
package io.legado.app.ui.config
import android.content.Intent
import android.os.Bundle
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.ui.main.MainActivity
import io.legado.app.utils.getViewModel
class FileAssociationActivity :
VMBaseActivity<FileAssociationViewModel>(R.layout.activity_file_association) {
override val viewModel: FileAssociationViewModel
get() = getViewModel(FileAssociationViewModel::class.java)
override fun onActivityCreated(savedInstanceState: Bundle?) {
intent.data?.let { data ->
val newIntent = viewModel.dispatchIndent(data)
if (newIntent != null) {
this.startActivityForResult(newIntent, 100)
} else {
gotoMainActivity()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
//返回后直接跳转到主页面
gotoMainActivity()
}
private fun gotoMainActivity() {
val mIntent = Intent()
mIntent.setClass(this, MainActivity::class.java)
startActivity(mIntent)
}
}

@ -0,0 +1,69 @@
package io.legado.app.ui.config
import android.app.Application
import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import androidx.documentfile.provider.DocumentFile
import io.legado.app.base.BaseViewModel
import io.legado.app.model.localBook.LocalBook
import io.legado.app.utils.isJsonArray
import io.legado.app.utils.isJsonObject
import io.legado.app.utils.readText
import java.io.File
class FileAssociationViewModel(application: Application) : BaseViewModel(application) {
fun dispatchIndent(uri: Uri): Intent? {
val url: String
//如果是普通的url,需要根据返回的内容判断是什么
if (uri.scheme == "file" || uri.scheme == "content") {
val content = if (uri.scheme == "file") {
val file = File(uri.path.toString())
if (file.exists()) {
file.readText()
} else {
null
}
} else {
DocumentFile.fromSingleUri(context, uri)?.readText(context)
}
var scheme = ""
if (content != null) {
if (content.isJsonObject() || content.isJsonArray()) {
//暂时根据文件内容判断属于什么
when {
content.contains("bookSourceUrl") -> {
scheme = "booksource"
}
content.contains("sourceUrl") -> {
scheme = "rsssource"
}
content.contains("pattern") -> {
scheme = "replace"
}
}
}
if (TextUtils.isEmpty(scheme)) {
execute {
LocalBook.importFile(uri.path.toString())
toast("添加本地文件成功${uri.path}")
}
return null
}
} else {
toast("文件不存在")
return null
}
url = "yuedu://${scheme}/importonline?src=${uri.path}"
} else if (uri.scheme == "yuedu") {
url = uri.toString()
} else {
url = "yuedu://booksource/importonline?src=${uri.path}"
}
val data = Uri.parse(url)
val newIndent = Intent(Intent.ACTION_VIEW)
newIndent.data = data
return newIndent
}
}

@ -43,6 +43,7 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
)
WebBook(bookSource).getBookInfo(book, this)
.onSuccess(IO) {
it.order = App.db.bookDao().maxOrder + 1
App.db.bookDao().insert(it)
successCount++
}.onError {

@ -81,10 +81,17 @@ class ReplaceRuleActivity : VMBaseActivity<ReplaceRuleViewModel>(R.layout.activi
when (it.path) {
"/importonline" -> it.getQueryParameter("src")?.let { url ->
Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
if (url.startsWith("http", false)){
viewModel.importSource(url) { msg ->
title_bar.snackbar(msg)
}
}
else{
viewModel.importSourceFromFilePath(url) { msg ->
title_bar.snackbar(msg)
}
}
}
else -> {
toast("格式不对")
}

@ -148,7 +148,7 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) :
private val movedItems = linkedSetOf<ReplaceRule>()
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (movedItems.isNotEmpty()) {
callBack.update(*movedItems.toTypedArray())
movedItems.clear()

@ -8,14 +8,28 @@ import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.ImportOldData
import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast
import java.io.File
class ReplaceRuleViewModel(application: Application) : BaseViewModel(application) {
fun importSourceFromFilePath(path: String, finally: (msg: String) -> Unit) {
execute {
val file = File(path)
if (file.exists()) {
importSource(file.readText(), finally)
} else {
withContext(Dispatchers.Main) {
finally("打开文件出错")
}
}
}.onError {
finally(it.localizedMessage ?: "打开文件出错")
}
}
fun importSource(text: String, showMsg: (msg: String) -> Unit) {
execute {
if (text.isAbsUrl()) {
@ -93,7 +107,7 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
FileUtils.createFileIfNotExist(file, "exportReplaceRule.json")
.writeText(json)
}.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}")
context.toast("成功导出至\n${file.absolutePath}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@ -106,7 +120,7 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
doc.createFile("", "exportReplaceRule.json")
?.writeText(context, json)
}.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}")
context.toast("成功导出至\n${doc.uri.path}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}

@ -193,7 +193,7 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
}
override fun upStarMenu() {
if (viewModel.star) {
if (viewModel.rssStar != null) {
starMenuItem?.setIcon(R.drawable.ic_star)
starMenuItem?.setTitle(R.string.in_favorites)
} else {

@ -15,6 +15,7 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppConst
import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.RssStar
import io.legado.app.help.http.HttpHelper
import io.legado.app.model.Rss
import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -22,6 +23,7 @@ import io.legado.app.utils.DocumentUtils
import io.legado.app.utils.FileUtils
import io.legado.app.utils.isContentPath
import io.legado.app.utils.writeBytes
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import java.io.File
import java.util.*
@ -34,7 +36,7 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
var rssArticle: RssArticle? = null
val contentLiveData = MutableLiveData<String>()
val urlLiveData = MutableLiveData<AnalyzeUrl>()
var star = false
var rssStar: RssStar? = null
var textToSpeech: TextToSpeech? = null
private var ttsInitFinish = false
private var ttsTextList = arrayListOf<String>()
@ -45,8 +47,8 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
val link = intent.getStringExtra("link")
if (origin != null && link != null) {
rssSource = App.db.rssSourceDao().getByKey(origin)
star = App.db.rssStarDao().get(origin, link) != null
rssArticle = App.db.rssArticleDao().get(origin, link)
rssStar = App.db.rssStarDao().get(origin, link)
rssArticle = rssStar?.toRssArticle() ?: App.db.rssArticleDao().get(origin, link)
rssArticle?.let { rssArticle ->
if (!rssArticle.description.isNullOrBlank()) {
contentLiveData.postValue(rssArticle.description)
@ -78,21 +80,26 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
}
private fun loadContent(rssArticle: RssArticle, ruleContent: String) {
Rss.getContent(rssArticle, ruleContent, this)
.onSuccess {
contentLiveData.postValue(it)
Rss.getContent(rssArticle, ruleContent, rssSource, this)
.onSuccess(IO) { body ->
rssArticle.description = body
App.db.rssArticleDao().insert(rssArticle)
rssStar?.let {
it.description = body
App.db.rssStarDao().insert(it)
}
contentLiveData.postValue(body)
}
}
fun favorite() {
execute {
rssArticle?.let {
if (star) {
rssStar?.let {
App.db.rssStarDao().delete(it.origin, it.link)
} else {
App.db.rssStarDao().insert(it.toStar())
}
star = !star
rssStar = null
} ?: rssArticle?.toStar()?.let {
App.db.rssStarDao().insert(it)
rssStar = it
}
}.onSuccess {
callBack?.upStarMenu()

@ -117,10 +117,17 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
when (it.path) {
"/importonline" -> it.getQueryParameter("src")?.let { url ->
Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
if (url.startsWith("http", false)){
viewModel.importSource(url) { msg ->
title_bar.snackbar(msg)
}
}
else{
viewModel.importSourceFromFilePath(url) { msg ->
title_bar.snackbar(msg)
}
}
}
else -> {
toast("格式不对")
}

@ -139,7 +139,7 @@ class RssSourceAdapter(context: Context, val callBack: CallBack) :
private val movedItems = hashSetOf<RssSource>()
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (movedItems.isNotEmpty()) {
callBack.update(*movedItems.toTypedArray())
movedItems.clear()

@ -9,7 +9,6 @@ import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.RssSource
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.Restore.jsonPath
import io.legado.app.utils.*
import org.jetbrains.anko.toast
@ -74,7 +73,7 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application)
FileUtils.createFileIfNotExist(file, "exportRssSource.json")
.writeText(json)
}.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}")
context.toast("成功导出至\n${file.absolutePath}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@ -87,7 +86,7 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application)
doc.createFile("", "exportRssSource.json")
?.writeText(context, json)
}.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}")
context.toast("成功导出至\n${doc.uri.path}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}

@ -2,6 +2,7 @@ package io.legado.app.ui.widget
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet
import android.view.Menu
import android.view.View
@ -186,6 +187,11 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a
toolbar.setSubtitleTextAppearance(context, resId)
}
fun transparent() {
elevation = 0f
backgroundColor = Color.TRANSPARENT
}
private fun attachToActivity() {
if (attachToActivity) {
activity?.let {

@ -66,7 +66,7 @@ public class EncodingDetect {
}
public static String getEncode(@NonNull byte[] bytes) {
int len = bytes.length > 2000 ? 2000 : bytes.length;
int len = Math.min(bytes.length, 2000);
byte[] cBytes = new byte[len];
System.arraycopy(bytes, 0, cBytes, 0, len);
BytesEncodingDetect bytesEncodingDetect = new BytesEncodingDetect();
@ -1010,14 +1010,8 @@ class BytesEncodingDetect extends Encoding {
column = rawtext[i + 1] + 256;
if (column < 0x9f) {
adjust = 1;
if (column > 0x7f) {
column -= 0x20;
} else {
column -= 0x19;
}
} else {
adjust = 0;
column -= 0x7e;
}
if (row < 0xa0) {
row = ((row - 0x70) << 1) - adjust;

@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22dp"
android:height="22dp"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path

@ -14,13 +14,10 @@
android:contentDescription="@string/bg_image"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
android:fitsSystemWindows="true"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/book_info" />
@ -81,7 +78,8 @@
android:paddingLeft="10dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="3dp">
android:paddingBottom="3dp"
tools:ignore="RtlHardcoded">
<TextView
android:id="@+id/tv_name"
@ -147,14 +145,13 @@
android:layout_weight="1.0"
android:orientation="vertical"
android:layout_marginTop="65dp"
android:layout_marginLeft="1px"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/background"
android:fillViewport="true"
@ -162,7 +159,8 @@
android:focusable="true"
android:padding="0dp"
android:scrollbarStyle="outsideInset"
android:scrollbars="vertical">
android:scrollbars="vertical"
tools:ignore="NestedWeights">
<LinearLayout
android:layout_width="match_parent"
@ -212,7 +210,7 @@
android:text="@string/origin_format"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_change_source"
@ -257,7 +255,7 @@
android:text="@string/read_dur_progress"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
</LinearLayout>
@ -290,7 +288,7 @@
android:text="@string/group_s"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_change_group"
@ -334,7 +332,7 @@
android:text="@string/toc_s"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_toc_view"
@ -373,7 +371,7 @@
android:layout_marginBottom="1px"
android:layout_width="match_parent"
android:layout_height="8dp"
android:background="@color/background"></View>
android:background="@color/background" />
<LinearLayout
android:id="@+id/fl_action"

@ -21,13 +21,10 @@
android:layout_height="match_parent"
android:background="#50000000" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
android:fitsSystemWindows="true"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_constraintTop_toTopOf="parent" />
<TextView
@ -40,7 +37,7 @@
android:paddingStart="3dp"
android:paddingEnd="3dp"
android:visibility="invisible"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintTop_toBottomOf="@id/title_bar"
app:layout_constraintRight_toRightOf="parent" />
<io.legado.app.ui.widget.image.CircleImageView
@ -54,7 +51,7 @@
app:layout_constraintBottom_toTopOf="@+id/ll_player_progress"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
app:layout_constraintTop_toBottomOf="@+id/title_bar" />
<TextView
android:id="@+id/tv_sub_title"

@ -21,13 +21,10 @@
android:background="#50000000"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
android:fitsSystemWindows="true"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/book_info" />

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="io.legado.app.ui.config.FileAssociationActivity">
</androidx.constraintlayout.widget.ConstraintLayout>

@ -6,7 +6,7 @@
<item
android:id="@+id/menu_stop"
android:title="@string/stop"
android:icon="@drawable/ic_stop_black_24dp"
android:icon="@drawable/ic_refresh_white_24dp"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />

@ -14,7 +14,7 @@
<item
android:id="@+id/menu_stop"
android:title="@string/stop"
android:icon="@drawable/ic_stop_black_24dp"
android:icon="@drawable/ic_refresh_white_24dp"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />

Loading…
Cancel
Save