Merge pull request #21 from gedoor/master

up
pull/379/head
口口吕 5 years ago committed by GitHub
commit 77fde70d94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      app/src/main/assets/updateLog.md
  2. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  3. 20
      app/src/main/java/io/legado/app/data/dao/BookDao.kt
  4. 9
      app/src/main/java/io/legado/app/data/entities/BookProgress.kt
  5. 8
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  6. 50
      app/src/main/java/io/legado/app/help/storage/SyncBookProgress.kt
  7. 39
      app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt
  8. 1
      app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
  9. 17
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  10. 8
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  11. 4
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  12. 120
      app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
  13. 20
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt
  14. 39
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
  15. 91
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt
  16. 25
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt
  17. 4
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
  18. 2
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  19. 2
      app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
  20. 33
      app/src/main/java/io/legado/app/utils/BitmapUtils.kt
  21. 2
      app/src/main/res/values/strings.xml
  22. 7
      app/src/main/res/xml/pref_config_backup.xml

@ -2,6 +2,15 @@
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
**2020/03/04**
* 修复仿真翻页动画
* 添加阅读记录同步,正常退出进入软件时同步阅读记录
**2020/03/03**
* 修复bug
* 优化排版,确保段距为0时每行在相同的位置
* 修复底部遮挡
**2020/03/02** **2020/03/02**
* 添加书源登录 * 添加书源登录
* 替换规则实时生效 * 替换规则实时生效

@ -32,6 +32,7 @@ object PreferKey {
const val webDavUrl = "web_dav_url" const val webDavUrl = "web_dav_url"
const val webDavAccount = "web_dav_account" const val webDavAccount = "web_dav_account"
const val webDavPassword = "web_dav_password" const val webDavPassword = "web_dav_password"
const val webDavCreateDir = "webDavCreateDir"
const val changeSourceLoadToc = "changeSourceLoadToc" const val changeSourceLoadToc = "changeSourceLoadToc"
const val chineseConverterType = "chineseConverterType" const val chineseConverterType = "chineseConverterType"
const val launcherIcon = "launcherIcon" const val launcherIcon = "launcherIcon"

@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
import androidx.room.* import androidx.room.*
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookProgress
@Dao @Dao
interface BookDao { interface BookDao {
@ -67,4 +68,23 @@ interface BookDao {
@Query("update books set `group` = :newGroupId where `group` = :oldGroupId") @Query("update books set `group` = :newGroupId where `group` = :oldGroupId")
fun upGroup(oldGroupId: Int, newGroupId: Int) fun upGroup(oldGroupId: Int, newGroupId: Int)
@get:Query("select bookUrl, durChapterIndex, durChapterPos, durChapterTime, durChapterTitle from books")
val allBookProgress: List<BookProgress>
@Query(
"""
update books set
durChapterIndex = :durChapterIndex, durChapterPos = :durChapterPos,
durChapterTime = :durChapterTime, durChapterTitle = :durChapterTitle
where bookUrl = :bookUrl and durChapterTime < :durChapterTime
"""
)
fun upBookProgress(
bookUrl: String,
durChapterIndex: Int,
durChapterPos: Int,
durChapterTime: Long,
durChapterTitle: String?
)
} }

@ -0,0 +1,9 @@
package io.legado.app.data.entities
data class BookProgress(
val bookUrl: String,
val durChapterIndex: Int,
val durChapterPos: Int,
val durChapterTime: Long,
val durChapterTitle: String?
)

@ -27,6 +27,7 @@ object ReadBookConfig {
val durConfig get() = getConfig(styleSelect) val durConfig get() = getConfig(styleSelect)
private val shareConfig get() = getConfig(5) private val shareConfig get() = getConfig(5)
var bg: Drawable? = null var bg: Drawable? = null
var bgMeanColor: Int = 0
init { init {
upConfig() upConfig()
@ -68,6 +69,13 @@ object ReadBookConfig {
val width = dm.widthPixels val width = dm.widthPixels
val height = dm.heightPixels val height = dm.heightPixels
bg = durConfig.bgDrawable(width, height) bg = durConfig.bgDrawable(width, height)
bg?.let {
if (it is BitmapDrawable) {
bgMeanColor = BitmapUtils.getMeanColor(it.bitmap)
} else if (it is ColorDrawable) {
bgMeanColor = it.color
}
}
} }
fun save() { fun save() {

@ -0,0 +1,50 @@
package io.legado.app.help.storage
import io.legado.app.App
import io.legado.app.data.entities.BookProgress
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.webdav.WebDav
import io.legado.app.utils.FileUtils
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
@Suppress("BlockingMethodInNonBlockingContext")
object SyncBookProgress {
const val fileName = "bookProgress.json"
private val file = FileUtils.createFileIfNotExist(App.INSTANCE.cacheDir, fileName)
private val webDavUrl = "${WebDavHelp.rootWebDavUrl}$fileName"
fun uploadBookProgress() {
Coroutine.async {
val value = App.db.bookDao().allBookProgress
if (value.isNotEmpty()) {
val json = GSON.toJson(value)
file.writeText(json)
if (WebDavHelp.initWebDav()) {
WebDav(webDavUrl).upload(file.absolutePath)
}
}
}
}
fun downloadBookProgress() {
Coroutine.async {
if (WebDavHelp.initWebDav()) {
WebDav(webDavUrl).downloadTo(file.absolutePath, true)
if (file.exists()) {
val json = file.readText()
GSON.fromJsonArray<BookProgress>(json)?.forEach {
App.db.bookDao().upBookProgress(
it.bookUrl,
it.durChapterIndex,
it.durChapterPos,
it.durChapterTime,
it.durChapterTitle
)
}
}
}
}
}
}

@ -10,6 +10,7 @@ import io.legado.app.lib.webdav.WebDav
import io.legado.app.lib.webdav.http.HttpAuth import io.legado.app.lib.webdav.http.HttpAuth
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.ZipUtils import io.legado.app.utils.ZipUtils
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefString import io.legado.app.utils.getPrefString
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
@ -25,31 +26,36 @@ object WebDavHelp {
private const val defaultWebDavUrl = "https://dav.jianguoyun.com/dav/" private const val defaultWebDavUrl = "https://dav.jianguoyun.com/dav/"
private val zipFilePath = "${FileUtils.getCachePath()}${File.separator}backup.zip" private val zipFilePath = "${FileUtils.getCachePath()}${File.separator}backup.zip"
private fun getWebDavUrl(): String { val rootWebDavUrl: String
get() {
var url = App.INSTANCE.getPrefString(PreferKey.webDavUrl) var url = App.INSTANCE.getPrefString(PreferKey.webDavUrl)
if (url.isNullOrEmpty()) { if (url.isNullOrEmpty()) {
url = defaultWebDavUrl url = defaultWebDavUrl
} }
if (!url.endsWith("/")) url += "/" if (!url.endsWith("/")) url = "${url}/"
if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCreateDir, true)) {
url = "${url}legado/"
}
return url return url
} }
private fun initWebDav(): Boolean { fun initWebDav(): Boolean {
val account = App.INSTANCE.getPrefString(PreferKey.webDavAccount) val account = App.INSTANCE.getPrefString(PreferKey.webDavAccount)
val password = App.INSTANCE.getPrefString(PreferKey.webDavPassword) val password = App.INSTANCE.getPrefString(PreferKey.webDavPassword)
if (!account.isNullOrBlank() && !password.isNullOrBlank()) { if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
HttpAuth.auth = HttpAuth.Auth(account, password) HttpAuth.auth = HttpAuth.Auth(account, password)
WebDav(rootWebDavUrl).makeAsDir()
return true return true
} }
return false return false
} }
private fun getWebDavFileNames(): ArrayList<String> { private fun getWebDavFileNames(): ArrayList<String> {
val url = getWebDavUrl() val url = rootWebDavUrl
val names = arrayListOf<String>() val names = arrayListOf<String>()
if (initWebDav()) { if (initWebDav()) {
try { try {
var files = WebDav(url + "legado/").listFiles() var files = WebDav(url).listFiles()
files = files.reversed() files = files.reversed()
for (index: Int in 0 until min(10, files.size)) { for (index: Int in 0 until min(10, files.size)) {
files[index].displayName?.let { files[index].displayName?.let {
@ -81,12 +87,16 @@ object WebDavHelp {
private fun restoreWebDav(name: String, success: () -> Unit) { private fun restoreWebDav(name: String, success: () -> Unit) {
Coroutine.async { Coroutine.async {
getWebDavUrl().let { rootWebDavUrl.let {
val file = WebDav(it + "legado/" + name) if (name == SyncBookProgress.fileName) {
file.downloadTo(zipFilePath, true) SyncBookProgress.downloadBookProgress()
@Suppress("BlockingMethodInNonBlockingContext") } else {
ZipUtils.unzipFile(zipFilePath, Backup.backupPath) val webDav = WebDav(it + name)
Restore.restore(Backup.backupPath) webDav.downloadTo(zipFilePath, true)
@Suppress("BlockingMethodInNonBlockingContext")
ZipUtils.unzipFile(zipFilePath, Backup.backupPath)
Restore.restore(Backup.backupPath)
}
} }
}.onSuccess { }.onSuccess {
success.invoke() success.invoke()
@ -102,10 +112,9 @@ object WebDavHelp {
} }
FileUtils.deleteFile(zipFilePath) FileUtils.deleteFile(zipFilePath)
if (ZipUtils.zipFiles(paths, zipFilePath)) { if (ZipUtils.zipFiles(paths, zipFilePath)) {
WebDav(getWebDavUrl() + "legado").makeAsDir() val backupDate = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val putUrl = getWebDavUrl() + "legado/backup" + .format(Date(System.currentTimeMillis()))
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val putUrl = "${rootWebDavUrl}backup${backupDate}.zip"
.format(Date(System.currentTimeMillis())) + ".zip"
WebDav(putUrl).upload(zipFilePath) WebDav(putUrl).upload(zipFilePath)
} }
} }

@ -202,7 +202,6 @@ constructor(urlStr: String) {
* 上传文件 * 上传文件
*/ */
@Throws(IOException::class) @Throws(IOException::class)
@JvmOverloads
fun upload(localPath: String, contentType: String? = null): Boolean { fun upload(localPath: String, contentType: String? = null): Boolean {
val file = File(localPath) val file = File(localPath)
if (!file.exists()) return false if (!file.exists()) return false

@ -30,15 +30,16 @@ object LocalBook {
} }
fun deleteBook(book: Book, deleteOriginal: Boolean) { fun deleteBook(book: Book, deleteOriginal: Boolean) {
if (book.isTxt()) { kotlin.runCatching {
val bookFile = if (book.isTxt()) {
FileUtils.getFile(AnalyzeTxtFile.cacheFolder, book.originName, subDirs = *arrayOf()) val bookFile = FileUtils.getFile(AnalyzeTxtFile.cacheFolder, book.originName)
bookFile.delete() bookFile.delete()
} }
if (deleteOriginal) { if (deleteOriginal) {
val uri = Uri.parse(book.bookUrl) val uri = Uri.parse(book.bookUrl)
DocumentFile.fromSingleUri(App.INSTANCE, uri)?.delete() DocumentFile.fromSingleUri(App.INSTANCE, uri)?.delete()
}
} }
} }
} }

@ -21,8 +21,6 @@ import io.legado.app.help.BlurTransformation
import io.legado.app.help.ImageLoader import io.legado.app.help.ImageLoader
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.ui.audio.AudioPlayActivity import io.legado.app.ui.audio.AudioPlayActivity
import io.legado.app.ui.book.changecover.ChangeCoverDialog import io.legado.app.ui.book.changecover.ChangeCoverDialog
import io.legado.app.ui.book.changesource.ChangeSourceDialog import io.legado.app.ui.book.changesource.ChangeSourceDialog
@ -232,17 +230,17 @@ class BookInfoActivity :
titleResource = R.string.sure, titleResource = R.string.sure,
messageResource = R.string.sure_delete_book_file messageResource = R.string.sure_delete_book_file
) { ) {
okButton { positiveButton(R.string.yes) {
viewModel.delBook(true) { viewModel.delBook(true) {
finish() finish()
} }
} }
noButton { negativeButton(R.string.no) {
viewModel.delBook(false) { viewModel.delBook(false) {
finish() finish()
} }
} }
} }.show()
} else { } else {
viewModel.delBook { viewModel.delBook {
upTvBookshelf() upTvBookshelf()

@ -202,12 +202,12 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
fun delBook(deleteOriginal: Boolean = false, success: (() -> Unit)? = null) { fun delBook(deleteOriginal: Boolean = false, success: (() -> Unit)? = null) {
execute { execute {
bookData.value?.let { bookData.value?.let {
App.db.bookDao().delete(it)
inBookshelf = false
if (it.isLocalBook()) { if (it.isLocalBook()) {
LocalBook.deleteBook(it, deleteOriginal) LocalBook.deleteBook(it, deleteOriginal)
} }
App.db.bookDao().delete(it)
} }
inBookshelf = false
}.onSuccess { }.onSuccess {
success?.invoke() success?.invoke()
} }

@ -28,7 +28,7 @@ object ChapterProvider {
var visibleHeight = 0 var visibleHeight = 0
var visibleRight = 0 var visibleRight = 0
var visibleBottom = 0 var visibleBottom = 0
private var lineSpacingExtra = 0f private var lineSpacingExtra = 0
private var paragraphSpacing = 0 private var paragraphSpacing = 0
var typeface: Typeface = Typeface.SANS_SERIF var typeface: Typeface = Typeface.SANS_SERIF
var titlePaint = TextPaint() var titlePaint = TextPaint()
@ -51,7 +51,7 @@ object ChapterProvider {
val pageLengths = arrayListOf<Int>() val pageLengths = arrayListOf<Int>()
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
var surplusText = content var surplusText = content
var durY = 0 var durY = 0f
textPages.add(TextPage()) textPages.add(TextPage())
while (surplusText.isNotEmpty()) { while (surplusText.isNotEmpty()) {
if (textPages.first().textLines.isEmpty()) { if (textPages.first().textLines.isEmpty()) {
@ -60,7 +60,9 @@ object ChapterProvider {
if (end > 0) { if (end > 0) {
val title = surplusText.substring(0, end) val title = surplusText.substring(0, end)
surplusText = surplusText.substring(end + 1) surplusText = surplusText.substring(end + 1)
durY = joinTitle(title, durY, textPages, pageLines, pageLengths, stringBuilder) durY = setTypeText(
title, durY, textPages, pageLines, pageLengths, stringBuilder, true
)
} }
} else { } else {
//正文 //正文
@ -73,7 +75,8 @@ object ChapterProvider {
text = surplusText text = surplusText
surplusText = "" surplusText = ""
} }
durY = joinBody(text, durY, textPages, pageLines, pageLengths, stringBuilder) durY =
setTypeText(text, durY, textPages, pageLines, pageLengths, stringBuilder, false)
} }
} }
textPages.last().height = durY + 20.dp textPages.last().height = durY + 20.dp
@ -103,116 +106,59 @@ object ChapterProvider {
} }
/** /**
* 标题 * 排版文字
*/ */
private fun joinTitle( private fun setTypeText(
title: String,
y: Int,
textPages: ArrayList<TextPage>,
pageLines: ArrayList<Int>,
pageLengths: ArrayList<Int>,
stringBuilder: StringBuilder
): Int {
var durY = y
val layout = StaticLayout(
title, titlePaint, visibleWidth,
Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra, true
)
for (lineIndex in 0 until layout.lineCount) {
textPages.last().height = durY
val textLine = TextLine(isTitle = true)
if (durY + layout.getLineBaseline(lineIndex) - layout.getLineTop(lineIndex)
+ contentPaint.fontMetrics.descent < visibleHeight
) {
textPages.last().textLines.add(textLine)
durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
} else {
textPages.last().text = stringBuilder.toString()
stringBuilder.clear()
pageLines.add(textPages.last().textLines.size)
pageLengths.add(textPages.last().text.length)
//新页面
durY = layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
textPages.add(TextPage())
textPages.last().textLines.add(textLine)
}
textLine.lineTop = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat()
textLine.lineBase = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
textLine.lineBottom = textLine.lineBase + contentPaint.fontMetrics.descent
val words =
title.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
stringBuilder.append(words)
textLine.text = words
val desiredWidth = layout.getLineWidth(lineIndex)
if (lineIndex != layout.lineCount - 1) {
addCharsToLineMiddle(textLine, words, titlePaint, desiredWidth, 0f)
} else {
//最后一行
val x = if (ReadBookConfig.titleCenter)
(visibleWidth - layout.getLineWidth(lineIndex)) / 2
else 0f
addCharsToLineLast(textLine, words, stringBuilder, titlePaint, x)
}
}
durY += paragraphSpacing
return durY
}
/**
* 正文
*/
private fun joinBody(
text: String, text: String,
y: Int, y: Float,
textPages: ArrayList<TextPage>, textPages: ArrayList<TextPage>,
pageLines: ArrayList<Int>, pageLines: ArrayList<Int>,
pageLengths: ArrayList<Int>, pageLengths: ArrayList<Int>,
stringBuilder: StringBuilder stringBuilder: StringBuilder,
): Int { isTitle: Boolean
): Float {
var durY = y var durY = y
val textPaint = if (isTitle) titlePaint else contentPaint
val layout = StaticLayout( val layout = StaticLayout(
text, contentPaint, visibleWidth, text, textPaint, visibleWidth,
Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra, true Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true
) )
for (lineIndex in 0 until layout.lineCount) { for (lineIndex in 0 until layout.lineCount) {
textPages.last().height = durY textPages.last().height = durY
val textLine = TextLine() val textLine = TextLine(isTitle = isTitle)
if (durY + layout.getLineBaseline(lineIndex) - layout.getLineTop(lineIndex) if (durY + textPaint.textHeight < visibleHeight) {
+ contentPaint.fontMetrics.descent < visibleHeight
) {
textPages.last().textLines.add(textLine) textPages.last().textLines.add(textLine)
durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) durY += textPaint.textHeight + lineSpacingExtra
} else { } else {
textPages.last().text = stringBuilder.toString() textPages.last().text = stringBuilder.toString()
stringBuilder.clear() stringBuilder.clear()
pageLines.add(textPages.last().textLines.size) pageLines.add(textPages.last().textLines.size)
pageLengths.add(textPages.last().text.length) pageLengths.add(textPages.last().text.length)
//新页面 //新页面
durY = layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) durY = textPaint.textHeight + lineSpacingExtra
textPages.add(TextPage()) textPages.add(TextPage())
textPages.last().textLines.add(textLine) textPages.last().textLines.add(textLine)
} }
textLine.lineTop = (paddingTop + durY - textLine.lineBottom = paddingTop + durY - lineSpacingExtra
(layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat() textLine.lineBase = textLine.lineBottom - textPaint.fontMetrics.descent
textLine.lineBase = (paddingTop + durY - textLine.lineTop = textLine.lineBottom - textPaint.textHeight
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
textLine.lineBottom = textLine.lineBase + contentPaint.fontMetrics.descent
val words = val words =
text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
stringBuilder.append(words) stringBuilder.append(words)
textLine.text = words textLine.text = words
val desiredWidth = layout.getLineWidth(lineIndex) val desiredWidth = layout.getLineWidth(lineIndex)
if (lineIndex == 0 && layout.lineCount > 1) { if (lineIndex == 0 && layout.lineCount > 1 && !isTitle) {
//第一行 //第一行
addCharsToLineFirst(textLine, words, contentPaint, desiredWidth) addCharsToLineFirst(textLine, words, textPaint, desiredWidth)
} else if (lineIndex == layout.lineCount - 1) { } else if (lineIndex == layout.lineCount - 1) {
//最后一行 //最后一行
addCharsToLineLast(textLine, words, stringBuilder, contentPaint, 0f) val x = if (isTitle && ReadBookConfig.titleCenter)
(visibleWidth - layout.getLineWidth(lineIndex)) / 2
else 0f
addCharsToLineLast(textLine, words, stringBuilder, textPaint, x)
} else { } else {
//中间行 //中间行
addCharsToLineMiddle(textLine, words, contentPaint, desiredWidth, 0f) addCharsToLineMiddle(textLine, words, textPaint, desiredWidth, 0f)
} }
} }
durY += paragraphSpacing durY += paragraphSpacing
@ -346,7 +292,7 @@ object ChapterProvider {
val bold = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL val bold = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL
contentPaint.typeface = Typeface.create(typeface, bold) contentPaint.typeface = Typeface.create(typeface, bold)
//间距 //间距
lineSpacingExtra = ReadBookConfig.lineSpacingExtra.dp.toFloat() lineSpacingExtra = ReadBookConfig.lineSpacingExtra.dp
paragraphSpacing = ReadBookConfig.paragraphSpacing.dp paragraphSpacing = ReadBookConfig.paragraphSpacing.dp
titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat() titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat()
contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat() contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat()
@ -366,4 +312,8 @@ object ChapterProvider {
visibleBottom = paddingTop + visibleHeight visibleBottom = paddingTop + visibleHeight
} }
private val TextPaint.textHeight: Float
get() {
return this.fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading
}
} }

@ -1,11 +1,12 @@
package io.legado.app.ui.book.read.page.delegate package io.legado.app.ui.book.read.page.delegate
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.PageView
class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
private val bitmapMatrix = Matrix()
private val shadowDrawableR: GradientDrawable private val shadowDrawableR: GradientDrawable
init { init {
@ -16,13 +17,6 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
shadowDrawableR.gradientType = GradientDrawable.LINEAR_GRADIENT shadowDrawableR.gradientType = GradientDrawable.LINEAR_GRADIENT
} }
override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) {
curPage.x = 0.toFloat()
prevPage.x = -viewWidth.toFloat()
nextPage.x = 0.toFloat()
super.setStartPoint(x, y, invalidate)
}
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
val offsetX = touchX - startX val offsetX = touchX - startX
@ -33,10 +27,14 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth
if (!isRunning) return if (!isRunning) return
if (mDirection == Direction.PREV) { if (mDirection == Direction.PREV) {
prevPage.translationX = offsetX - viewWidth bitmapMatrix.setTranslate(distanceX, 0.toFloat())
curBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
prevBitmap?.let { canvas.drawBitmap(it, bitmapMatrix, null) }
addShadow(distanceX.toInt(), canvas) addShadow(distanceX.toInt(), canvas)
} else if (mDirection == Direction.NEXT) { } else if (mDirection == Direction.NEXT) {
curPage.translationX = offsetX bitmapMatrix.setTranslate(distanceX - viewWidth, 0.toFloat())
nextBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
curBitmap?.let { canvas.drawBitmap(it, bitmapMatrix, null) }
addShadow(distanceX.toInt(), canvas) addShadow(distanceX.toInt(), canvas)
} }
} }
@ -52,8 +50,6 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
} }
override fun onAnimStop() { override fun onAnimStop() {
curPage.x = 0.toFloat()
prevPage.x = -viewWidth.toFloat()
if (!isCancel) { if (!isCancel) {
pageView.fillPage(mDirection) pageView.fillPage(mDirection)
} }

@ -1,11 +1,40 @@
package io.legado.app.ui.book.read.page.delegate package io.legado.app.ui.book.read.page.delegate
import android.graphics.Bitmap
import android.view.MotionEvent import android.view.MotionEvent
import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.PageView
import io.legado.app.utils.screenshot
import kotlin.math.abs import kotlin.math.abs
abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageView) { abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageView) {
protected var curBitmap: Bitmap? = null
protected var prevBitmap: Bitmap? = null
protected var nextBitmap: Bitmap? = null
override fun setDirection(direction: Direction) {
super.setDirection(direction)
setBitmap()
}
private fun setBitmap() {
when (mDirection) {
Direction.PREV -> {
prevBitmap?.recycle()
prevBitmap = prevPage.screenshot()
curBitmap?.recycle()
curBitmap = curPage.screenshot()
}
Direction.NEXT -> {
nextBitmap?.recycle()
nextBitmap = nextPage.screenshot()
curBitmap?.recycle()
curBitmap = curPage.screenshot()
}
else -> Unit
}
}
override fun onTouch(event: MotionEvent) { override fun onTouch(event: MotionEvent) {
when (event.action) { when (event.action) {
MotionEvent.ACTION_DOWN -> { MotionEvent.ACTION_DOWN -> {
@ -69,4 +98,14 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
onAnimStart() onAnimStart()
} }
override fun onDestroy() {
super.onDestroy()
prevBitmap?.recycle()
prevBitmap = null
curBitmap?.recycle()
curBitmap = null
nextBitmap?.recycle()
nextBitmap = null
}
} }

@ -3,8 +3,8 @@ package io.legado.app.ui.book.read.page.delegate
import android.graphics.* import android.graphics.*
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.os.Build import android.os.Build
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.PageView
import io.legado.app.utils.screenshot
import kotlin.math.* import kotlin.math.*
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@ -73,10 +73,6 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
private val mPaint: Paint = Paint().apply { style = Paint.Style.FILL } private val mPaint: Paint = Paint().apply { style = Paint.Style.FILL }
private var curBitmap: Bitmap? = null
private var prevBitmap: Bitmap? = null
private var nextBitmap: Bitmap? = null
init { init {
//设置颜色数组 //设置颜色数组
val color = intArrayOf(0x333333, -0x4fcccccd) val color = intArrayOf(0x333333, -0x4fcccccd)
@ -118,31 +114,21 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat() mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat()
} }
override fun onDestroy() {
super.onDestroy()
prevBitmap?.recycle()
prevBitmap = null
curBitmap?.recycle()
curBitmap = null
nextBitmap?.recycle()
nextBitmap = null
}
override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) {
super.setStartPoint(x, y, invalidate) super.setStartPoint(x, y, false)
calcCornerXY(x, y) calcCornerXY(x, y)
} }
override fun setTouchPoint(x: Float, y: Float, invalidate: Boolean) { override fun setTouchPoint(x: Float, y: Float, invalidate: Boolean) {
super.setTouchPoint(x, y, false) super.setTouchPoint(x, y, false)
//触摸y中间位置吧y变成屏幕高度 //触摸y中间位置吧y变成屏幕高度
if ((startY > viewHeight / 3.0 && startY < viewHeight * 2 / 3.0) if ((startY > viewHeight / 3 && startY < viewHeight * 2 / 3)
|| mDirection == Direction.PREV || mDirection == Direction.PREV
) { ) {
touchY = viewHeight.toFloat() touchY = viewHeight.toFloat()
} }
if (startY > viewHeight / 3.0 && startY < viewHeight / 2.0 if (startY > viewHeight / 3 && startY < viewHeight / 2
&& mDirection == Direction.NEXT && mDirection == Direction.NEXT
) { ) {
touchY = 1f touchY = 1f
@ -152,37 +138,22 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
override fun setDirection(direction: Direction) { override fun setDirection(direction: Direction) {
super.setDirection(direction) super.setDirection(direction)
setBitmap()
when (direction) { when (direction) {
Direction.PREV -> Direction.PREV ->
//上一页滑动不出现对角 //上一页滑动不出现对角
if (startX > viewWidth / 2.0) { if (startX > viewWidth / 2) {
calcCornerXY(startX, viewHeight.toFloat()) calcCornerXY(startX, viewHeight.toFloat())
} else { } else {
calcCornerXY(viewWidth - startX, viewHeight.toFloat()) calcCornerXY(viewWidth - startX, viewHeight.toFloat())
} }
Direction.NEXT -> Direction.NEXT ->
if (viewWidth / 2.0 > startX) { if (viewWidth / 2 > startX) {
calcCornerXY(viewWidth - startX, startY) calcCornerXY(viewWidth - startX, startY)
} }
else -> Unit else -> Unit
} }
} }
private fun setBitmap() {
when (mDirection) {
Direction.PREV -> {
prevBitmap = prevPage.screenshot()
curBitmap = curPage.screenshot()
}
Direction.NEXT -> {
nextBitmap = nextPage.screenshot()
curBitmap = curPage.screenshot()
}
else -> Unit
}
}
override fun onAnimStart() { override fun onAnimStart() {
var dx: Float var dx: Float
val dy: Float val dy: Float
@ -223,6 +194,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
} }
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
if (!isRunning) return
when (mDirection) { when (mDirection) {
Direction.NEXT -> { Direction.NEXT -> {
calcPoints() calcPoints()
@ -297,6 +269,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mMatrix.setValues(mMatrixArray) mMatrix.setValues(mMatrixArray)
mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y) mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y)
mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y) mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y)
canvas.drawColor(ReadBookConfig.bgMeanColor)
canvas.drawBitmap(bitmap, mMatrix, mPaint) canvas.drawBitmap(bitmap, mMatrix, mPaint)
mPaint.colorFilter = null mPaint.colorFilter = null
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y) canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y)
@ -485,8 +458,8 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
* 计算拖拽点对应的拖拽脚 * 计算拖拽点对应的拖拽脚
*/ */
private fun calcCornerXY(x: Float, y: Float) { private fun calcCornerXY(x: Float, y: Float) {
mCornerX = if (x <= viewWidth / 2.0) 0 else viewWidth mCornerX = if (x <= viewWidth / 2) 0 else viewWidth
mCornerY = if (y <= viewHeight / 2.0) 0 else viewHeight mCornerY = if (y <= viewHeight / 2) 0 else viewHeight
mIsRtOrLb = (mCornerX == 0 && mCornerY == viewHeight) mIsRtOrLb = (mCornerX == 0 && mCornerY == viewHeight)
|| (mCornerY == 0 && mCornerX == viewWidth) || (mCornerY == 0 && mCornerX == viewWidth)
} }
@ -497,21 +470,23 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mMiddleX = (mTouchX + mCornerX) / 2 mMiddleX = (mTouchX + mCornerX) / 2
mMiddleY = (mTouchY + mCornerY) / 2 mMiddleY = (mTouchY + mCornerY) / 2
mBezierControl1.x = mBezierControl1.x =
mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX) mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX)
mBezierControl1.y = mCornerY.toFloat() mBezierControl1.y = mCornerY.toFloat()
mBezierControl2.x = mCornerX.toFloat() mBezierControl2.x = mCornerX.toFloat()
mBezierControl2.y = if ((mCornerY - mMiddleY).toInt() == 0) {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / 0.1f val f4 = mCornerY - mMiddleY
if (f4 == 0f) {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / 0.1f
} else { } else {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY) mBezierControl2.y =
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY)
} }
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2 mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2
mBezierStart1.y = mCornerY.toFloat() mBezierStart1.y = mCornerY.toFloat()
//固定左边上下两个点
// 固定左边上下两个点
if (mTouchX > 0 && mTouchX < viewWidth) { if (mTouchX > 0 && mTouchX < viewWidth) {
if (mBezierStart1.x < 0 || mBezierStart1.x > viewWidth) { if (mBezierStart1.x < 0 || mBezierStart1.x > viewWidth) {
if (mBezierStart1.x < 0) if (mBezierStart1.x < 0)
@ -520,6 +495,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
val f1 = abs(mCornerX - mTouchX) val f1 = abs(mCornerX - mTouchX)
val f2 = viewWidth * f1 / mBezierStart1.x val f2 = viewWidth * f1 / mBezierStart1.x
mTouchX = abs(mCornerX - f2) mTouchX = abs(mCornerX - f2)
val f3 = abs(mCornerX - mTouchX) * abs(mCornerY - mTouchY) / f1 val f3 = abs(mCornerX - mTouchX) * abs(mCornerY - mTouchY) / f1
mTouchY = abs(mCornerY - f3) mTouchY = abs(mCornerY - f3)
@ -531,10 +507,14 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mBezierControl1.y = mCornerY.toFloat() mBezierControl1.y = mCornerY.toFloat()
mBezierControl2.x = mCornerX.toFloat() mBezierControl2.x = mCornerX.toFloat()
mBezierControl2.y = if ((mCornerY - mMiddleY).toInt() == 0) {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / 0.1f val f5 = mCornerY - mMiddleY
if (f5 == 0f) {
mBezierControl2.y =
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / 0.1f
} else { } else {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY) mBezierControl2.y =
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY)
} }
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2 mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2
@ -543,12 +523,19 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mBezierStart2.x = mCornerX.toFloat() mBezierStart2.x = mCornerX.toFloat()
mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y) / 2 mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y) / 2
mTouchToCornerDis = hypot(mTouchX - mCornerX, touchY - mCornerY) mTouchToCornerDis = hypot(
(mTouchX - mCornerX).toDouble(),
(mTouchY - mCornerY).toDouble()
).toFloat()
mBezierEnd1 = mBezierEnd1 = getCross(
getCross(PointF(mTouchX, mTouchY), mBezierControl1, mBezierStart1, mBezierStart2) PointF(mTouchX, mTouchY), mBezierControl1, mBezierStart1,
mBezierEnd2 = mBezierStart2
getCross(PointF(mTouchX, mTouchY), mBezierControl2, mBezierStart1, mBezierStart2) )
mBezierEnd2 = getCross(
PointF(mTouchX, mTouchY), mBezierControl2, mBezierStart1,
mBezierStart2
)
mBezierVertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4 mBezierVertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4
mBezierVertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4 mBezierVertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4

@ -1,16 +1,12 @@
package io.legado.app.ui.book.read.page.delegate package io.legado.app.ui.book.read.page.delegate
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Matrix
import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.PageView
class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { private val bitmapMatrix = Matrix()
curPage.x = 0f
prevPage.x = -viewWidth.toFloat()
nextPage.x = viewWidth.toFloat()
super.setStartPoint(x, y, invalidate)
}
override fun onAnimStart() { override fun onAnimStart() {
val distanceX: Float val distanceX: Float
@ -41,21 +37,22 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
if ((mDirection == Direction.NEXT && offsetX > 0) if ((mDirection == Direction.NEXT && offsetX > 0)
|| (mDirection == Direction.PREV && offsetX < 0) || (mDirection == Direction.PREV && offsetX < 0)
) return ) return
val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth
if (!isRunning) return if (!isRunning) return
if (mDirection == Direction.PREV) { if (mDirection == Direction.PREV) {
curPage.translationX = offsetX bitmapMatrix.setTranslate(distanceX + viewWidth, 0.toFloat())
prevPage.translationX = offsetX - viewWidth curBitmap?.let { canvas.drawBitmap(it, bitmapMatrix, null) }
bitmapMatrix.setTranslate(distanceX, 0.toFloat())
prevBitmap?.let { canvas.drawBitmap(it, bitmapMatrix, null) }
} else if (mDirection == Direction.NEXT) { } else if (mDirection == Direction.NEXT) {
curPage.translationX = offsetX bitmapMatrix.setTranslate(distanceX, 0.toFloat())
nextPage.translationX = offsetX + viewWidth nextBitmap?.let { canvas.drawBitmap(it, bitmapMatrix, null) }
bitmapMatrix.setTranslate(distanceX - viewWidth, 0.toFloat())
curBitmap?.let { canvas.drawBitmap(it, bitmapMatrix, null) }
} }
} }
override fun onAnimStop() { override fun onAnimStop() {
curPage.x = 0f
prevPage.x = -viewWidth.toFloat()
nextPage.x = viewWidth.toFloat()
if (!isCancel) { if (!isCancel) {
pageView.fillPage(mDirection) pageView.fillPage(mDirection)
} }

@ -14,7 +14,7 @@ data class TextPage(
var pageSize: Int = 0, var pageSize: Int = 0,
var chapterSize: Int = 0, var chapterSize: Int = 0,
var chapterIndex: Int = 0, var chapterIndex: Int = 0,
var height: Int = 0 var height: Float = 0f
) { ) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@ -46,7 +46,7 @@ data class TextPage(
} }
textLines.add(textLine) textLines.add(textLine)
} }
height = ChapterProvider.visibleHeight height = ChapterProvider.visibleHeight.toFloat()
} }
return this return this
} }

@ -16,6 +16,7 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.storage.Backup import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.SyncBookProgress
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadAloud
@ -128,6 +129,7 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
override fun finish() { override fun finish() {
if (!BuildConfig.DEBUG) { if (!BuildConfig.DEBUG) {
SyncBookProgress.uploadBookProgress()
Backup.autoBack(this) Backup.autoBack(this)
} }
super.finish() super.finish()

@ -8,6 +8,7 @@ import io.legado.app.R
import io.legado.app.base.BaseActivity import io.legado.app.base.BaseActivity
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.storage.SyncBookProgress
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.main.MainActivity import io.legado.app.ui.main.MainActivity
@ -41,6 +42,7 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) {
else -> null else -> null
} }
} }
SyncBookProgress.downloadBookProgress()
root_view.postDelayed({ startMainActivity() }, 300) root_view.postDelayed({ startMainActivity() }, 300)
} }

@ -5,6 +5,7 @@ import android.graphics.Bitmap
import android.graphics.Bitmap.Config import android.graphics.Bitmap.Config
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color
import android.renderscript.Allocation import android.renderscript.Allocation
import android.renderscript.Element import android.renderscript.Element
import android.renderscript.RenderScript import android.renderscript.RenderScript
@ -12,10 +13,7 @@ import android.renderscript.ScriptIntrinsicBlur
import android.view.View import android.view.View
import io.legado.app.App import io.legado.app.App
import java.io.IOException import java.io.IOException
import kotlin.math.ceil import kotlin.math.*
import kotlin.math.floor
import kotlin.math.min
import kotlin.math.sqrt
@Suppress("unused", "WeakerAccess") @Suppress("unused", "WeakerAccess")
@ -257,4 +255,31 @@ object BitmapUtils {
return blurredBitmap return blurredBitmap
} }
fun getMeanColor(bitmap: Bitmap): Int {
val width: Int = bitmap.width
val height: Int = bitmap.height
var pixel: Int
var pixelSumRed = 0
var pixelSumBlue = 0
var pixelSumGreen = 0
for (i in 0..99) {
for (j in 70..99) {
pixel = bitmap.getPixel(
(i * width / 100.toFloat()).roundToInt(),
(j * height / 100.toFloat()).roundToInt()
)
pixelSumRed += Color.red(pixel)
pixelSumGreen += Color.green(pixel)
pixelSumBlue += Color.blue(pixel)
}
}
val averagePixelRed = pixelSumRed / 3000
val averagePixelBlue = pixelSumBlue / 3000
val averagePixelGreen = pixelSumGreen / 3000
return Color.rgb(
averagePixelRed + 3,
averagePixelGreen + 3, averagePixelBlue + 3
)
}
} }

@ -614,7 +614,7 @@
<string name="selectText">长按选择文本</string> <string name="selectText">长按选择文本</string>
<string name="header">页眉</string> <string name="header">页眉</string>
<string name="main_body">正文</string> <string name="main_body">正文</string>
<string name="footer"></string> <string name="footer"></string>
<string name="select_end">文本选择结束位置</string> <string name="select_end">文本选择结束位置</string>
<string name="select_start">文本选择开始位置</string> <string name="select_start">文本选择开始位置</string>
<string name="share_layout">共用布局</string> <string name="share_layout">共用布局</string>

@ -24,6 +24,13 @@
android:summary="@string/web_dav_pw_s" android:summary="@string/web_dav_pw_s"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference
android:key="webDavCreateDir"
android:defaultValue="true"
android:title="创建子文件夹"
android:summary="创建legado文件夹作为备份文件夹"
app:iconSpaceReserved="false" />
</io.legado.app.ui.widget.prefs.PreferenceCategory> </io.legado.app.ui.widget.prefs.PreferenceCategory>
<io.legado.app.ui.widget.prefs.PreferenceCategory app:iconSpaceReserved="false"> <io.legado.app.ui.widget.prefs.PreferenceCategory app:iconSpaceReserved="false">

Loading…
Cancel
Save