Merge remote-tracking branch 'origin/master'

pull/61/head
Administrator 5 years ago
commit e9f3779ed0
  1. 4
      app/src/main/assets/updateLog.md
  2. 2
      app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
  3. 20
      app/src/main/java/io/legado/app/service/AudioPlayService.kt
  4. 12
      app/src/main/java/io/legado/app/service/help/AudioPlay.kt
  5. 14
      app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
  6. 5
      app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
  7. 3
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  8. 29
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  9. 3
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
  10. 6
      app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt
  11. 1
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
  12. 17
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  13. 13
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
  14. 16
      app/src/main/java/io/legado/app/ui/widget/page/ChapterProvider.kt
  15. 4
      app/src/main/java/io/legado/app/web/controller/SourceController.kt
  16. 1
      app/src/main/res/layout/activity_rss_read.xml
  17. 78
      app/src/main/res/layout/dialog_download_choice.xml

@ -4,6 +4,10 @@
* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】,提示存储权限,选择允许即可导入成功。
* 注意:由于安卓10更改了权限策略,还需要给「允许安装其他应用」的权限才能导入源。MIUI11也需要此权限。
**2019/12/16**
* 添加几个主题选择
* 音频播放添加header支持
**2019/12/15**
* 修复清理缓存会把其他文件删除的问题
* 详情页模糊背景

@ -75,7 +75,7 @@ interface BookSourceDao {
fun update(vararg bookSource: BookSource)
@Delete
fun delete(vararg bookSource: BookSource)
fun delete(bookSource: BookSource)
@Query("delete from book_sources where bookSourceUrl = :key")
fun delete(key: String)

@ -9,6 +9,7 @@ import android.graphics.BitmapFactory
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.media.MediaPlayer
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.support.v4.media.session.MediaSessionCompat
@ -44,10 +45,10 @@ class AudioPlayService : BaseService(),
companion object {
var isRun = false
var pause = false
var timeMinute: Int = 0
}
var pause = false
private val handler = Handler()
private lateinit var audioManager: AudioManager
private var mFocusRequest: AudioFocusRequest? = null
@ -91,7 +92,6 @@ class AudioPlayService : BaseService(),
Action.prev -> moveToPrev()
Action.next -> moveToNext()
Action.adjustSpeed -> upSpeed(intent.getFloatExtra("adjust", 1f))
Action.moveTo -> moveTo(intent.getIntExtra("index", AudioPlay.durChapterIndex))
Action.addTimer -> addTimer()
Action.setTimer -> setTimer(intent.getIntExtra("minute", 0))
Action.adjustProgress -> adjustProgress(intent.getIntExtra("position", position))
@ -121,7 +121,8 @@ class AudioPlayService : BaseService(),
AudioPlay.status = Status.PLAY
postEvent(Bus.AUDIO_STATE, Status.PLAY)
mediaPlayer.reset()
mediaPlayer.setDataSource(url)
val uri = Uri.parse(url)
mediaPlayer.setDataSource(this, uri, AudioPlay.headers())
mediaPlayer.prepareAsync()
} catch (e: Exception) {
launch {
@ -133,7 +134,7 @@ class AudioPlayService : BaseService(),
}
private fun pause(pause: Boolean) {
this.pause = pause
AudioPlayService.pause = pause
handler.removeCallbacks(mpRunnable)
position = mediaPlayer.currentPosition
mediaPlayer.pause()
@ -311,16 +312,6 @@ class AudioPlayService : BaseService(),
}
}
private fun moveTo(index: Int) {
mediaPlayer.pause()
AudioPlay.durChapterIndex = index
AudioPlay.durPageIndex = 0
AudioPlay.book?.durChapterIndex = AudioPlay.durChapterIndex
saveRead()
position = 0
loadContent(AudioPlay.durChapterIndex)
}
private fun moveToPrev() {
if (AudioPlay.durChapterIndex > 0) {
mediaPlayer.pause()
@ -354,6 +345,7 @@ class AudioPlayService : BaseService(),
book.durChapterTime = System.currentTimeMillis()
book.durChapterIndex = AudioPlay.durChapterIndex
book.durChapterPos = AudioPlay.durPageIndex
book.durChapterTitle = subtitle
App.db.bookDao().update(book)
}
}

@ -21,6 +21,10 @@ object AudioPlay {
var webBook: WebBook? = null
val loadingChapters = arrayListOf<Int>()
fun headers(): Map<String, String>? {
return webBook?.bookSource?.getHeaderMap()
}
fun play(context: Context) {
val intent = Intent(context, AudioPlayService::class.java)
intent.action = Action.play
@ -85,12 +89,4 @@ object AudioPlay {
}
}
fun moveTo(context: Context, index: Int) {
if (AudioPlayService.isRun) {
val intent = Intent(context, AudioPlayService::class.java)
intent.action = Action.moveTo
intent.putExtra("index", index)
context.startService(intent)
}
}
}

@ -10,6 +10,7 @@ import android.view.MenuItem
import android.widget.SeekBar
import androidx.lifecycle.Observer
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
@ -21,6 +22,7 @@ import io.legado.app.help.ImageLoader
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.service.AudioPlayService
import io.legado.app.service.help.AudioPlay
import io.legado.app.ui.changesource.ChangeSourceDialog
import io.legado.app.ui.chapterlist.ChapterListActivity
@ -117,6 +119,7 @@ class AudioPlayActivity : VMBaseActivity<AudioPlayViewModel>(R.layout.activity_a
.centerCrop()
.into(iv_cover)
ImageLoader.load(this, path)
.transition(DrawableTransitionOptions.withCrossFade(1500))
.thumbnail(defaultCover())
.centerCrop()
.apply(RequestOptions.bitmapTransform(BlurTransformation(this, 25)))
@ -170,7 +173,16 @@ class AudioPlayActivity : VMBaseActivity<AudioPlayViewModel>(R.layout.activity_a
when (requestCode) {
requestCodeChapter -> data?.getIntExtra("index", AudioPlay.durChapterIndex)?.let {
if (it != AudioPlay.durChapterIndex) {
AudioPlay.moveTo(this, it)
val isPlay = !AudioPlayService.pause
AudioPlay.pause(this)
AudioPlay.status = Status.STOP
AudioPlay.durChapterIndex = it
AudioPlay.durPageIndex = 0
AudioPlay.book?.durChapterIndex = AudioPlay.durChapterIndex
viewModel.saveRead()
if (isPlay) {
AudioPlay.play(this)
}
}
}
}

@ -124,13 +124,16 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
}
}
private fun saveRead() {
fun saveRead() {
execute {
AudioPlay.book?.let { book ->
book.lastCheckCount = 0
book.durChapterTime = System.currentTimeMillis()
book.durChapterIndex = AudioPlay.durChapterIndex
book.durChapterPos = AudioPlay.durPageIndex
App.db.bookChapterDao().getChapter(book.bookUrl, book.durChapterIndex)?.let {
book.durChapterTitle = it.title
}
App.db.bookDao().update(book)
}
}

@ -7,7 +7,6 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.Observer
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions.bitmapTransform
@ -115,7 +114,7 @@ class BookInfoActivity :
.error(R.drawable.image_cover_default)
.centerCrop()
.into(iv_cover)
Glide.with(this).load(it)
ImageLoader.load(this, it)
.transition(DrawableTransitionOptions.withCrossFade(1500))
.thumbnail(defaultCover())
.centerCrop()

@ -1,5 +1,6 @@
package io.legado.app.ui.book.read
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
@ -8,6 +9,7 @@ import android.text.SpannableStringBuilder
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
@ -18,11 +20,10 @@ import io.legado.app.constant.Status
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.ReadBookConfig
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.lib.dialogs.*
import io.legado.app.receiver.TimeElectricityReceiver
import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.Download
import io.legado.app.service.help.ReadAloud
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.config.*
@ -38,6 +39,7 @@ import io.legado.app.ui.widget.page.PageView
import io.legado.app.ui.widget.page.delegate.PageDelegate
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_book_read.*
import kotlinx.android.synthetic.main.dialog_download_choice.view.*
import kotlinx.android.synthetic.main.view_book_page.*
import kotlinx.android.synthetic.main.view_read_menu.*
import kotlinx.coroutines.Dispatchers.IO
@ -131,6 +133,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
/**
* 菜单
*/
@SuppressLint("InflateParams")
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_change_source -> {
@ -146,6 +149,26 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
viewModel.refreshContent(it)
}
}
R.id.menu_download -> ReadBook.book?.let { book ->
alert(titleResource = R.string.download_offline) {
var view: View? = null
customView {
layoutInflater.inflate(R.layout.dialog_download_choice, null).apply {
view = this
edit_start.setText(book.durChapterIndex.toString())
edit_end.setText(book.totalChapterNum.toString())
}
}
yesButton {
view?.apply {
val start = edit_start?.text?.toString()?.toInt() ?: 0
val end = edit_end?.text?.toString()?.toInt() ?: book.totalChapterNum
Download.start(this@ReadBookActivity, book.bookUrl, start, end)
}
}
noButton()
}.show().applyTint()
}
}
return super.onCompatOptionsItemSelected(item)
}

@ -91,7 +91,8 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
App.db.bookSourceDao().getBookSource(it)
}.let {
val json = GSON.toJson(it)
val file = FileHelp.getFile(Backup.exportPath + File.separator + "exportBookSource.json")
val file =
FileHelp.getFile(Backup.exportPath + File.separator + "exportBookSource.json")
file.writeText(json)
}
}.onSuccess {

@ -48,9 +48,9 @@ class ChapterListFragment : VMBaseFragment<ChapterListViewModel>(R.layout.fragme
viewModel.bookUrl?.let { bookUrl ->
App.db.bookChapterDao().observeByBook(bookUrl).observe(viewLifecycleOwner, Observer {
adapter.setItems(it)
viewModel.book?.let {
durChapterIndex = it.durChapterIndex
tv_current_chapter_info.text = it.durChapterTitle
viewModel.book?.let { book ->
durChapterIndex = book.durChapterIndex
tv_current_chapter_info.text = book.durChapterTitle
recycler_view.scrollToPosition(durChapterIndex)
}
})

@ -126,6 +126,7 @@ class RssArticlesActivity : VMBaseActivity<RssArticlesViewModel>(R.layout.activi
override fun readRss(rssArticle: RssArticle) {
viewModel.read(rssArticle)
startActivity<ReadRssActivity>(
Pair("title", rssArticle.title),
Pair("origin", rssArticle.origin),
Pair("link", rssArticle.link)
)

@ -25,7 +25,7 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
override fun onActivityCreated(savedInstanceState: Bundle?) {
viewModel.callBack = this
title = intent.getStringExtra("title")
title_bar.title = intent.getStringExtra("title")
initWebView()
initLiveData()
viewModel.initData(intent)
@ -59,20 +59,11 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
viewModel.rssArticle?.let {
upJavaScriptEnable()
val url = NetworkUtils.getAbsoluteURL(it.origin, it.link)
val html = viewModel.clHtml(content)
if (viewModel.rssSource?.loadWithBaseUrl == true) {
webView.loadDataWithBaseURL(
url,
"<style>img{max-width:100%}</style>$content",
"text/html",
"utf-8",
url
)
webView.loadDataWithBaseURL(url, html, "text/html", "utf-8", url)
} else {
webView.loadData(
"<style>img{max-width:100%}</style>$content",
"text/html",
"utf-8"
)
webView.loadData(html, "text/html", "utf-8")
}
}
})

@ -77,6 +77,19 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application) {
}
}
fun clHtml(content: String): String {
return """
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style>img{max-width:100% !important; width:auto; height:auto;}</style>
</head>
<body style:"height:auto;max-width: 100%; width:auto;">
$content
</body></html>
"""
}
interface CallBack {
fun upStarMenu()
}

@ -4,6 +4,8 @@ import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.text.style.RelativeSizeSpan
import androidx.core.text.HtmlCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT
import io.legado.app.App
import io.legado.app.data.entities.BookChapter
import io.legado.app.lib.theme.accentColor
@ -17,7 +19,9 @@ object ChapterProvider {
fun getTextChapter(
bookChapter: BookChapter,
content: String, chapterSize: Int
content: String,
chapterSize: Int,
isHtml: Boolean = false
): TextChapter {
textView?.let {
val textPages = arrayListOf<TextPage>()
@ -26,7 +30,15 @@ object ChapterProvider {
var surplusText = content
var pageIndex = 0
while (surplusText.isNotEmpty()) {
val spannableStringBuilder = SpannableStringBuilder(surplusText)
val spannableStringBuilder =
if (isHtml) {
HtmlCompat.fromHtml(
surplusText,
FROM_HTML_MODE_COMPACT
) as SpannableStringBuilder
} else {
SpannableStringBuilder(surplusText)
}
if (pageIndex == 0) {
val end = surplusText.indexOf("\n")
if (end > 0) {

@ -71,7 +71,9 @@ class SourceController {
fun deleteSources(postData: String?): ReturnData {
kotlin.runCatching {
GSON.fromJsonArray<BookSource>(postData)?.let {
App.db.bookSourceDao().delete(*it.toTypedArray())
it.forEach { source ->
App.db.bookSourceDao().delete(source)
}
}
}
return ReturnData().setData("已执行"/*okSources*/)

@ -5,6 +5,7 @@
android:orientation="vertical">
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/background"
android:gravity="center"
android:orientation="horizontal">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="5dp"
android:text="@string/chapter"
android:textColor="@color/tv_text_default"
android:textSize="16sp"
tools:ignore="RtlHardcoded" />
<EditText
android:id="@+id/edit_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/bg_edit"
android:inputType="number"
android:lines="1"
android:maxLength="5"
android:minWidth="60dp"
android:paddingLeft="5dp"
android:paddingTop="4dp"
android:paddingRight="5dp"
android:paddingBottom="4dp"
android:textColor="@color/tv_text_default"
android:textCursorDrawable="@drawable/shape_text_cursor"
android:textSize="14sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="@string/to"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
<EditText
android:id="@+id/edit_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/bg_edit"
android:inputType="number"
android:lines="1"
android:maxLength="5"
android:minWidth="60dp"
android:paddingLeft="5dp"
android:paddingTop="4dp"
android:paddingRight="5dp"
android:paddingBottom="4dp"
android:textColor="@color/tv_text_default"
android:textCursorDrawable="@drawable/shape_text_cursor"
android:textSize="14sp" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Loading…
Cancel
Save