Merge pull request #3 from gedoor/master

更新
pull/282/head
10bits 4 years ago committed by GitHub
commit e34fd9bf0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 2
      app/build.gradle
  3. 6
      app/src/main/AndroidManifest.xml
  4. 79
      app/src/main/assets/18PlusList.txt
  5. 18
      app/src/main/assets/updateLog.md
  6. 3
      app/src/main/java/io/legado/app/data/entities/rule/ContentRule.kt
  7. 2
      app/src/main/java/io/legado/app/help/AppConfig.kt
  8. 14
      app/src/main/java/io/legado/app/help/SourceHelp.kt
  9. 55
      app/src/main/java/io/legado/app/help/http/AjaxWebView.kt
  10. 6
      app/src/main/java/io/legado/app/lib/theme/ATH.kt
  11. 14
      app/src/main/java/io/legado/app/model/webBook/BookContent.kt
  12. 28
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  13. 4
      app/src/main/java/io/legado/app/ui/book/read/Help.kt
  14. 6
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  15. 97
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  16. 5
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  17. 5
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/NoAnimPageDelegate.kt
  18. 16
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  19. 1
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
  20. 29
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt
  21. 5
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ImageProvider.kt
  22. 4
      app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
  23. 7
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
  24. 4
      app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
  25. 4
      app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
  26. 6
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
  27. 68
      app/src/main/java/io/legado/app/ui/widget/dialog/PhotoDialog.kt
  28. 1267
      app/src/main/java/io/legado/app/ui/widget/image/PhotoView.kt
  29. 48
      app/src/main/java/io/legado/app/ui/widget/image/photo/Info.kt
  30. 56
      app/src/main/java/io/legado/app/ui/widget/image/photo/RotateGestureDetector.kt
  31. 15
      app/src/main/res/layout/dialog_photo_view.xml
  32. 14
      app/src/main/res/values-zh-rHK/strings.xml
  33. 14
      app/src/main/res/values-zh-rTW/strings.xml
  34. 4
      app/src/main/res/values-zh/strings.xml
  35. 66
      app/src/main/res/values/strings.xml

@ -5,5 +5,7 @@
## 阅读3.0
书源规则 https://celeter.github.io
[![aVElJs.th.jpg](https://s1.ax1x.com/2020/07/28/aVElJs.th.jpg)](https://imgchr.com/i/aVElJs) [![aVEQij.th.jpg](https://s1.ax1x.com/2020/07/28/aVEQij.th.jpg)](https://imgchr.com/i/aVEQij) [![aVEudg.th.jpg](https://s1.ax1x.com/2020/07/28/aVEudg.th.jpg)](https://imgchr.com/i/aVEudg) [![aVEneS.th.jpg](https://s1.ax1x.com/2020/07/28/aVEneS.th.jpg)](https://imgchr.com/i/aVEneS) [![aVEKoQ.th.jpg](https://s1.ax1x.com/2020/07/28/aVEKoQ.th.jpg)](https://imgchr.com/i/aVEKoQ) [![aVE1Wn.th.jpg](https://s1.ax1x.com/2020/07/28/aVE1Wn.th.jpg)](https://imgchr.com/i/aVE1Wn)
## 免责声明
https://gedoor.github.io/MyBookshelf/disclaimer.html

@ -124,7 +124,7 @@ dependencies {
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
//androidX
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.media:media:1.1.0'
implementation 'androidx.preference:preference:1.1.1'

@ -295,10 +295,12 @@
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:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="text/plain" />
<data android:mimeType="application/json" />
<data android:mimeType="application/epub" />

@ -1,3 +1,82 @@
OGN5dS5jb20=
c2cwMC54eXo=
aXRyYWZmaWNuZXQuY29t
eGlhb3FpYW5nNTIw
MTIzeGlhb3FpYW5n
eGlhb3FpYW5neHM=
eGlhb3FpYW5nNTIw
MzM1eHM=
eGN4czk=
eGN4czUyMA==
c2h1YmFvYW4=
c2h1YmFvd2FuZzEyMw==
c2h1YmFvYW4=
aGFpdGFuZzEyMw==
eXV6aGFpd3VsYQ==
cG8xOA==
Ymwtbm92ZWw=
NXRucw==
c2hhb3NodWdl
amluamlzaHV3dQ==
NDJ3Zw==
eWlxdXNodQ==
c2h1YmFvd2FuZzEyMw==
M2hlYmFv
MzNoZWJhbw==
bHVvcWl1enc=
bXlzaHVnZQ==
c3NzeHN3
eWl0ZQ==
Y3Vpd2VpanV1
Y3Vpd2VpanV4cw==
Y3Vpd2VpanV4
eGlhb3FpYW5nd3g=
YXN6dw==
YXN6dzY=
c2FuaGFveHM=
ODdzaHV3dQ==
NDh3eA==
bG9uZ3Rlbmcy
NnF3eA==
bG9uZ3Rlbmd4cw==
aGF4ZHU=
M3R3eA==
aGF4d3g1
NjZsZXdlbg==
eGJhbnpodQ==
aGR5cA==
ZHliejk=
ZGl5aWJhbnpodTk=
ZGl5aWJhbnpodQ==
ZGl5aWJhbnpodTc=
YnoyMjI=
d29kZWFwaTAwMQ==
dGFuZ3poZWthbg==
YmF4aWFueHM=
eGlhb3NodW9zaGVuemhhbg==
ZGFtb2tl
emh3ZW5wZw==
eXV6aGFpZ2U=
d21wOA==
OXhpYW53ZW4=
bmFucmVudmlw
cmV5b28=
eWZ4aWFvc2h1b2U=
c2Fuaml1enc=
N3Fpbmc3
cWR4aWFvc2h1bw==
Y2hpbmVzZXpq
MzlzaHViYW8=
a3l4czU=
NTZtcw==
bml1c2hh
bWt4czY=
MjIyMjJ4cw==
OTVkdXNodQ==
YmFuemh1MjI=
d3JsdHh0
dHVkb3V0eHQ=
cm5neHM=
OTl3ZW5rdQ==
bGFvc2lqaXhz
ZnVzaHV6aGFpMQ==

@ -3,6 +3,24 @@
* 请关注公众号[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
* 新公众号[开源阅读]()已启用,[开源阅读软件]()备用
**2020/07/30**
* 所有版本都加上书源屏蔽列表,有其它想法的可以自己编译软件,反正是开源的
**2020/07/29**
* 正文图片样式为FULL的自动为滚动模式
**2020/07/28**
* 长图正文规则添加图片样式FULL,可以滚动浏览了
**2020/07/26**
* 优化翻页,加快翻页速度
**2020/07/25**
* 正文规则添加多页合并后的替换规则,格式同样是##regex##replaceTo
* 正文图片添加长按缩放
* 正文规则添加图片样式规则,可以设置为FULL
* 其它一些bug修复
**2020/07/21**
* 优化文字选择,不再缓存
* 添加忽略恢复列表,方便不同手机配置不同

@ -9,5 +9,6 @@ data class ContentRule(
var nextContentUrl: String? = null,
var webJs: String? = null,
var sourceRegex: String? = null,
var imageStyle: Int = 0 //0默认大小居中,1最大宽度
var replaceRegex: String? = null,
var imageStyle: String? = null //默认大小居中,1最大宽度
) : Parcelable

@ -137,5 +137,7 @@ object AppConfig {
val readBodyToLh: Boolean get() = App.INSTANCE.getPrefBoolean(PreferKey.readBodyToLh, true)
val isGooglePlay: Boolean get() = App.INSTANCE.channel == "google"
val isCoolApk: Boolean get() = App.INSTANCE.channel == "coolApk"
}

@ -3,6 +3,7 @@ package io.legado.app.help
import android.os.Handler
import android.os.Looper
import io.legado.app.App
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.RssSource
import io.legado.app.utils.EncoderUtils
import io.legado.app.utils.NetworkUtils
@ -33,9 +34,20 @@ object SourceHelp {
}
}
fun insertBookSource(vararg bookSources: BookSource) {
bookSources.forEach { bookSource ->
if (is18Plus(bookSource.bookSourceUrl)) {
handler.post {
App.INSTANCE.toast("${bookSource.bookSourceName}是18+网址,禁止导入.")
}
} else {
App.db.bookSourceDao().insert(bookSource)
}
}
}
private fun is18Plus(url: String?): Boolean {
url ?: return false
if (AppConfig.isGooglePlay) return false
val baseUrl = NetworkUtils.getBaseUrl(url)
baseUrl ?: return false
try {

@ -1,12 +1,14 @@
package io.legado.app.help.http
import android.annotation.SuppressLint
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.TextUtils
import android.webkit.*
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import io.legado.app.App
import io.legado.app.constant.AppConst
import org.apache.commons.text.StringEscapeUtils
@ -142,30 +144,6 @@ class AjaxWebView {
handler.postDelayed(runnable, 1000)
}
override fun onReceivedError(
view: WebView,
errorCode: Int,
description: String,
failingUrl: String
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
handler.obtainMessage(MSG_ERROR, Exception(description))
.sendToTarget()
}
}
override fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
handler.obtainMessage(
MSG_ERROR,
Exception(error.description.toString())
).sendToTarget()
}
}
}
private class EvalJsRunnable(
@ -213,31 +191,6 @@ class AjaxWebView {
}
}
override fun onReceivedError(
view: WebView,
errorCode: Int,
description: String,
failingUrl: String
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
handler.obtainMessage(MSG_ERROR, Exception(description))
.sendToTarget()
}
}
override fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
handler.obtainMessage(
MSG_ERROR,
Exception(error.description.toString())
).sendToTarget()
}
}
override fun onPageFinished(view: WebView, url: String) {
params.setCookie(url)
if (params.hasJavaScript()) {

@ -5,6 +5,7 @@ import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.view.View
import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
@ -17,6 +18,7 @@ import androidx.viewpager.widget.ViewPager
import com.google.android.material.bottomnavigation.BottomNavigationView
import io.legado.app.R
import io.legado.app.help.AppConfig
import io.legado.app.utils.dp
import io.legado.app.utils.getCompatColor
import kotlinx.android.synthetic.main.activity_main.view.*
import org.jetbrains.anko.backgroundColor
@ -138,6 +140,10 @@ object ATH {
}
fun setAlertDialogTint(dialog: AlertDialog): AlertDialog {
val background = GradientDrawable()
background.cornerRadius = 3F.dp
background.setColor(dialog.context.backgroundColor)
dialog.window?.setBackgroundDrawable(background)
val colorStateList = Selector.colorBuild()
.setDefaultColor(ThemeStore.accentColor(dialog.context))
.setPressedColor(ColorUtils.darkenColor(ThemeStore.accentColor(dialog.context)))

@ -95,13 +95,19 @@ object BookContent {
content.append(item.content).append("\n")
}
}
content.deleteCharAt(content.length - 1)
var contentStr = content.toString().htmlFormat()
val replaceRegex = bookSource.ruleContent?.replaceRegex
replaceRegex?.trim { it <= ' ' }?.split("##")?.let {
if (it.size > 1) {
contentStr = contentStr.replace(it[1].toRegex(), it.getOrNull(2) ?: "")
}
}
Debug.log(bookSource.bookSourceUrl, "┌获取章节名称")
Debug.log(bookSource.bookSourceUrl, "${bookChapter.title}")
Debug.log(bookSource.bookSourceUrl, "┌获取正文内容")
Debug.log(bookSource.bookSourceUrl, "\n$content")
return content.toString()
Debug.log(bookSource.bookSourceUrl, "\n$contentStr")
return contentStr
}
@Throws(Exception::class)
@ -126,7 +132,7 @@ object BookContent {
}
Debug.log(bookSource.bookSourceUrl, "" + nextUrlList.joinToString(""), printLog)
}
val content = analyzeRule.getString(contentRule.content).htmlFormat()
val content = analyzeRule.getString(contentRule.content)
return ContentData(content, nextUrlList)
}
}

@ -64,6 +64,7 @@ object ReadBook {
null
}
}
callBack?.upPageAnim()
}
fun upMsg(msg: String?) {
@ -313,7 +314,13 @@ object ReadBook {
when (chapter.index) {
durChapterIndex -> {
curTextChapter =
ChapterProvider.getTextChapter(book, chapter, contents, chapterSize)
ChapterProvider.getTextChapter(
book,
chapter,
contents,
chapterSize,
imageStyle
)
if (upContent) callBack?.upContent(resetPageOffset = resetPageOffset)
callBack?.upView()
curPageChanged()
@ -322,12 +329,24 @@ object ReadBook {
}
durChapterIndex - 1 -> {
prevTextChapter =
ChapterProvider.getTextChapter(book, chapter, contents, chapterSize)
ChapterProvider.getTextChapter(
book,
chapter,
contents,
chapterSize,
imageStyle
)
if (upContent) callBack?.upContent(-1, resetPageOffset)
}
durChapterIndex + 1 -> {
nextTextChapter =
ChapterProvider.getTextChapter(book, chapter, contents, chapterSize)
ChapterProvider.getTextChapter(
book,
chapter,
contents,
chapterSize,
imageStyle
)
if (upContent) callBack?.upContent(1, resetPageOffset)
}
}
@ -338,6 +357,8 @@ object ReadBook {
}
}
val imageStyle get() = webBook?.bookSource?.ruleContent?.imageStyle
fun saveRead() {
Coroutine.async {
book?.let { book ->
@ -358,5 +379,6 @@ object ReadBook {
fun upView()
fun pageChanged()
fun contentLoadFinish()
fun upPageAnim()
}
}

@ -20,6 +20,7 @@ import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.service.help.Download
import io.legado.app.service.help.ReadBook
import io.legado.app.utils.applyTint
@ -131,6 +132,7 @@ object Help {
LayoutInflater.from(context).inflate(R.layout.dialog_download_choice, null)
.apply {
view = this
setBackgroundColor(context.backgroundColor)
edit_start.setText(book.durChapterIndex.toString())
edit_end.setText(book.totalChapterNum.toString())
}
@ -157,7 +159,7 @@ object Help {
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view.apply {
hint = "备注内容"
setHint(R.string.note_content)
}
}
}

@ -550,6 +550,12 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
}
}
override fun upPageAnim() {
launch {
page_view?.upPageAnim()
}
}
/**
* 页面改变
*/

@ -16,6 +16,7 @@ import io.legado.app.ui.book.read.page.entities.TextLine
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.book.read.page.provider.ImageProvider
import io.legado.app.ui.widget.dialog.PhotoDialog
import io.legado.app.utils.activity
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getPrefBoolean
@ -213,74 +214,61 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
) {
if (!selectAble) return
if (!visibleRect.contains(x, y)) return
var relativeOffset = relativeOffset(0)
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
var relativeOffset: Float
for (relativePos in 0..2) {
relativeOffset = relativeOffset(relativePos)
if (relativePos > 0) {
//滚动翻页
if (!ReadBookConfig.isScroll) return
if (relativeOffset >= ChapterProvider.visibleHeight) return
}
val page = relativePage(relativePos)
for ((lineIndex, textLine) in page.textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true
invalidate()
selectStart[0] = 0
selectStart[1] = lineIndex
selectStart[2] = charIndex
selectEnd[0] = 0
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
initSelect(
page.chapterIndex,
relativePos,
textLine,
textChar,
lineIndex,
charIndex,
relativeOffset,
select
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(0, lineIndex, charIndex)
return
}
}
return
}
}
if (!ReadBookConfig.isScroll) return
//滚动翻页
relativeOffset = relativeOffset(1)
if (relativeOffset >= ChapterProvider.visibleHeight) return
val nextPage = relativePage(1)
for ((lineIndex, textLine) in nextPage.textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
textChar.selected = true
invalidate()
selectStart[0] = 1
selectStart[1] = lineIndex
selectStart[2] = charIndex
selectEnd[0] = 1
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(1, lineIndex, charIndex)
return
}
}
return
}
private fun initSelect(
chapterIndex: Int,
relativePage: Int,
textLine: TextLine,
textChar: TextChar,
lineIndex: Int,
charIndex: Int,
relativeOffset: Float,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit
) {
if (textChar.isImage) {
activity?.supportFragmentManager?.let {
PhotoDialog.show(it, chapterIndex, textChar.charData)
}
relativeOffset = relativeOffset(2)
if (relativeOffset >= ChapterProvider.visibleHeight) return
for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
for ((charIndex, textChar) in textLine.textChars.withIndex()) {
if (x > textChar.start && x < textChar.end) {
} else {
textChar.selected = true
invalidate()
selectStart[0] = 2
selectStart[0] = relativePage
selectStart[1] = lineIndex
selectStart[2] = charIndex
selectEnd[0] = 2
selectEnd[0] = relativePage
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(
@ -289,12 +277,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(2, lineIndex, charIndex)
return
}
}
return
}
select(relativePage, lineIndex, charIndex)
}
}

@ -17,6 +17,7 @@ import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.activity
import io.legado.app.utils.screenshot
import kotlinx.android.synthetic.main.activity_book_read.view.*
import java.util.*
class PageView(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs),
@ -109,7 +110,9 @@ class PageView(context: Context, attrs: AttributeSet) :
fun upPageAnim() {
pageDelegate?.onDestroy()
pageDelegate = null
pageDelegate = when (ReadBookConfig.pageAnim) {
pageDelegate = if (ReadBook.imageStyle?.toUpperCase(Locale.ROOT) == "FULL") {
ScrollPageDelegate(this)
} else when (ReadBookConfig.pageAnim) {
0 -> CoverPageDelegate(this)
1 -> SlidePageDelegate(this)
2 -> SimulationPageDelegate(this)

@ -1,5 +1,6 @@
package io.legado.app.ui.book.read.page.delegate
import android.view.MotionEvent
import io.legado.app.ui.book.read.page.PageView
class NoAnimPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
@ -11,4 +12,8 @@ class NoAnimPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView)
stopScroll()
}
override fun onDoubleTap(e: MotionEvent): Boolean {
return onSingleTapUp(e)
}
}

@ -10,12 +10,12 @@ import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import androidx.annotation.CallSuper
import com.google.android.material.snackbar.Snackbar
import io.legado.app.R
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.ContentView
import io.legado.app.ui.book.read.page.PageView
import kotlin.math.abs
import io.legado.app.R
abstract class PageDelegate(protected val pageView: PageView) :
GestureDetector.SimpleOnGestureListener() {
@ -24,6 +24,7 @@ abstract class PageDelegate(protected val pageView: PageView) :
pageView.width * 0.66f, pageView.height * 0.66f
)
protected val context: Context = pageView.context
private val animationSpeed = 300
//起始点
protected var startX: Float = 0f
@ -115,13 +116,12 @@ abstract class PageDelegate(protected val pageView: PageView) :
}
protected fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int) {
scroller.startScroll(
startX,
startY,
dx,
dy,
if (dx != 0) (abs(dx) * 0.3).toInt() else (abs(dy) * 0.3).toInt()
)
val duration = if (dx != 0) {
(animationSpeed * abs(dx)) / viewWidth
} else {
(animationSpeed * abs(dy)) / viewHeight
}
scroller.startScroll(startX, startY, dx, dy, duration)
isRunning = true
isStarted = true
pageView.invalidate()

@ -22,6 +22,7 @@ data class TextPage(
fun upLinesPosition() = ChapterProvider.apply {
if (!ReadBookConfig.textBottomJustify) return@apply
if (textLines.size <= 1) return@apply
if (textLines.last().isImage) return@apply
if (visibleHeight - height >= with(textLines.last()) { lineBottom - lineTop }) return@apply
val surplus = (visibleBottom - textLines.last().lineBottom)
if (surplus == 0f) return@apply

@ -17,6 +17,7 @@ import io.legado.app.ui.book.read.page.entities.TextChar
import io.legado.app.ui.book.read.page.entities.TextLine
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.utils.*
import java.util.*
import java.util.regex.Pattern
@ -51,7 +52,8 @@ object ChapterProvider {
book: Book,
bookChapter: BookChapter,
contents: List<String>,
chapterSize: Int
chapterSize: Int,
imageStyle: String?
): TextChapter {
val textPages = arrayListOf<TextPage>()
val pageLines = arrayListOf<Int>()
@ -69,7 +71,7 @@ object ChapterProvider {
src?.let {
durY =
setTypeImage(
book, bookChapter, src, durY, textPages
book, bookChapter, src, durY, textPages, imageStyle
)
}
} else {
@ -116,26 +118,39 @@ object ChapterProvider {
chapter: BookChapter,
src: String,
y: Float,
textPages: ArrayList<TextPage>
textPages: ArrayList<TextPage>,
imageStyle: String?
): Float {
var durY = y
ImageProvider.getImage(book, chapter.index, src)?.let {
if (durY > visibleHeight) {
textPages.last().height = durY
textPages.add(TextPage())
durY = 0f
}
var height = it.height
var width = it.width
when (imageStyle?.toUpperCase(Locale.ROOT)) {
"FULL" -> {
width = visibleWidth
height = it.height * visibleWidth / it.width
}
else -> {
if (it.width > visibleWidth) {
height = it.height * visibleWidth / it.width
width =
visibleWidth
width = visibleWidth
}
if (height > visibleHeight) {
width = width * visibleHeight / height
height =
visibleHeight
height = visibleHeight
}
if (durY + height > visibleHeight) {
textPages.last().height = durY
textPages.add(TextPage())
durY = 0f
}
}
}
val textLine = TextLine(isImage = true)
textLine.lineTop = durY
durY += height

@ -10,10 +10,11 @@ import io.legado.app.utils.FileUtils
import io.legado.app.utils.MD5Utils
import io.legado.app.utils.externalFilesDir
import java.io.FileOutputStream
import java.util.concurrent.ConcurrentHashMap
object ImageProvider {
private val cache = hashMapOf<Int, HashMap<String, Bitmap>>()
private val cache = ConcurrentHashMap<Int, ConcurrentHashMap<String, Bitmap>>()
@Synchronized
fun getCache(chapterIndex: Int, src: String): Bitmap? {
@ -24,7 +25,7 @@ object ImageProvider {
fun setCache(chapterIndex: Int, src: String, bitmap: Bitmap) {
var indexCache = cache[chapterIndex]
if (indexCache == null) {
indexCache = hashMapOf()
indexCache = ConcurrentHashMap()
cache[chapterIndex] = indexCache
}
indexCache[src] = bitmap

@ -233,6 +233,8 @@ class BookSourceEditActivity :
add(EditEntity("nextContentUrl", cr?.nextContentUrl, R.string.rule_next_content))
add(EditEntity("webJs", cr?.webJs, R.string.rule_web_js))
add(EditEntity("sourceRegex", cr?.sourceRegex, R.string.rule_source_regex))
add(EditEntity("replaceRegex", cr?.replaceRegex, R.string.rule_replace_regex))
add(EditEntity("imageStyle", cr?.imageStyle, R.string.rule_image_style))
}
//发现
val er = source?.getExploreRule()
@ -332,6 +334,8 @@ class BookSourceEditActivity :
"nextContentUrl" -> contentRule.nextContentUrl = it.value
"webJs" -> contentRule.webJs = it.value
"sourceRegex" -> contentRule.sourceRegex = it.value
"replaceRegex" -> contentRule.replaceRegex = it.value
"imageStyle" -> contentRule.imageStyle = it.value
}
}
source.ruleSearch = searchRule

@ -8,6 +8,7 @@ import com.jayway.jsonpath.JsonPath
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.BookSource
import io.legado.app.help.SourceHelp
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.OldRule
import io.legado.app.help.storage.Restore.jsonPath
@ -205,7 +206,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
}
} else {
OldRule.jsonToBookSource(text1)?.let {
App.db.bookSourceDao().insert(it)
SourceHelp.insertBookSource(it)
count = 1
}
}
@ -220,7 +221,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
bookSources.add(it)
}
}
App.db.bookSourceDao().insert(*bookSources.toTypedArray())
SourceHelp.insertBookSource(*bookSources.toTypedArray())
"导入${bookSources.size}"
}
text1.isAbsUrl() -> {
@ -251,7 +252,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
bookSources.add(source)
}
}
App.db.bookSourceDao().insert(*bookSources.toTypedArray())
SourceHelp.insertBookSource(*bookSources.toTypedArray())
return bookSources.size
}
}

@ -89,7 +89,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view.apply {
hint = "分组名称"
setHint(R.string.group_name)
}
}
}
@ -111,7 +111,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view.apply {
hint = "分组名称"
setHint(R.string.group_name)
setText(group)
}
}

@ -80,9 +80,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
}
PreferKey.cleanCache -> {
BookHelp.clearCache()
requireActivity().cacheDir.listFiles()?.forEach {
it.delete()
}
FileUtils.deleteFile(requireActivity().cacheDir.absolutePath)
toast(R.string.clear_cache_success)
}
PreferKey.defaultCover -> {

@ -81,11 +81,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
fun postLoad() {
execute {
FileUtils.getDirFile(context.cacheDir, "Fonts").let {
if (it.exists()) {
it.delete()
}
}
FileUtils.deleteFile(FileUtils.getPath(context.cacheDir, "Fonts"))
}
}
}

@ -0,0 +1,68 @@
package io.legado.app.ui.widget.dialog
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.provider.ImageProvider
import kotlinx.android.synthetic.main.dialog_photo_view.*
class PhotoDialog : BaseDialogFragment() {
companion object {
fun show(
fragmentManager: FragmentManager,
chapterIndex: Int,
src: String
) {
PhotoDialog().apply {
val bundle = Bundle()
bundle.putInt("chapterIndex", chapterIndex)
bundle.putString("src", src)
arguments = bundle
}.show(fragmentManager, "photoDialog")
}
}
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_photo_view, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
arguments?.let {
val chapterIndex = it.getInt("chapterIndex")
val src = it.getString("src")
ReadBook.book?.let { book ->
src?.let {
ImageProvider.getImage(book, chapterIndex, src)?.let { bitmap ->
photo_view.setImageBitmap(bitmap)
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,48 @@
package io.legado.app.ui.widget.image.photo
import android.graphics.PointF
import android.graphics.RectF
import android.widget.ImageView
class Info(
rect: RectF,
img: RectF,
widget: RectF,
base: RectF,
screenCenter: PointF,
scale: Float,
degrees: Float,
scaleType: ImageView.ScaleType?
) {
// 内部图片在整个手机界面的位置
var mRect = RectF()
// 控件在窗口的位置
var mImgRect = RectF()
var mWidgetRect = RectF()
var mBaseRect = RectF()
var mScreenCenter = PointF()
var mScale = 0f
var mDegrees = 0f
var mScaleType: ImageView.ScaleType? = null
init {
mRect.set(rect)
mImgRect.set(img)
mWidgetRect.set(widget)
mScale = scale
mScaleType = scaleType
mDegrees = degrees
mBaseRect.set(base)
mScreenCenter.set(screenCenter)
}
}

@ -0,0 +1,56 @@
package io.legado.app.ui.widget.image.photo
import android.view.MotionEvent
import kotlin.math.abs
import kotlin.math.atan
class RotateGestureDetector(l: OnRotateListener) {
private val MAX_DEGREES_STEP = 120
private val mListener: OnRotateListener? = null
private var mPrevSlope = 0f
private var mCurrSlope = 0f
private val x1 = 0f
private val y1 = 0f
private val x2 = 0f
private val y2 = 0f
fun onTouchEvent(event: MotionEvent) {
when (event.actionMasked) {
MotionEvent.ACTION_POINTER_DOWN,
MotionEvent.ACTION_POINTER_UP -> {
if (event.pointerCount == 2) mPrevSlope = caculateSlope(event)
}
MotionEvent.ACTION_MOVE -> if (event.pointerCount > 1) {
mCurrSlope = caculateSlope(event)
val currDegrees = Math.toDegrees(atan(mCurrSlope.toDouble()));
val prevDegrees = Math.toDegrees(atan(mPrevSlope.toDouble()));
val deltaSlope = currDegrees - prevDegrees;
if (abs(deltaSlope) <= MAX_DEGREES_STEP) {
mListener?.onRotate(deltaSlope.toFloat(), (x2 + x1) / 2, (y2 + y1) / 2);
}
mPrevSlope = mCurrSlope;
}
}
}
private fun caculateSlope(event: MotionEvent): Float {
val x1 = event.getX(0);
val y1 = event.getY(0);
val x2 = event.getX(1);
val y2 = event.getY(1);
return (y2 - y1) / (x2 - x1);
}
}
interface OnRotateListener {
fun onRotate(degrees: Float, focusX: Float, focusY: Float)
}

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background">
<io.legado.app.ui.widget.image.PhotoView
android:id="@+id/photo_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -719,9 +719,13 @@
<string name="alipay_payment_qr_code">支付寶收款二維碼</string>
<string name="qq_collection_qr_code">QQ收款二維碼</string>
<string name="contributors_summary">gedoor,Invinciblelee等,详情请在github中查看</string>
<string name="clear_cache_summary">清除已下载书籍和字体缓存</string>
<string name="default_cover">默认封面</string>
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
<string name="clear_cache_summary">清除已下載書籍和字體緩存</string>
<string name="default_cover">默認封面</string>
<string name="restore_ignore">恢復忽略列表</string>
<string name="restore_ignore_summary">恢復時忽略一些內容不恢復,方便不同手機配置不同</string>
<string name="read_config">閱讀界面設置</string>
<string name="rule_image_style">图片样式(imageStyle)</string>
<string name="rule_replace_regex">替换规则(replaceRegex)</string>
<string name="group_name">分組名稱</string>
<string name="note_content">備註內容</string>
</resources>

@ -719,10 +719,14 @@
<string name="alipay_payment_qr_code">支付寶收款二維碼</string>
<string name="qq_collection_qr_code">QQ收款二維碼</string>
<string name="contributors_summary">gedoor,Invinciblelee等,详情请在github中查看</string>
<string name="clear_cache_summary">清除已下载书籍和字体缓存</string>
<string name="default_cover">默认封面</string>
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
<string name="clear_cache_summary">清除已下載書籍和字體緩存</string>
<string name="default_cover">默認封面</string>
<string name="restore_ignore">恢復忽略列表</string>
<string name="restore_ignore_summary">恢復時忽略一些內容不恢復,方便不同手機配置不同</string>
<string name="read_config">閱讀界面設置</string>
<string name="rule_image_style">图片样式(imageStyle)</string>
<string name="rule_replace_regex">替换规则(replaceRegex)</string>
<string name="group_name">分組名稱</string>
<string name="note_content">備註內容</string>
</resources>

@ -724,4 +724,8 @@
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
<string name="rule_image_style">图片样式(imageStyle)</string>
<string name="rule_replace_regex">替换规则(replaceRegex)</string>
<string name="group_name">分组名称</string>
<string name="note_content">备注内容</string>
</resources>

@ -40,7 +40,7 @@
<string name="in_favorites">in Favorites</string>
<string name="out_favorites">Not in Favorites</string>
<string name="rss">Subscription</string>
<string name="all">ALL</string>
<string name="all">All</string>
<string name="recent_reading">Recent reading</string>
<string name="last_read">Last reading</string>
<string name="update_log">What\'s new</string>
@ -55,8 +55,8 @@
<string name="bookshelf_layout">Layout</string>
<string name="view">View</string>
<string name="book_library">Library</string>
<string name="book_local">Import Books</string>
<string name="book_source">Book Sources</string>
<string name="book_local">Import books</string>
<string name="book_source">Book sources</string>
<string name="book_source_manage">Sources management</string>
<string name="book_source_manage_desc">Create/Import/Edit/Manage Book sources</string>
<string name="setting">Settings</string>
@ -76,17 +76,17 @@
<string name="read_dur_progress">Origin: %s</string>
<string name="book_name">Title</string>
<string name="lasted_show">Latest: %s</string>
<string name="check_add_bookshelf">Would you like to put %s on the bookshelf?</string>
<string name="import_books_count">%s text files in total</string>
<string name="check_add_bookshelf">Would you like to add %s to your Bookshelf?</string>
<string name="import_books_count">%s text file(s) in total</string>
<string name="is_loading">Loading…</string>
<string name="retry">Retry</string>
<string name="web_service">Web service</string>
<string name="web_service_desc">Enable web service</string>
<string name="web_edit_source">web editing book sources</string>
<string name="web_edit_source">Edit book sources on the web</string>
<string name="http_ip">http://%1$s:%2$d</string>
<string name="download_offline">Offline download</string>
<string name="download_offline_t">Offline download</string>
<string name="download_offline_s">Download the selected chapter to Storage</string>
<string name="download_offline_s">Download the selected chapter(s) to Storage</string>
<string name="change_origin">Change Origin</string>
<string name="about_description">
\u3000\u3000 This is an open source reading software newly developed by Kotlin, welcome to join us. Follow the WeChat Official Account [开源阅读]!
@ -101,7 +101,7 @@
<string name="ps_auto_download">Download the latest chapters automatically when updating books</string>
<string name="backup_restore">Backup and restore</string>
<string name="web_dav_set">WebDav settings</string>
<string name="web_dav_set_import_old">WebDav settings/import legacy data</string>
<string name="web_dav_set_import_old">WebDav settings/Import legacy data</string>
<string name="backup">Backup</string>
<string name="restore">Restore</string>
<string name="backup_permission">Backup needs storage permission</string>
@ -145,7 +145,7 @@
<string name="pause">Pause</string>
<string name="resume">Resume</string>
<string name="set_timer">Timer</string>
<string name="read_aloud_pause">Speaking Pause</string>
<string name="read_aloud_pause">Speak Paused</string>
<string name="read_aloud_timer">Speaking(%d min left)</string>
<string name="ps_hide_navigation_bar">Hide virtual buttons when reading</string>
<string name="pt_hide_navigation_bar">Hide navigation bar</string>
@ -161,24 +161,24 @@
<string name="background">Background</string>
<string name="author">Author</string>
<string name="author_show">Author: %s</string>
<string name="aloud_stop">Pause</string>
<string name="aloud_stop">Speak Stopped</string>
<string name="clear_cache">Clear cache</string>
<string name="clear_cache_success">Cache cleared</string>
<string name="action_save">Save</string>
<string name="edit_source">Edit source</string>
<string name="edit_book_source">Edit Book Sources</string>
<string name="disable_book_source">Disable Book Source</string>
<string name="add_book_source">Add Book Source</string>
<string name="edit_book_source">Edit Book source</string>
<string name="disable_book_source">Disable Book source</string>
<string name="add_book_source">Add Book source</string>
<string name="add_rss_source">Add subscription source</string>
<string name="book_file_selector">Add books</string>
<string name="scan_book_source">Scan</string>
<string name="copy_source">Copy source</string>
<string name="paste_source">Paste source</string>
<string name="source_rule_s">Source rule description</string>
<string name="source_rule_s">Source rules description</string>
<string name="check_update">Check for Updates</string>
<string name="camera_scan">Scan QR code</string>
<string name="scan_image">Scan local images</string>
<string name="rule_summary">Rule description</string>
<string name="rule_summary">Rules description</string>
<string name="share">Share</string>
<string name="share_app">Share to</string>
<string name="flow_sys">Follow system</string>
@ -187,7 +187,7 @@
<string name="import_book_source_local">Import local sources</string>
<string name="import_book_source_on_line">Import online sources</string>
<string name="replace_rule_title">Replacement</string>
<string name="replace_rule_edit">Edit replacement rules</string>
<string name="replace_rule_edit">Edit replacement rule</string>
<string name="replace_rule">Pattern</string>
<string name="replace_to">Replacement</string>
<string name="img_cover">Cover</string>
@ -204,7 +204,7 @@
<string name="clear_all_content">Clearing the cache will delete all saved chapters. Are you sure to delete it?</string>
<string name="book_source_share_url">Book sources sharing</string>
<string name="replace_rule_summary">Rule name</string>
<string name="replace_rule_invalid">Pattern rule is empty or does not meet regular expression requirements</string>
<string name="replace_rule_invalid">Pattern rule is empty or does not conform the regex specification.</string>
<string name="select_action">Selection action</string>
<string name="select_all">Select all</string>
<string name="select_all_count">Select all(%1$d/%2$d)</string>
@ -240,7 +240,7 @@
<string name="clear_font">Default font</string>
<string name="find_on_www">Discovery</string>
<string name="find_source_manage">Discovery</string>
<string name="find_empty">No content.Go to Sources Management to add it!</string>
<string name="find_empty">No content.Go to Sources management to add it!</string>
<string name="del_all">Delete all</string>
<string name="searchHistory">Search history</string>
<string name="clear">Clear</string>
@ -268,8 +268,8 @@
<string name="load_over_time">Loading timeout</string>
<string name="join_group">Follow:%s</string>
<string name="copy_complete">Copied successfully</string>
<string name="arrange_bookshelf">Bookshelf Arrangement</string>
<string name="clear_bookshelf_s">it will delete all books. Be careful,please.</string>
<string name="arrange_bookshelf">Bookshelf arrangement</string>
<string name="clear_bookshelf_s">It will delete all books. Be careful,please.</string>
<string name="search_book_source">Search book sources</string>
<string name="search_rss_source">Search subscription sources</string>
<string name="search_book_source_num">Search( %d sources in total)</string>
@ -317,11 +317,11 @@
<string name="auto_next_page">Auto scroll</string>
<string name="auto_next_page_stop">Stop Auto scroll</string>
<string name="auto_next_page_speed">Auto scroll speed</string>
<string name="book_info">Book info</string>
<string name="book_info_edit">Edit book info</string>
<string name="book_info">Book infomation</string>
<string name="book_info_edit">Edit book infomation</string>
<string name="ps_default_read">Use Bookshelf as start page</string>
<string name="pt_default_read">Auto jump to Recent list</string>
<string name="use_to">Replacement object, book name or source name is available</string>
<string name="use_to">Replacement object. Book name or source name is available</string>
<string name="menu_action_group">Groups</string>
<string name="download_path">Cache path</string>
<string name="cleanCache">Clear cache</string>
@ -330,11 +330,11 @@
<string name="download_update">Download updates</string>
<string name="volume_key_page_on_play">Volume keys to turn page when reading</string>
<string name="tip_margin_change">Margin adjustment</string>
<string name="allow_update">Allow update</string>
<string name="allow_update">Enable update</string>
<string name="disable_update">Disable update</string>
<string name="revert_selection">Inverse</string>
<string name="search_book_key">Search bookName/author</string>
<string name="debug_hint">BookName,Author,URL</string>
<string name="search_book_key">Search book name/author</string>
<string name="debug_hint">Book name,Author,URL</string>
<string name="faq">FAQ</string>
<string name="pt_show_all_find">Display all Discovery</string>
<string name="ps_show_all_find">Display the selected origin\'s Discovery if closed</string>
@ -410,6 +410,8 @@
<string name="rule_next_content">正文下一页URL规则(nextContentUrl)</string>
<string name="rule_web_js">webJs</string>
<string name="rule_source_regex">资源正则(sourceRegex)</string>
<string name="rule_replace_regex">替换规则(replaceRegex)</string>
<string name="rule_image_style">图片样式(imageStyle)</string>
<string name="source_icon">图标(sourceIcon)</string>
<string name="r_articles">列表规则(ruleArticles)</string>
@ -554,7 +556,7 @@
<string name="group_select">Group selection</string>
<string name="group_edit">Group editing</string>
<string name="move_to_group">Move to group</string>
<string name="add_group">Add to group</string>
<string name="add_group">Add to Groups</string>
<string name="add_replace_rule">New replacement</string>
<string name="group">Group</string>
<string name="group_s">Group: %s</string>
@ -574,7 +576,7 @@
<string name="web_dav_account">WebDav account</string>
<string name="web_dav_account_s">Input your WebDav account</string>
<string name="rss_source">Subscription source</string>
<string name="rss_source_edit">Subscription source editing</string>
<string name="rss_source_edit">Edit Subscription source</string>
<string name="screen">Filter</string>
<string name="screen_find">Search Discovery sources</string>
<string name="dur_pos">Current location:</string>
@ -722,8 +724,10 @@
<string name="contributors_summary">gedoor,Invinciblelee etc. Checking in github for details</string>
<string name="clear_cache_summary">Clear the cache of the downloaded books and fonts</string>
<string name="default_cover">Default cover</string>
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
<string name="restore_ignore">Bypass list</string>
<string name="restore_ignore_summary">Ignore some contents while restoring</string>
<string name="read_config">Reading interface settings</string>
<string name="group_name">Group name</string>
<string name="note_content">Remarks section</string>
</resources>
Loading…
Cancel
Save