Merge pull request #34 from gedoor/master

up
pull/379/head
口口吕 5 years ago committed by GitHub
commit 3e3f523aa4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      app/src/debug/res/values/strings.xml
  2. 14
      app/src/main/AndroidManifest.xml
  3. 16
      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. 7
      app/src/main/java/io/legado/app/help/ItemTouchCallback.kt
  7. 9
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  8. 10
      app/src/main/java/io/legado/app/service/BaseReadAloudService.kt
  9. 2
      app/src/main/java/io/legado/app/ui/README.md
  10. 1
      app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
  11. 18
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
  12. 35
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt
  13. 13
      app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt
  14. 2
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  15. 2
      app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
  16. 7
      app/src/main/java/io/legado/app/ui/book/group/GroupViewModel.kt
  17. 8
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  18. 3
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  19. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  20. 4
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  21. 41
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt
  22. 10
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  23. 2
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceAdapter.kt
  24. 39
      app/src/main/java/io/legado/app/ui/config/FileAssociationActivity.kt
  25. 69
      app/src/main/java/io/legado/app/ui/config/FileAssociationViewModel.kt
  26. 1
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
  27. 11
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
  28. 2
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleAdapter.kt
  29. 17
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt
  30. 11
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  31. 2
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
  32. 8
      app/src/main/java/io/legado/app/utils/EncodingDetect.java
  33. 8
      app/src/main/res/layout/activity_file_association.xml
  34. 2
      app/src/main/res/layout/activity_welcome.xml
  35. 2
      app/src/main/res/menu/change_cover.xml
  36. 2
      app/src/main/res/menu/change_source.xml
  37. 2
      app/src/main/res/values-night/colors.xml
  38. 2
      app/src/main/res/values/colors.xml
  39. 11
      app/src/main/res/values/strings.xml
  40. 2
      app/src/main/res/xml/pref_config_read.xml

@ -1,4 +1,4 @@
<resources> <resources>
<string name="app_name">读.debug</string> <string name="app_name">读.debug</string>
<string name="receiving_shared_label">读.debug·搜索</string> <string name="receiving_shared_label">读.debug·搜索</string>
</resources> </resources>

@ -287,7 +287,19 @@
<data android:mimeType="text/plain" /> <data android:mimeType="text/plain" />
</intent-filter> </intent-filter>
</activity> </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.CheckSourceService" />
<service android:name=".service.DownloadService" /> <service android:name=".service.DownloadService" />
<service android:name=".service.WebService" /> <service android:name=".service.WebService" />

@ -3,6 +3,13 @@
* 请关注[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 * 请关注[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
* 弄了个企业公众号[开源阅读](),后面弄好后会把原来的[开源阅读软件]()迁移过来 * 弄了个企业公众号[开源阅读](),后面弄好后会把原来的[开源阅读软件]()迁移过来
**2020/03/22**
* 添加文件关联 by wqfantexi
* 手动排序可以了,在书架整理里面拖动排序
* 删除分组时同时删除书籍里的分组信息,下次添加新分组时不会自动出现在分组内
* 修复换源丢失分组信息的bug
* 修复部分朗读引擎不自动朗读下一章的bug
**2020/03/21** **2020/03/21**
* 详情页点击书名搜索 * 详情页点击书名搜索
@ -266,14 +273,7 @@
- 调试正文页>>输入正文页URL,如:`--https://www.zhaishuyuan.com/chapter/30394/20940996` - 调试正文页>>输入正文页URL,如:`--https://www.zhaishuyuan.com/chapter/30394/20940996`
* 修改订阅中自动添加style的情景 * 修改订阅中自动添加style的情景
订阅源的内容规则中存在`<style>``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>
```
**2019/12/28** **2019/12/28**
* 添加下载界面 * 添加下载界面

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

@ -12,16 +12,16 @@ interface BookDao {
@Query("SELECT * FROM books order by durChapterTime desc") @Query("SELECT * FROM books order by durChapterTime desc")
fun observeAll(): LiveData<List<Book>> 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>> 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>> 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>> 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>> fun observeDownload(): LiveData<List<Book>>
@Query("SELECT * FROM books WHERE (`group` & :group) > 0") @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||'%'") @Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'")
fun liveDataSearch(key: String): LiveData<List<Book>> 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)") @Query("SELECT * FROM books WHERE `name` in (:names)")
fun findByName(vararg names: String): List<Book> fun findByName(vararg names: String): List<Book>
@ -57,6 +60,9 @@ interface BookDao {
@get:Query("SELECT COUNT(*) FROM books") @get:Query("SELECT COUNT(*) FROM books")
val allBookCount: Int val allBookCount: Int
@get:Query("select max(`order`) from books")
val maxOrder: Int
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg book: Book) fun insert(vararg book: Book)

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

@ -31,11 +31,10 @@ object Backup {
fun autoBack(context: Context) { fun autoBack(context: Context) {
val lastBackup = context.getPrefLong(PreferKey.lastBackup) val lastBackup = context.getPrefLong(PreferKey.lastBackup)
if (lastBackup + TimeUnit.DAYS.toMillis(1) < System.currentTimeMillis()) { if (lastBackup + TimeUnit.DAYS.toMillis(1) < System.currentTimeMillis()) {
return Coroutine.async {
} context.getPrefString(PreferKey.backupPath)?.let {
Coroutine.async { backup(context, it, true)
context.getPrefString(PreferKey.backupPath)?.let { }
backup(context, it, true)
} }
} }
} }

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

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

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

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

@ -2,14 +2,18 @@ package io.legado.app.ui.book.arrange
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.help.ItemTouchCallback import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.theme.backgroundColor
import kotlinx.android.synthetic.main.item_arrange_book.view.* import kotlinx.android.synthetic.main.item_arrange_book.view.*
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.*
class ArrangeBookAdapter(context: Context, val callBack: CallBack) : class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<Book>(context, R.layout.item_arrange_book), 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>) { override fun convert(holder: ItemViewHolder, item: Book, payloads: MutableList<Any>) {
with(holder.itemView) { with(holder.itemView) {
backgroundColor = context.backgroundColor
tv_name.text = item.name tv_name.text = item.name
tv_author.text = item.author tv_author.text = item.author
tv_author.visibility = if (item.author.isEmpty()) View.GONE else View.VISIBLE 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(",") 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 { interface CallBack {
val groupList: List<BookGroup> val groupList: List<BookGroup>
fun upSelectCount() fun upSelectCount()
fun updateBook(vararg book: Book)
fun deleteBook(book: Book) fun deleteBook(book: Book)
fun selectGroup(groupId: Int, requestCode: Int) fun selectGroup(groupId: Int, requestCode: Int)
} }

@ -13,6 +13,7 @@ import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus 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.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
@ -81,9 +82,15 @@ class DownloadActivity : VMBaseActivity<DownloadViewModel>(R.layout.activity_dow
private fun initLiveData() { private fun initLiveData() {
bookshelfLiveData?.removeObservers(this) bookshelfLiveData?.removeObservers(this)
bookshelfLiveData = App.db.bookDao().observeDownload() bookshelfLiveData = App.db.bookDao().observeDownload()
bookshelfLiveData?.observe(this, Observer { bookshelfLiveData?.observe(this, Observer { list ->
adapter.setItems(it) val books = when (getPrefInt(PreferKey.bookshelfSort)) {
initCacheSize(it) 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 return true
} }
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (isMoved) { if (isMoved) {
for ((index, item) in getItems().withIndex()) { for ((index, item) in getItems().withIndex()) {
item.order = index + 1 item.order = index + 1

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

@ -32,6 +32,13 @@ class GroupViewModel(application: Application) : BaseViewModel(application) {
fun delGroup(vararg bookGroup: BookGroup) { fun delGroup(vararg bookGroup: BookGroup) {
execute { execute {
App.db.bookGroupDao().delete(*bookGroup) 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())
}
} }
} }

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

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

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

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

@ -12,10 +12,7 @@ data class TextChapter(
val chaptersSize: Int val chaptersSize: Int
) { ) {
fun page(index: Int): TextPage? { fun page(index: Int): TextPage? {
if (index >= 0 && index < pages.size) { return pages.getOrNull(index)
return pages[index]
}
return null
} }
fun lastPage(): TextPage? { fun lastPage(): TextPage? {
@ -25,20 +22,6 @@ data class TextChapter(
return null 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 { fun lastIndex(): Int {
return pages.size - 1 return pages.size - 1
} }
@ -70,28 +53,6 @@ data class TextChapter(
return stringBuilder.toString() 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 { fun getContent(): String {
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
pages.forEach { pages.forEach {

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

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

@ -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) WebBook(bookSource).getBookInfo(book, this)
.onSuccess(IO) { .onSuccess(IO) {
it.order = App.db.bookDao().maxOrder + 1
App.db.bookDao().insert(it) App.db.bookDao().insert(it)
successCount++ successCount++
}.onError { }.onError {

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

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

@ -10,11 +10,26 @@ import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.http.HttpHelper import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.ImportOldData import io.legado.app.help.storage.ImportOldData
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import java.io.File import java.io.File
class ReplaceRuleViewModel(application: Application) : BaseViewModel(application) { 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) { fun importSource(text: String, showMsg: (msg: String) -> Unit) {
execute { execute {
if (text.isAbsUrl()) { if (text.isAbsUrl()) {

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

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

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

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

@ -22,7 +22,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ems="1" android:ems="1"
android:text="读" android:text="读"
android:textSize="49sp" android:textSize="49sp"
app:layout_constraintVertical_bias="0.3" app:layout_constraintVertical_bias="0.3"
app:layout_constraintRight_toLeftOf="@+id/tv_sub_title" app:layout_constraintRight_toLeftOf="@+id/tv_sub_title"

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

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

@ -7,7 +7,7 @@
<color name="background">@color/md_grey_900</color> <color name="background">@color/md_grey_900</color>
<color name="background_card">@color/md_grey_850</color> <color name="background_card">@color/md_grey_850</color>
<color name="background_menu">@color/md_grey_800</color> <color name="background_menu">@color/md_grey_800</color>
<color name="background_prefs">#10303030</color> <color name="background_prefs">#AA111111</color>
<color name="night_mask">#69000000</color> <color name="night_mask">#69000000</color>

@ -16,7 +16,7 @@
<color name="background">@color/md_grey_50</color> <color name="background">@color/md_grey_50</color>
<color name="background_card">@color/md_grey_100</color> <color name="background_card">@color/md_grey_100</color>
<color name="background_menu">@color/md_grey_200</color> <color name="background_menu">@color/md_grey_200</color>
<color name="background_prefs">@color/md_grey_50</color> <color name="background_prefs">#AAFFFFFF</color>
<color name="transparent">#00000000</color> <color name="transparent">#00000000</color>
<color name="transparent10">#10000000</color> <color name="transparent10">#10000000</color>

@ -1,5 +1,5 @@
<resources> <resources>
<string name="app_name"></string> <string name="app_name"></string>
<string name="navigation_drawer_open">Open navigation drawer</string> <string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string> <string name="navigation_drawer_close">Close navigation drawer</string>
<string name="nav_header_title">Android Studio</string> <string name="nav_header_title">Android Studio</string>
@ -20,7 +20,7 @@
<string name="dialog_setting">去设置</string> <string name="dialog_setting">去设置</string>
<string name="tip_cannot_jump_setting_page">无法跳转至设置界面</string> <string name="tip_cannot_jump_setting_page">无法跳转至设置界面</string>
<string name="tip_perm_request_storage">读需要访问存储卡权限,请前往“设置”—“应用权限”—打开所需权限</string> <string name="tip_perm_request_storage">读需要访问存储卡权限,请前往“设置”—“应用权限”—打开所需权限</string>
<string name="dynamic_click_retry">点击重试</string> <string name="dynamic_click_retry">点击重试</string>
<string name="dynamic_loading">正在加载</string> <string name="dynamic_loading">正在加载</string>
@ -42,9 +42,8 @@
<string name="all">全部</string> <string name="all">全部</string>
<string name="recent_reading">最近阅读</string> <string name="recent_reading">最近阅读</string>
<string name="last_read">最后阅读</string> <string name="last_read">最后阅读</string>
<string name="read_summary">让阅读成为一种习惯。</string>
<string name="update_log">更新日志</string> <string name="update_log">更新日志</string>
<string name="receiving_shared_label">读·搜索</string> <string name="receiving_shared_label">读·搜索</string>
<string name="bookshelf_empty">书架还空着,先去添加吧!</string> <string name="bookshelf_empty">书架还空着,先去添加吧!</string>
<string name="action_search">搜索</string> <string name="action_search">搜索</string>
<string name="action_download">下载</string> <string name="action_download">下载</string>
@ -132,8 +131,6 @@
<string name="previous_chapter">上一章</string> <string name="previous_chapter">上一章</string>
<string name="pt_hide_status_bar">隐藏状态栏</string> <string name="pt_hide_status_bar">隐藏状态栏</string>
<string name="ps_hide_status_bar">阅读界面隐藏状态栏</string> <string name="ps_hide_status_bar">阅读界面隐藏状态栏</string>
<string name="pt_read_line_adjust">阅读行数调整</string>
<string name="ps_read_line_adjust">阅读行数减一行,如阅读界面显示不全可启用</string>
<string name="read_aloud">朗读</string> <string name="read_aloud">朗读</string>
<string name="read_aloud_t">正在朗读</string> <string name="read_aloud_t">正在朗读</string>
<string name="read_aloud_s">点击打开阅读界面</string> <string name="read_aloud_s">点击打开阅读界面</string>
@ -612,7 +609,7 @@
<string name="sys_folder_picker">系统文件夹选择器</string> <string name="sys_folder_picker">系统文件夹选择器</string>
<string name="app_folder_picker">自带选择器\n(Android10以上因权限限制可能无法使用)</string> <string name="app_folder_picker">自带选择器\n(Android10以上因权限限制可能无法使用)</string>
<string name="a10_permission_toast">Android10以上因权限限制可能无法读写文件</string> <string name="a10_permission_toast">Android10以上因权限限制可能无法读写文件</string>
<string name="add_to_text_context_menu_s">长按文字在操作菜单中显示读·搜索</string> <string name="add_to_text_context_menu_s">长按文字在操作菜单中显示读·搜索</string>
<string name="add_to_text_context_menu_t">文字操作显示搜索</string> <string name="add_to_text_context_menu_t">文字操作显示搜索</string>
<string name="record_log">记录日志</string> <string name="record_log">记录日志</string>
<string name="chinese_converter">中文简繁体转换</string> <string name="chinese_converter">中文简繁体转换</string>

@ -60,7 +60,7 @@
android:key="selectText" android:key="selectText"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<Preference <io.legado.app.ui.widget.prefs.Preference
android:key="customPageKey" android:key="customPageKey"
android:title="@string/custom_page_key" android:title="@string/custom_page_key"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />

Loading…
Cancel
Save