mirror of
https://github.com/gedoor/legado.git
synced 2024-08-30 09:23:26 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
2ac94f9486
27
.github/scripts/cronet.sh
vendored
27
.github/scripts/cronet.sh
vendored
@ -1,14 +1,16 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
echo "fetch release info from https://chromiumdash.appspot.com ..."
|
branch=$1
|
||||||
|
[ -z $1 ] && branch=Stable
|
||||||
branch="Stable"
|
[ -z $GITHUB_ENV ] && echo "Error: Unexpected github workflow environment" && exit
|
||||||
|
|
||||||
|
# 获取最新cronet版本
|
||||||
|
echo "fetch $branch release info from https://chromiumdash.appspot.com ..."
|
||||||
lastest_cronet_version=`curl -s "https://chromiumdash.appspot.com/fetch_releases?channel=$branch&platform=Android&num=1&offset=0" | jq .[0].version -r`
|
lastest_cronet_version=`curl -s "https://chromiumdash.appspot.com/fetch_releases?channel=$branch&platform=Android&num=1&offset=0" | jq .[0].version -r`
|
||||||
echo "lastest_cronet_version: $lastest_cronet_version"
|
echo "lastest_cronet_version: $lastest_cronet_version"
|
||||||
#lastest_cronet_version=100.0.4845.0
|
#lastest_cronet_version=100.0.4845.0
|
||||||
lastest_cronet_main_version=${lastest_cronet_version%%\.*}.0.0.0
|
lastest_cronet_main_version=${lastest_cronet_version%%\.*}.0.0.0
|
||||||
|
# 检查版本是否存在
|
||||||
function checkVersionExit() {
|
function checkVersionExit() {
|
||||||
local jar_url="https://storage.googleapis.com/chromium-cronet/android/$lastest_cronet_version/Release/cronet/cronet_api.jar"
|
local jar_url="https://storage.googleapis.com/chromium-cronet/android/$lastest_cronet_version/Release/cronet/cronet_api.jar"
|
||||||
statusCode=$(curl -s -I -w %{http_code} "$jar_url" -o /dev/null)
|
statusCode=$(curl -s -I -w %{http_code} "$jar_url" -o /dev/null)
|
||||||
@ -17,18 +19,25 @@ function checkVersionExit() {
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
# 添加变量到github env
|
||||||
|
function writeVariableToGithubEnv() {
|
||||||
|
echo "$1=$2" >> $GITHUB_ENV
|
||||||
|
}
|
||||||
|
# 获取本地cronet版本
|
||||||
path=$GITHUB_WORKSPACE/gradle.properties
|
path=$GITHUB_WORKSPACE/gradle.properties
|
||||||
current_cronet_version=`cat $path | grep CronetVersion | sed s/CronetVersion=//`
|
current_cronet_version=`cat $path | grep CronetVersion | sed s/CronetVersion=//`
|
||||||
echo "current_cronet_version: $current_cronet_version"
|
echo "current_cronet_version: $current_cronet_version"
|
||||||
|
|
||||||
if [[ $current_cronet_version < $lastest_cronet_version ]];then
|
if [[ $current_cronet_version < $lastest_cronet_version ]];then
|
||||||
checkVersionExit
|
checkVersionExit
|
||||||
|
# 更新gradle.properties
|
||||||
sed -i s/CronetVersion=.*/CronetVersion=$lastest_cronet_version/ $path
|
sed -i s/CronetVersion=.*/CronetVersion=$lastest_cronet_version/ $path
|
||||||
sed -i s/CronetMainVersion=.*/CronetMainVersion=$lastest_cronet_main_version/ $path
|
sed -i s/CronetMainVersion=.*/CronetMainVersion=$lastest_cronet_main_version/ $path
|
||||||
|
# 添加更新日志
|
||||||
sed "15a* 更新cronet: $lastest_cronet_version" -i $GITHUB_WORKSPACE/app/src/main/assets/updateLog.md
|
sed "15a* 更新cronet: $lastest_cronet_version" -i $GITHUB_WORKSPACE/app/src/main/assets/updateLog.md
|
||||||
echo "start download latest cronet"
|
# 生成pull request信息
|
||||||
chmod +x gradlew
|
writeVariableToGithubEnv PR_TITLE "Bump cronet from $current_cronet_version to $lastest_cronet_version"
|
||||||
./gradlew app:downloadCronet
|
writeVariableToGithubEnv PR_BODY "Changes in the [Git log](https://chromium.googlesource.com/chromium/src/+log/$current_cronet_version..$lastest_cronet_version)"
|
||||||
|
# 生成cronet flag
|
||||||
|
writeVariableToGithubEnv cronet ok
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
17
.github/workflows/cronet.yml
vendored
17
.github/workflows/cronet.yml
vendored
@ -11,17 +11,19 @@ jobs:
|
|||||||
if: ${{ github.repository == 'gedoor/legado' }}
|
if: ${{ github.repository == 'gedoor/legado' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Gradle
|
|
||||||
uses: gradle/gradle-build-action@v2
|
|
||||||
|
|
||||||
- name: Check Cronet Updates
|
- name: Check Cronet Updates
|
||||||
|
run: source .github/scripts/cronet.sh
|
||||||
|
|
||||||
|
- uses: gradle/gradle-build-action@v2
|
||||||
|
if: ${{ env.cronet == 'ok' }}
|
||||||
|
- name: Download Cronet
|
||||||
|
if: ${{ env.cronet == 'ok' }}
|
||||||
run: |
|
run: |
|
||||||
echo "获取cronet最新版本"
|
chmod +x gradlew
|
||||||
source .github/scripts/cronet.sh
|
./gradlew app:downloadCronet
|
||||||
echo "PR_TITLE=Bump cronet from $current_cronet_version to $lastest_cronet_version " >> $GITHUB_ENV
|
|
||||||
echo "PR_BODY=Changes in the [Git log](https://chromium.googlesource.com/chromium/src/+log/$current_cronet_version..$lastest_cronet_version)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
|
if: ${{ env.cronet == 'ok' }}
|
||||||
uses: peter-evans/create-pull-request@v4
|
uses: peter-evans/create-pull-request@v4
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
@ -38,4 +40,3 @@ jobs:
|
|||||||
*cronet.json
|
*cronet.json
|
||||||
*updateLog.md
|
*updateLog.md
|
||||||
gradle.properties
|
gradle.properties
|
||||||
|
|
||||||
|
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -60,6 +60,8 @@ jobs:
|
|||||||
VERSIONL: ${{ needs.prepare.outputs.versionL }}
|
VERSIONL: ${{ needs.prepare.outputs.versionL }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
- name: Clear 18PlusList.txt
|
- name: Clear 18PlusList.txt
|
||||||
run: |
|
run: |
|
||||||
echo "清空18PlusList.txt"
|
echo "清空18PlusList.txt"
|
||||||
|
@ -172,7 +172,7 @@ dependencies {
|
|||||||
implementation('com.google.code.gson:gson:2.10.1')
|
implementation('com.google.code.gson:gson:2.10.1')
|
||||||
|
|
||||||
//lifecycle
|
//lifecycle
|
||||||
def lifecycle_version = '2.5.1'
|
def lifecycle_version = '2.6.0'
|
||||||
implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycle_version")
|
implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycle_version")
|
||||||
implementation("androidx.lifecycle:lifecycle-service:$lifecycle_version")
|
implementation("androidx.lifecycle:lifecycle-service:$lifecycle_version")
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ dependencies {
|
|||||||
|
|
||||||
//加解密类库,有些书源使用
|
//加解密类库,有些书源使用
|
||||||
//noinspection GradleDependency,GradlePackageUpdate
|
//noinspection GradleDependency,GradlePackageUpdate
|
||||||
implementation('cn.hutool:hutool-crypto:5.8.14')
|
implementation('cn.hutool:hutool-crypto:5.8.15')
|
||||||
|
|
||||||
//firebase, 崩溃统计和性能统计, 会导致共存版本崩溃
|
//firebase, 崩溃统计和性能统计, 会导致共存版本崩溃
|
||||||
//implementation platform('com.google.firebase:firebase-bom:30.0.1')
|
//implementation platform('com.google.firebase:firebase-bom:30.0.1')
|
||||||
|
1
app/proguard-rules.pro
vendored
1
app/proguard-rules.pro
vendored
@ -392,6 +392,7 @@
|
|||||||
-keep class io.legado.app.lib.cronet.CronetInterceptor{*;}
|
-keep class io.legado.app.lib.cronet.CronetInterceptor{*;}
|
||||||
-keep class io.legado.app.lib.cronet.CronetLoader{*;}
|
-keep class io.legado.app.lib.cronet.CronetLoader{*;}
|
||||||
-keep class io.legado.app.help.AppUpdateGitHub{*;}
|
-keep class io.legado.app.help.AppUpdateGitHub{*;}
|
||||||
|
-keep class io.legado.app.help.AppIntentType{*;}
|
||||||
# Error Exception
|
# Error Exception
|
||||||
-keep class * extends java.lang.Exception
|
-keep class * extends java.lang.Exception
|
||||||
-keep class * extends java.lang.Error
|
-keep class * extends java.lang.Error
|
||||||
|
@ -381,12 +381,20 @@
|
|||||||
<data android:mimeType="*/*" />
|
<data android:mimeType="*/*" />
|
||||||
<!--TXT-->
|
<!--TXT-->
|
||||||
<data android:pathAdvancedPattern=".*\\.[tT][xX][tT]" />
|
<data android:pathAdvancedPattern=".*\\.[tT][xX][tT]" />
|
||||||
|
<data android:pathPattern=".*\\.txt" />
|
||||||
|
<data android:pathPattern=".*\\.TXT" />
|
||||||
<!--JSON-->
|
<!--JSON-->
|
||||||
<data android:pathAdvancedPattern=".*\\.[jJ][sS][oO][nN]" />
|
<data android:pathAdvancedPattern=".*\\.[jJ][sS][oO][nN]" />
|
||||||
|
<data android:pathPattern=".*\\.json" />
|
||||||
|
<data android:pathPattern=".*\\.JSON" />
|
||||||
<!-- EPUB -->
|
<!-- EPUB -->
|
||||||
<data android:pathAdvancedPattern=".*\\.[eE][pP][uU][bB]" />
|
<data android:pathAdvancedPattern=".*\\.[eE][pP][uU][bB]" />
|
||||||
|
<data android:pathPattern=".*\\.epub" />
|
||||||
|
<data android:pathPattern=".*\\.EPUB" />
|
||||||
<!-- pdf -->
|
<!-- pdf -->
|
||||||
<data android:pathAdvancedPattern=".*\\.[pP][dD][fF]" />
|
<data android:pathAdvancedPattern=".*\\.[pP][dD][fF]" />
|
||||||
|
<data android:pathPattern=".*\\.pdf" />
|
||||||
|
<data android:pathPattern=".*\\.PDF" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package io.legado.app.help
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.webkit.WebSettings
|
||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.model.ReadAloud
|
import io.legado.app.model.ReadAloud
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
@ -65,6 +66,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
|
|||||||
map["MODEL"] = Build.MODEL
|
map["MODEL"] = Build.MODEL
|
||||||
map["SDK_INT"] = Build.VERSION.SDK_INT.toString()
|
map["SDK_INT"] = Build.VERSION.SDK_INT.toString()
|
||||||
map["RELEASE"] = Build.VERSION.RELEASE
|
map["RELEASE"] = Build.VERSION.RELEASE
|
||||||
|
map["WebViewUserAgent"] = WebSettings.getDefaultUserAgent(appCtx)
|
||||||
//获取app版本信息
|
//获取app版本信息
|
||||||
AppConst.appInfo.let {
|
AppConst.appInfo.let {
|
||||||
map["versionName"] = it.versionName
|
map["versionName"] = it.versionName
|
||||||
|
@ -585,16 +585,18 @@ class AnalyzeUrl(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置cookie 优先级
|
* 设置cookie 优先级
|
||||||
* urlOption临时cookie > 数据库cookie = okhttp CookieJar保存在内存中的cookie
|
* urlOption临时cookie > 数据库cookie
|
||||||
*/
|
*/
|
||||||
private fun setCookie() {
|
private fun setCookie() {
|
||||||
val cookie = kotlin.run {
|
val cookie = kotlin.run {
|
||||||
|
/* 每次调用getXX cookieJar已经保存过了
|
||||||
if (enabledCookieJar) {
|
if (enabledCookieJar) {
|
||||||
val key = "${domain}_cookieJar"
|
val key = "${domain}_cookieJar"
|
||||||
CacheManager.getFromMemory(key)?.let {
|
CacheManager.getFromMemory(key)?.let {
|
||||||
return@run it
|
return@run it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
CookieStore.getCookie(domain)
|
CookieStore.getCookie(domain)
|
||||||
}
|
}
|
||||||
if (cookie.isNotEmpty()) {
|
if (cookie.isNotEmpty()) {
|
||||||
@ -608,7 +610,7 @@ class AnalyzeUrl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存cookie在访问结束时就保存,不等到下次访问
|
* 保存cookieJar中的cookie在访问结束时就保存,不等到下次访问
|
||||||
*/
|
*/
|
||||||
private fun saveCookie() {
|
private fun saveCookie() {
|
||||||
//书源启用保存cookie时 添加内存中的cookie到数据库
|
//书源启用保存cookie时 添加内存中的cookie到数据库
|
||||||
|
@ -151,7 +151,7 @@ object BookInfo {
|
|||||||
coroutineContext.ensureActive()
|
coroutineContext.ensureActive()
|
||||||
Debug.log(bookSource.bookSourceUrl, "┌获取文件下载链接")
|
Debug.log(bookSource.bookSourceUrl, "┌获取文件下载链接")
|
||||||
book.downloadUrls = analyzeRule.getStringList(infoRule.downloadUrls, isUrl = true)
|
book.downloadUrls = analyzeRule.getStringList(infoRule.downloadUrls, isUrl = true)
|
||||||
if (book.downloadUrls == null) {
|
if (book.downloadUrls.isNullOrEmpty()) {
|
||||||
Debug.log(bookSource.bookSourceUrl, "└")
|
Debug.log(bookSource.bookSourceUrl, "└")
|
||||||
throw NoStackTraceException("下载链接为空")
|
throw NoStackTraceException("下载链接为空")
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,7 +45,7 @@ class GroupSelectDialog() : BaseDialogFragment(R.layout.dialog_book_group_picker
|
|||||||
private var requestCode: Int = -1
|
private var requestCode: Int = -1
|
||||||
private val viewModel: GroupViewModel by viewModels()
|
private val viewModel: GroupViewModel by viewModels()
|
||||||
private val adapter by lazy { GroupAdapter(requireContext()) }
|
private val adapter by lazy { GroupAdapter(requireContext()) }
|
||||||
private var callBack = (activity as? CallBack)
|
private val callBack get() = (activity as? CallBack)
|
||||||
private var groupId: Long = 0
|
private var groupId: Long = 0
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
@ -52,8 +52,7 @@ class BookInfoActivity :
|
|||||||
VMBaseActivity<ActivityBookInfoBinding, BookInfoViewModel>(toolBarTheme = Theme.Dark),
|
VMBaseActivity<ActivityBookInfoBinding, BookInfoViewModel>(toolBarTheme = Theme.Dark),
|
||||||
GroupSelectDialog.CallBack,
|
GroupSelectDialog.CallBack,
|
||||||
ChangeBookSourceDialog.CallBack,
|
ChangeBookSourceDialog.CallBack,
|
||||||
ChangeCoverDialog.CallBack,
|
ChangeCoverDialog.CallBack {
|
||||||
BookInfoViewModel.CallBack {
|
|
||||||
|
|
||||||
private val tocActivityResult = registerForActivityResult(TocActivityResult()) {
|
private val tocActivityResult = registerForActivityResult(TocActivityResult()) {
|
||||||
it?.let {
|
it?.let {
|
||||||
@ -108,10 +107,9 @@ class BookInfoActivity :
|
|||||||
binding.flAction.setBackgroundColor(bottomBackground)
|
binding.flAction.setBackgroundColor(bottomBackground)
|
||||||
binding.tvShelf.setTextColor(getPrimaryTextColor(ColorUtils.isColorLight(bottomBackground)))
|
binding.tvShelf.setTextColor(getPrimaryTextColor(ColorUtils.isColorLight(bottomBackground)))
|
||||||
binding.tvToc.text = getString(R.string.toc_s, getString(R.string.loading))
|
binding.tvToc.text = getString(R.string.toc_s, getString(R.string.loading))
|
||||||
viewModel.callBack = this
|
|
||||||
viewModel.bookData.observe(this) { showBook(it) }
|
viewModel.bookData.observe(this) { showBook(it) }
|
||||||
viewModel.chapterListData.observe(this) { upLoading(false, it) }
|
viewModel.chapterListData.observe(this) { upLoading(false, it) }
|
||||||
//viewModel.webFileData.observe(this) { showWebFileDownloadAlert() }
|
viewModel.waitDialogData.observe(this) { upWaitDialogStatus(it) }
|
||||||
viewModel.initData(intent)
|
viewModel.initData(intent)
|
||||||
initViewEvent()
|
initViewEvent()
|
||||||
}
|
}
|
||||||
@ -499,31 +497,34 @@ class BookInfoActivity :
|
|||||||
private fun showWebFileDownloadAlert(
|
private fun showWebFileDownloadAlert(
|
||||||
onClick: ((Book) -> Unit)? = null
|
onClick: ((Book) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
viewModel.webFileData.value?.let {
|
val webFiles = viewModel.webFiles
|
||||||
alert(titleResource = R.string.download_and_import_file) {
|
if (webFiles.isEmpty()) {
|
||||||
items<BookInfoViewModel.WebFile>(it) { _, webFile, _ ->
|
toastOnUi("Unexpected webFileData")
|
||||||
if (webFile.isSupported) {
|
return
|
||||||
/* import */
|
}
|
||||||
viewModel.importOrDownloadWebFile<Book>(webFile) {
|
alert(titleResource = R.string.download_and_import_file) {
|
||||||
onClick?.invoke(it)
|
items<BookInfoViewModel.WebFile>(webFiles) { _, webFile, _ ->
|
||||||
}
|
if (webFile.isSupported) {
|
||||||
} else {
|
/* import */
|
||||||
alert(
|
viewModel.importOrDownloadWebFile<Book>(webFile) {
|
||||||
title = getString(R.string.draw),
|
onClick?.invoke(it)
|
||||||
message = getString(R.string.file_not_supported, webFile.name)
|
}
|
||||||
) {
|
} else {
|
||||||
neutralButton(R.string.open_fun) {
|
alert(
|
||||||
/* download only */
|
title = getString(R.string.draw),
|
||||||
viewModel.importOrDownloadWebFile<Uri>(webFile) { uri ->
|
message = getString(R.string.file_not_supported, webFile.name)
|
||||||
openFileUri(uri, "*/*")
|
) {
|
||||||
}
|
neutralButton(R.string.open_fun) {
|
||||||
|
/* download only */
|
||||||
|
viewModel.importOrDownloadWebFile<Uri>(webFile) { uri ->
|
||||||
|
openFileUri(uri, "*/*")
|
||||||
}
|
}
|
||||||
noButton()
|
|
||||||
}
|
}
|
||||||
|
noButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ?: toastOnUi("Unexpected webFileData")
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readBook(book: Book) {
|
private fun readBook(book: Book) {
|
||||||
@ -588,14 +589,15 @@ class BookInfoActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onWebFileProcessFinally() {
|
private fun upWaitDialogStatus(isShow: Boolean) {
|
||||||
waitDialog.dismiss()
|
val showText = "Loading....."
|
||||||
}
|
if (isShow) {
|
||||||
|
waitDialog.run {
|
||||||
override fun onWebFileProcessStart() {
|
setText(showText)
|
||||||
waitDialog.run {
|
show()
|
||||||
setText("Loading.....")
|
}
|
||||||
show()
|
} else {
|
||||||
|
waitDialog.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,8 +21,6 @@ import io.legado.app.help.AppWebDav
|
|||||||
import io.legado.app.help.book.*
|
import io.legado.app.help.book.*
|
||||||
import io.legado.app.help.coroutine.Coroutine
|
import io.legado.app.help.coroutine.Coroutine
|
||||||
import io.legado.app.lib.webdav.ObjectNotFoundException
|
import io.legado.app.lib.webdav.ObjectNotFoundException
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeRule
|
|
||||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
|
||||||
import io.legado.app.model.BookCover
|
import io.legado.app.model.BookCover
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.model.localBook.LocalBook
|
import io.legado.app.model.localBook.LocalBook
|
||||||
@ -30,18 +28,17 @@ import io.legado.app.model.webBook.WebBook
|
|||||||
import io.legado.app.utils.isContentScheme
|
import io.legado.app.utils.isContentScheme
|
||||||
import io.legado.app.utils.postEvent
|
import io.legado.app.utils.postEvent
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
import io.legado.app.utils.runOnUI
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
|
||||||
class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||||
val bookData = MutableLiveData<Book>()
|
val bookData = MutableLiveData<Book>()
|
||||||
val chapterListData = MutableLiveData<List<BookChapter>>()
|
val chapterListData = MutableLiveData<List<BookChapter>>()
|
||||||
val webFileData = MutableLiveData<List<WebFile>>()
|
val webFiles = mutableListOf<WebFile>()
|
||||||
var inBookshelf = false
|
var inBookshelf = false
|
||||||
var bookSource: BookSource? = null
|
var bookSource: BookSource? = null
|
||||||
private var changeSourceCoroutine: Coroutine<*>? = null
|
private var changeSourceCoroutine: Coroutine<*>? = null
|
||||||
var callBack: CallBack? = null
|
val waitDialogData = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
fun initData(intent: Intent) {
|
fun initData(intent: Intent) {
|
||||||
execute {
|
execute {
|
||||||
@ -241,45 +238,24 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
scope: CoroutineScope = viewModelScope
|
scope: CoroutineScope = viewModelScope
|
||||||
) {
|
) {
|
||||||
execute(scope) {
|
execute(scope) {
|
||||||
|
webFiles.clear()
|
||||||
val fileName = "${book.name} 作者:${book.author}"
|
val fileName = "${book.name} 作者:${book.author}"
|
||||||
if (book.downloadUrls.isNullOrEmpty()) {
|
book.downloadUrls!!.map {
|
||||||
val ruleDownloadUrls = bookSource.getBookInfoRule().downloadUrls
|
val mFileName = "${fileName}.${LocalBook.parseFileSuffix(it)}"
|
||||||
val content = AnalyzeUrl(book.bookUrl, source = bookSource).getStrResponse().body
|
val isSupportedFile = AppPattern.bookFileRegex.matches(mFileName)
|
||||||
val analyzeRule = AnalyzeRule(book, bookSource)
|
WebFile(it, mFileName, isSupportedFile)
|
||||||
analyzeRule.setContent(content).setBaseUrl(book.bookUrl)
|
|
||||||
analyzeRule.getStringList(ruleDownloadUrls, isUrl = true)?.let {
|
|
||||||
parseDownloadUrls(it, fileName)
|
|
||||||
} ?: throw NoStackTraceException("Unexpected ruleDownloadUrls")
|
|
||||||
} else {
|
|
||||||
parseDownloadUrls(book.downloadUrls, fileName)
|
|
||||||
}
|
}
|
||||||
}.onError {
|
}.onError {
|
||||||
context.toastOnUi("LoadWebFileError\n${it.localizedMessage}")
|
context.toastOnUi("LoadWebFileError\n${it.localizedMessage}")
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
webFileData.postValue(it)
|
webFiles.addAll(it)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseDownloadUrls(
|
|
||||||
downloadUrls: List<String>?,
|
|
||||||
fileName: String
|
|
||||||
): List<WebFile>? {
|
|
||||||
val urls = downloadUrls
|
|
||||||
return urls?.map {
|
|
||||||
val mFileName = "${fileName}.${LocalBook.parseFileSuffix(it)}"
|
|
||||||
val isSupportedFile = AppPattern.bookFileRegex.matches(mFileName)
|
|
||||||
WebFile(it, mFileName, isSupportedFile)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> importOrDownloadWebFile(webFile: WebFile, success: ((T) -> Unit)?) {
|
fun <T> importOrDownloadWebFile(webFile: WebFile, success: ((T) -> Unit)?) {
|
||||||
bookSource ?: return
|
bookSource ?: return
|
||||||
execute {
|
execute {
|
||||||
callBack?.run {
|
waitDialogData.postValue(true)
|
||||||
runOnUI {
|
|
||||||
onWebFileProcessStart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (webFile.isSupported) {
|
if (webFile.isSupported) {
|
||||||
val book = LocalBook.importFileOnLine(webFile.url, webFile.name, bookSource)
|
val book = LocalBook.importFileOnLine(webFile.url, webFile.name, bookSource)
|
||||||
changeToLocalBook(book)
|
changeToLocalBook(book)
|
||||||
@ -291,12 +267,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
}.onError {
|
}.onError {
|
||||||
context.toastOnUi("ImportWebFileError\n${it.localizedMessage}")
|
context.toastOnUi("ImportWebFileError\n${it.localizedMessage}")
|
||||||
}.onFinally {
|
}.onFinally {
|
||||||
callBack?.run {
|
waitDialogData.postValue(false)
|
||||||
runOnUI {
|
|
||||||
onWebFileProcessFinally()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,9 +404,4 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CallBack {
|
|
||||||
fun onWebFileProcessStart()
|
|
||||||
fun onWebFileProcessFinally()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,7 @@ import java.net.URLDecoder
|
|||||||
/**
|
/**
|
||||||
* rss阅读界面
|
* rss阅读界面
|
||||||
*/
|
*/
|
||||||
class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>(false),
|
class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>(false) {
|
||||||
ReadRssViewModel.CallBack {
|
|
||||||
|
|
||||||
override val binding by viewBinding(ActivityRssReadBinding::inflate)
|
override val binding by viewBinding(ActivityRssReadBinding::inflate)
|
||||||
override val viewModel by viewModels<ReadRssViewModel>()
|
override val viewModel by viewModels<ReadRssViewModel>()
|
||||||
@ -50,7 +49,8 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
viewModel.callBack = this
|
viewModel.upStarMenuData.observe(this) { upStarMenu() }
|
||||||
|
viewModel.upTtsMenuData.observe(this) { upTtsMenu(it) }
|
||||||
binding.titleBar.title = intent.getStringExtra("title")
|
binding.titleBar.title = intent.getStringExtra("title")
|
||||||
initWebView()
|
initWebView()
|
||||||
initLiveData()
|
initLiveData()
|
||||||
@ -223,7 +223,7 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun upStarMenu() {
|
private fun upStarMenu() {
|
||||||
starMenuItem?.isVisible = viewModel.rssArticle != null
|
starMenuItem?.isVisible = viewModel.rssArticle != null
|
||||||
if (viewModel.rssStar != null) {
|
if (viewModel.rssStar != null) {
|
||||||
starMenuItem?.setIcon(R.drawable.ic_star)
|
starMenuItem?.setIcon(R.drawable.ic_star)
|
||||||
@ -235,7 +235,7 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
|
|||||||
starMenuItem?.icon?.setTintMutate(primaryTextColor)
|
starMenuItem?.icon?.setTintMutate(primaryTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun upTtsMenu(isPlaying: Boolean) {
|
private fun upTtsMenu(isPlaying: Boolean) {
|
||||||
launch {
|
launch {
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
ttsMenuItem?.setIcon(R.drawable.ic_stop_black_24dp)
|
ttsMenuItem?.setIcon(R.drawable.ic_stop_black_24dp)
|
||||||
|
@ -27,13 +27,14 @@ import java.util.*
|
|||||||
|
|
||||||
|
|
||||||
class ReadRssViewModel(application: Application) : BaseViewModel(application) {
|
class ReadRssViewModel(application: Application) : BaseViewModel(application) {
|
||||||
var callBack: CallBack? = null
|
|
||||||
var rssSource: RssSource? = null
|
var rssSource: RssSource? = null
|
||||||
var rssArticle: RssArticle? = null
|
var rssArticle: RssArticle? = null
|
||||||
var tts: TTS? = null
|
var tts: TTS? = null
|
||||||
val contentLiveData = MutableLiveData<String>()
|
val contentLiveData = MutableLiveData<String>()
|
||||||
val urlLiveData = MutableLiveData<AnalyzeUrl>()
|
val urlLiveData = MutableLiveData<AnalyzeUrl>()
|
||||||
var rssStar: RssStar? = null
|
var rssStar: RssStar? = null
|
||||||
|
val upTtsMenuData = MutableLiveData<Boolean>()
|
||||||
|
val upStarMenuData = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
fun initData(intent: Intent) {
|
fun initData(intent: Intent) {
|
||||||
execute {
|
execute {
|
||||||
@ -72,7 +73,7 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onFinally {
|
}.onFinally {
|
||||||
callBack?.upStarMenu()
|
upStarMenuData.postValue(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
rssStar = it
|
rssStar = it
|
||||||
}
|
}
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
callBack?.upStarMenu()
|
upStarMenuData.postValue(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,11 +197,11 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
tts = TTS().apply {
|
tts = TTS().apply {
|
||||||
setSpeakStateListener(object : TTS.SpeakStateListener {
|
setSpeakStateListener(object : TTS.SpeakStateListener {
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
callBack?.upTtsMenu(true)
|
upTtsMenuData.postValue(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDone() {
|
override fun onDone() {
|
||||||
callBack?.upTtsMenu(false)
|
upTtsMenuData.postValue(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -213,8 +214,4 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
tts?.clearTts()
|
tts?.clearTts()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CallBack {
|
|
||||||
fun upStarMenu()
|
|
||||||
fun upTtsMenu(isPlaying: Boolean)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -11,8 +11,8 @@ fun CookieManager.removeCookie(domain: String) {
|
|||||||
"https://$domain"
|
"https://$domain"
|
||||||
)
|
)
|
||||||
urls.forEach { url ->
|
urls.forEach { url ->
|
||||||
val cookieGlob = cm.getCookie(url)
|
val cookieGlob: String? = cm.getCookie(url)
|
||||||
cookieGlob.splitNotBlank(";").forEach {
|
cookieGlob?.splitNotBlank(";")?.forEach {
|
||||||
val cookieName = it.substringBefore("=")
|
val cookieName = it.substringBefore("=")
|
||||||
cm.setCookie(url, "$cookieName=; Expires=Wed, 31 Dec 2000 23:59:59 GMT")
|
cm.setCookie(url, "$cookieName=; Expires=Wed, 31 Dec 2000 23:59:59 GMT")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user