-
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/web/bookshelf.js b/app/src/main/assets/web/bookshelf.js
index c182f49fa..7aa35df0f 100644
--- a/app/src/main/assets/web/bookshelf.js
+++ b/app/src/main/assets/web/bookshelf.js
@@ -1,11 +1,14 @@
var $ = document.querySelector.bind(document)
, $$ = document.querySelectorAll.bind(document)
, $c = document.createElement.bind(document)
- , randomImg = "http://acg.bakayun.cn/randbg.php?t=dfzh"
+ , randomImg = "http://api.mtyqx.cn/api/random.php"
, randomImg2 = "http://img.xjh.me/random_img.php"
, books
;
+var now_chapter = -1;
+var sum_chapter = 0;
+
var formatTime = value => {
return new Date(value).toLocaleString('zh-CN', {
hour12: false, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"
@@ -13,13 +16,13 @@ var formatTime = value => {
};
var apiMap = {
- getBookshelf: "/getBookshelf",
- getChapterList: "/getChapterList",
- getBookContent: "/getBookContent",
- saveBook: "/saveBook"
+ "getBookshelf": "/getBookshelf",
+ "getChapterList": "/getChapterList",
+ "getBookContent": "/getBookContent",
+ "saveBook": "/saveBook"
};
-var apiAddress = (apiName, url) => {
+var apiAddress = (apiName, url, index) => {
let address = $('#address').value || window.location.host;
if (!(/^http|^\/\//).test(address)) {
address = "//" + address;
@@ -28,6 +31,9 @@ var apiAddress = (apiName, url) => {
address += ":1122";
}
localStorage.setItem('address', address);
+ if (apiName == "getBookContent") {
+ return address + apiMap[apiName] + (url ? "?url=" + encodeURIComponent(url) : "") + "&index=" + index;
+ }
return address + apiMap[apiName] + (url ? "?url=" + encodeURIComponent(url) : "");
};
@@ -41,12 +47,12 @@ var init = () => {
alert(getBookshelf.errorMsg);
return;
}
- books = data.data.sort((book1, book2) => book1.serialNumber - book2.serialNumber);
- books.forEach(book => {
+ books = data.data;
+ books.forEach((book, i) => {
let bookDiv = $c("div");
let img = $c("img");
img.src = book.coverUrl || randomImg;
- img.setAttribute("data-series-num", book.serialNumber);
+ img.setAttribute("data-series-num", i);
bookDiv.appendChild(img);
bookDiv.innerHTML += `
书名: | ${book.name} |
@@ -59,6 +65,8 @@ var init = () => {
});
$$('#books img').forEach(bookImg =>
bookImg.addEventListener("click", () => {
+ now_chapter = -1;
+ sum_chapter = 0;
$('#allcontent').classList.add("read");
var book = books[bookImg.getAttribute("data-series-num")];
$("#info").innerHTML = `
@@ -85,11 +93,13 @@ var init = () => {
data.data.forEach(chapter => {
let ch = $c("button");
- ch.setAttribute("data-url", chapter.durChapterUrl);
- ch.setAttribute("title", chapter.durChapterName);
- ch.innerHTML = chapter.durChapterName.length > 15 ? chapter.durChapterName.substring(0, 14) + "..." : chapter.durChapterName;
+ ch.setAttribute("data-url", chapter.bookUrl);
+ ch.setAttribute("data-index", chapter.index);
+ ch.setAttribute("title", chapter.title);
+ ch.innerHTML = chapter.title.length > 15 ? chapter.title.substring(0, 14) + "..." : chapter.title;
$("#chapter").appendChild(ch);
});
+ sum_chapter = data.data.length;
$('#chapter').scrollTop = 0;
$("#content").innerHTML = "章节列表加载完成!";
});
@@ -126,15 +136,47 @@ $('#showchapter').addEventListener("click", () => {
window.location.hash = "#chapter";
});
+$('#up').addEventListener('click', e => {
+ if (now_chapter > 0) {
+ now_chapter--;
+ let clickEvent = document.createEvent('MouseEvents');
+ clickEvent.initEvent("click", true, false);
+ $('[data-index="' + now_chapter + '"]').dispatchEvent(clickEvent);
+ } else if (now_chapter == 0) {
+ alert("已经是第一章了^_^!")
+ } else {
+
+ }
+});
+
+$('#down').addEventListener('click', e => {
+ if (now_chapter > -1) {
+ if (now_chapter < sum_chapter - 1) {
+ now_chapter++;
+ let clickEvent = document.createEvent('MouseEvents');
+ clickEvent.initEvent("click", true, false);
+ $('[data-index="' + now_chapter + '"]').dispatchEvent(clickEvent);
+
+ } else {
+ alert("已经是最后一章了^_^!")
+ }
+ }
+});
+
$('#chapter').addEventListener("click", (e) => {
if (e.target.tagName === "BUTTON") {
var url = e.target.getAttribute("data-url");
+ var index = e.target.getAttribute("data-index");
var name = e.target.getAttribute("title");
if (!url) {
- alert("未取得章节地址");
+ alert("未取得书籍地址");
+ }
+ if (!index && (0 != index)) {
+ alert("未取得章节索引");
}
+ now_chapter = parseInt(index);
$("#content").innerHTML = "" + name + " 加载中...
";
- fetch(apiAddress("getBookContent", url), { mode: "cors" })
+ fetch(apiAddress("getBookContent", url, index), { mode: "cors" })
.then(res => res.json())
.then(data => {
if (!data.isSuccess) {
diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt
index d1a3e9464..7cb9eff1f 100644
--- a/app/src/main/java/io/legado/app/App.kt
+++ b/app/src/main/java/io/legado/app/App.kt
@@ -1,13 +1,11 @@
package io.legado.app
-import android.app.Activity
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.res.Configuration
import android.os.Build
-import android.os.Bundle
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatDelegate
import com.jeremyliao.liveeventbus.LiveEventBus
@@ -56,7 +54,7 @@ class App : Application() {
.lifecycleObserverAlwaysActive(true)
.autoClear(false)
- registerActivityLife()
+ registerActivityLifecycleCallbacks(ActivityHelp)
}
override fun onConfigurationChanged(newConfig: Configuration) {
@@ -116,9 +114,7 @@ class App : Application() {
*/
@RequiresApi(Build.VERSION_CODES.O)
private fun createChannelId() {
- val notificationManager =
- getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
- notificationManager?.let {
+ (getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager)?.let {
//用唯一的ID创建渠道对象
val downloadChannel = NotificationChannel(
channelIdDownload,
@@ -157,32 +153,4 @@ class App : Application() {
}
}
- private fun registerActivityLife() {
- registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
- override fun onActivityPaused(activity: Activity) {
- }
-
- override fun onActivityResumed(activity: Activity) {
- }
-
- override fun onActivityStarted(activity: Activity) {
-
- }
-
- override fun onActivityDestroyed(activity: Activity) {
- ActivityHelp.remove(activity)
- }
-
- override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) {
- }
-
- override fun onActivityStopped(activity: Activity) {
- }
-
- override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
- ActivityHelp.add(activity)
- }
-
- })
- }
}
diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt
index 96cc7567f..810e6deb2 100644
--- a/app/src/main/java/io/legado/app/constant/AppConst.kt
+++ b/app/src/main/java/io/legado/app/constant/AppConst.kt
@@ -4,8 +4,6 @@ import android.annotation.SuppressLint
import io.legado.app.App
import io.legado.app.R
import io.legado.app.data.entities.BookGroup
-import io.legado.app.utils.getPrefBoolean
-import io.legado.app.utils.putPrefBoolean
import java.text.SimpleDateFormat
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
@@ -29,14 +27,18 @@ object AppConst {
ScriptEngineManager().getEngineByName("rhino")
}
- val TIME_FORMAT: SimpleDateFormat by lazy {
+ val timeFormat: SimpleDateFormat by lazy {
SimpleDateFormat("HH:mm")
}
- val DATE_FORMAT: SimpleDateFormat by lazy {
+ val dateFormat: SimpleDateFormat by lazy {
SimpleDateFormat("yyyy/MM/dd HH:mm")
}
+ val fileNameFormat: SimpleDateFormat by lazy {
+ SimpleDateFormat("yy-MM-dd-HH-mm-ss")
+ }
+
val keyboardToolChars: List by lazy {
arrayListOf(
"@", "&", "|", "%", "/", ":", "[", "]", "{", "}", "<", ">", "\\", "$", "#", "!", ".",
@@ -48,18 +50,6 @@ object AppConst {
val bookGroupLocal = BookGroup(-2, App.INSTANCE.getString(R.string.local))
val bookGroupAudio = BookGroup(-3, App.INSTANCE.getString(R.string.audio))
- var bookGroupLocalShow: Boolean
- get() = App.INSTANCE.getPrefBoolean("bookGroupLocal", false)
- set(value) {
- App.INSTANCE.putPrefBoolean("bookGroupLocal", value)
- }
-
- var bookGroupAudioShow: Boolean
- get() = App.INSTANCE.getPrefBoolean("bookGroupAudio", false)
- set(value) {
- App.INSTANCE.putPrefBoolean("bookGroupAudio", value)
- }
-
const val notificationIdRead = 1144771
const val notificationIdAudio = 1144772
const val notificationIdWeb = 1144773
diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt
index f25d76be5..b323a2711 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt
@@ -27,6 +27,9 @@ interface BookDao {
@Query("SELECT * FROM books WHERE (`group` & :group) > 0")
fun observeByGroup(group: Int): LiveData>
+ @Query("select * from books where (SELECT sum(groupId) FROM book_groups) & `group` = 0")
+ fun observeNoGroup(): LiveData>
+
@Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'")
fun liveDataSearch(key: String): LiveData>
diff --git a/app/src/main/java/io/legado/app/help/ActivityHelp.kt b/app/src/main/java/io/legado/app/help/ActivityHelp.kt
index 6138c0c7e..b14c76670 100644
--- a/app/src/main/java/io/legado/app/help/ActivityHelp.kt
+++ b/app/src/main/java/io/legado/app/help/ActivityHelp.kt
@@ -1,13 +1,15 @@
package io.legado.app.help
import android.app.Activity
+import android.app.Application
+import android.os.Bundle
import java.lang.ref.WeakReference
import java.util.*
/**
* Activity管理器,管理项目中Activity的状态
*/
-object ActivityHelp {
+object ActivityHelp : Application.ActivityLifecycleCallbacks {
private val activities: MutableList> = arrayListOf()
@@ -86,4 +88,27 @@ object ActivityHelp {
}
}
+ override fun onActivityPaused(activity: Activity) {
+ }
+
+ override fun onActivityResumed(activity: Activity) {
+ }
+
+ override fun onActivityStarted(activity: Activity) {
+
+ }
+
+ override fun onActivityDestroyed(activity: Activity) {
+ remove(activity)
+ }
+
+ override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) {
+ }
+
+ override fun onActivityStopped(activity: Activity) {
+ }
+
+ override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
+ add(activity)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt
index f71652c23..6eef2bfa0 100644
--- a/app/src/main/java/io/legado/app/help/AppConfig.kt
+++ b/app/src/main/java/io/legado/app/help/AppConfig.kt
@@ -92,4 +92,23 @@ object AppConfig {
set(value) {
App.INSTANCE.putPrefInt(PreferKey.systemTypefaces, value)
}
+
+ var bookGroupAllShow: Boolean
+ get() = App.INSTANCE.getPrefBoolean("bookGroupAll", true)
+ set(value) {
+ App.INSTANCE.putPrefBoolean("bookGroupAll", value)
+ }
+
+ var bookGroupLocalShow: Boolean
+ get() = App.INSTANCE.getPrefBoolean("bookGroupLocal", false)
+ set(value) {
+ App.INSTANCE.putPrefBoolean("bookGroupLocal", value)
+ }
+
+ var bookGroupAudioShow: Boolean
+ get() = App.INSTANCE.getPrefBoolean("bookGroupAudio", false)
+ set(value) {
+ App.INSTANCE.putPrefBoolean("bookGroupAudio", value)
+ }
+
}
diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt
index 7cfd763a1..493ede9c3 100644
--- a/app/src/main/java/io/legado/app/help/JsExtensions.kt
+++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt
@@ -2,7 +2,7 @@ package io.legado.app.help
import android.util.Base64
import androidx.annotation.Keep
-import io.legado.app.constant.AppConst.DATE_FORMAT
+import io.legado.app.constant.AppConst.dateFormat
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.EncoderUtils
import io.legado.app.utils.MD5Utils
@@ -50,6 +50,6 @@ interface JsExtensions {
}
fun timeFormat(time: Long): String {
- return DATE_FORMAT.format(Date(time))
+ return dateFormat.format(Date(time))
}
}
diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
index b1a17905f..d0e67e901 100644
--- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
+++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
@@ -251,6 +251,12 @@ object ReadBookConfig {
if (shareLayout) shareConfig.footerPaddingTop = value
else durConfig.footerPaddingTop = value
+ var showHeaderLine: Boolean
+ get() = if (shareLayout) shareConfig.showHeaderLine else durConfig.showHeaderLine
+ set(value) =
+ if (shareLayout) shareConfig.showHeaderLine = value
+ else durConfig.showHeaderLine = value
+
var showFooterLine: Boolean
get() = if (shareLayout) shareConfig.showFooterLine else durConfig.showFooterLine
set(value) =
@@ -288,6 +294,7 @@ object ReadBookConfig {
var footerPaddingLeft: Int = 16,
var footerPaddingRight: Int = 16,
var footerPaddingTop: Int = 6,
+ var showHeaderLine: Boolean = false,
var showFooterLine: Boolean = true
) {
fun setBg(bgType: Int, bg: String) {
diff --git a/app/src/main/java/io/legado/app/help/http/HttpHelper.kt b/app/src/main/java/io/legado/app/help/http/HttpHelper.kt
index a37e053fb..df417e2f9 100644
--- a/app/src/main/java/io/legado/app/help/http/HttpHelper.kt
+++ b/app/src/main/java/io/legado/app/help/http/HttpHelper.kt
@@ -57,6 +57,16 @@ object HttpHelper {
return null
}
+ suspend fun simpleGetByteAsync(url: String): ByteArray? {
+ NetworkUtils.getBaseUrl(url)?.let { baseUrl ->
+ return getByteRetrofit(baseUrl)
+ .create(HttpGetApi::class.java)
+ .getMapByteAsync(url, mapOf(), mapOf())
+ .body()
+ }
+ return null
+ }
+
inline fun getApiService(baseUrl: String, encode: String? = null): T {
return getRetrofit(baseUrl, encode).create(T::class.java)
}
diff --git a/app/src/main/java/io/legado/app/model/WebBook.kt b/app/src/main/java/io/legado/app/model/WebBook.kt
index 5a51b486c..69dfb943c 100644
--- a/app/src/main/java/io/legado/app/model/WebBook.kt
+++ b/app/src/main/java/io/legado/app/model/WebBook.kt
@@ -84,8 +84,8 @@ class WebBook(val bookSource: BookSource) {
scope: CoroutineScope = Coroutine.DEFAULT,
context: CoroutineContext = Dispatchers.IO
): Coroutine {
- book.type = bookSource.bookSourceType
return Coroutine.async(scope, context) {
+ book.type = bookSource.bookSourceType
val body =
if (!book.infoHtml.isNullOrEmpty()) {
book.infoHtml
@@ -111,8 +111,8 @@ class WebBook(val bookSource: BookSource) {
scope: CoroutineScope = Coroutine.DEFAULT,
context: CoroutineContext = Dispatchers.IO
): Coroutine> {
- book.type = bookSource.bookSourceType
return Coroutine.async(scope, context) {
+ book.type = bookSource.bookSourceType
val body =
if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) {
book.tocHtml
@@ -139,36 +139,50 @@ class WebBook(val bookSource: BookSource) {
context: CoroutineContext = Dispatchers.IO
): Coroutine {
return Coroutine.async(scope, context) {
- if (bookSource.getContentRule().content.isNullOrEmpty()) {
- Debug.log(sourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}")
- return@async bookChapter.url
- }
- val body =
- if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) {
- book.tocHtml
- } else {
- val analyzeUrl =
- AnalyzeUrl(
- book = book,
- ruleUrl = bookChapter.url,
- baseUrl = book.tocUrl,
- headerMapF = bookSource.getHeaderMap()
- )
- analyzeUrl.getResponseAwait(
- bookSource.bookSourceUrl,
- jsStr = bookSource.getContentRule().webJs,
- sourceRegex = bookSource.getContentRule().sourceRegex
- ).body
- }
- BookContent.analyzeContent(
- this,
- body,
- book,
- bookChapter,
- bookSource,
- bookChapter.url,
- nextChapterUrl
+ getContentSuspend(
+ book, bookChapter, nextChapterUrl, scope
)
}
}
+
+ /**
+ * 章节内容
+ */
+ suspend fun getContentSuspend(
+ book: Book,
+ bookChapter: BookChapter,
+ nextChapterUrl: String? = null,
+ scope: CoroutineScope = Coroutine.DEFAULT
+ ): String {
+ if (bookSource.getContentRule().content.isNullOrEmpty()) {
+ Debug.log(sourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}")
+ return bookChapter.url
+ }
+ val body =
+ if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) {
+ book.tocHtml
+ } else {
+ val analyzeUrl =
+ AnalyzeUrl(
+ book = book,
+ ruleUrl = bookChapter.url,
+ baseUrl = book.tocUrl,
+ headerMapF = bookSource.getHeaderMap()
+ )
+ analyzeUrl.getResponseAwait(
+ bookSource.bookSourceUrl,
+ jsStr = bookSource.getContentRule().webJs,
+ sourceRegex = bookSource.getContentRule().sourceRegex
+ ).body
+ }
+ return BookContent.analyzeContent(
+ scope,
+ body,
+ book,
+ bookChapter,
+ bookSource,
+ bookChapter.url,
+ nextChapterUrl
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt
index b5b983476..732506449 100644
--- a/app/src/main/java/io/legado/app/service/DownloadService.kt
+++ b/app/src/main/java/io/legado/app/service/DownloadService.kt
@@ -29,8 +29,10 @@ class DownloadService : BaseService() {
private val handler = Handler()
private var runnable: Runnable = Runnable { upDownload() }
private val downloadMap = hashMapOf>()
+ private val downloadCount = hashMapOf();
private val finalMap = hashMapOf>()
private var notificationContent = "正在启动下载"
+
private val notificationBuilder by lazy {
val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload)
.setSmallIcon(R.drawable.ic_download)
@@ -97,6 +99,11 @@ class DownloadService : BaseService() {
finalMap.remove(bookUrl)
}
+ private fun updateNotification(downloadCount:DownloadCount, totalCount: Int, content: String){
+ notificationContent =
+ "进度:${downloadCount.downloadFinishedCount}/$totalCount,成功:${downloadCount.successCount},$content"
+ }
+
private fun download() {
val task = Coroutine.async(this, context = searchPool) {
downloadMap.forEach { entry ->
@@ -106,6 +113,9 @@ class DownloadService : BaseService() {
val bookSource =
App.db.bookSourceDao().getBookSource(book.origin) ?: return@async
val webBook = WebBook(bookSource)
+
+ downloadCount[entry.key] = DownloadCount()
+
entry.value.forEach { chapter ->
if (!isActive) return@async
if (downloadMap.containsKey(book.bookUrl)) {
@@ -116,16 +126,19 @@ class DownloadService : BaseService() {
scope = this,
context = searchPool
)
- .onStart {
- notificationContent = chapter.title
- }
+ //.onStart {
+ // notificationContent = "启动:" + chapter.title
+ //}
.onSuccess(IO) { content ->
content?.let {
+ downloadCount[entry.key]?.increaseSuccess()
BookHelp.saveContent(book, chapter, content)
}
}
.onFinally(IO) {
synchronized(this@DownloadService) {
+ downloadCount[entry.key]?.increaseFinished()
+ downloadCount[entry.key]?.let { updateNotification(it, entry.value.size, chapter.title) }
val chapterMap =
finalMap[book.bookUrl]
?: linkedSetOf().apply {
@@ -135,9 +148,14 @@ class DownloadService : BaseService() {
if (chapterMap.size == entry.value.size) {
downloadMap.remove(book.bookUrl)
finalMap.remove(book.bookUrl)
+ downloadCount.remove(entry.key)
}
}
}
+ } else{
+ //无需下载的,设置为增加成功
+ downloadCount[entry.key]?.increaseSuccess()
+ downloadCount[entry.key]?.increaseFinished()
}
}
}
@@ -176,4 +194,17 @@ class DownloadService : BaseService() {
val notification = builder.build()
startForeground(AppConst.notificationIdDownload, notification)
}
+}
+
+class DownloadCount{
+ @Volatile public var downloadFinishedCount = 0 // 下载完成的条目数量
+ @Volatile public var successCount = 0 //下载成功的条目数量
+
+ fun increaseSuccess(){
+ ++successCount;
+ }
+
+ fun increaseFinished(){
+ ++downloadFinishedCount;
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
index 8fc7d1294..d86cb39a1 100644
--- a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
@@ -1,13 +1,19 @@
package io.legado.app.ui.about
import android.os.Bundle
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.style.ForegroundColorSpan
import android.view.Menu
import android.view.MenuItem
import io.legado.app.R
import io.legado.app.base.BaseActivity
+import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.openUrl
+import kotlinx.android.synthetic.main.activity_about.*
import org.jetbrains.anko.share
+
class AboutActivity : BaseActivity(R.layout.activity_about) {
override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -17,6 +23,16 @@ class AboutActivity : BaseActivity(R.layout.activity_about) {
supportFragmentManager.beginTransaction()
.replace(R.id.fl_fragment, aboutFragment, fTag)
.commit()
+ tv_app_summary.post {
+ val span = ForegroundColorSpan(accentColor)
+ val spannableString = SpannableString(tv_app_summary.text)
+ val start = spannableString.indexOf("开源阅读软件")
+ spannableString.setSpan(
+ span, start, start + 6,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ tv_app_summary.text = spannableString
+ }
}
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
diff --git a/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt b/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
index 0270b3f77..f82fa1b64 100644
--- a/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
@@ -2,7 +2,6 @@ package io.legado.app.ui.about
import android.content.Context
import android.content.Intent
-import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.preference.Preference
@@ -12,7 +11,6 @@ import io.legado.app.utils.ACache
import io.legado.app.utils.openUrl
import io.legado.app.utils.sendToClip
import org.jetbrains.anko.longToast
-import java.net.URLEncoder
class DonateFragment : PreferenceFragmentCompat() {
@@ -36,7 +34,6 @@ class DonateFragment : PreferenceFragmentCompat() {
"zfbHbRwm" -> requireContext().openUrl(zfbHbRwmUrl)
"zfbSkRwm" -> requireContext().openUrl(zfbSkRwmUrl)
"qqSkRwm" -> requireContext().openUrl(qqSkRwmUrl)
- "zfbSk" -> aliDonate(requireContext())
"zfbHbSsm" -> getZfbHb(requireContext())
"gzGzh" -> requireContext().sendToClip("开源阅读软件")
}
@@ -59,19 +56,4 @@ class DonateFragment : PreferenceFragmentCompat() {
}
}
- private fun aliDonate(context: Context) {
- try {
- val qrCode = URLEncoder.encode(
- "https://qr.alipay.com/tsx06677nwdk3javroq4ef0?_s=Dweb-other",
- "utf-8"
- )
- val aliPayQr =
- "alipayqr://platformapi/startapp?saId=10000007&qrcode=$qrCode&_t=${System.currentTimeMillis()}"
- val intent = Intent(Intent.ACTION_VIEW, Uri.parse(aliPayQr))
- context.startActivity(intent)
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
-
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
index f03cc1df5..f3b0d5ee5 100644
--- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
@@ -100,6 +100,7 @@ class ArrangeBookActivity : VMBaseActivity(R.layout.activi
-1 -> App.db.bookDao().observeAll()
-2 -> App.db.bookDao().observeLocal()
-3 -> App.db.bookDao().observeAudio()
+ -11 -> App.db.bookDao().observeNoGroup()
else -> App.db.bookDao().observeByGroup(groupId)
}
booksLiveData?.observe(this, Observer {
@@ -112,6 +113,11 @@ class ArrangeBookActivity : VMBaseActivity(R.layout.activi
when (item.itemId) {
R.id.menu_group_manage -> GroupManageDialog()
.show(supportFragmentManager, "groupManage")
+ R.id.menu_no_group -> {
+ title_bar.subtitle = getString(R.string.no_group)
+ groupId = -11
+ initBookData()
+ }
R.id.menu_all -> {
title_bar.subtitle = item.title
groupId = -1
diff --git a/app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt b/app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt
index f731806a7..fec1ed537 100644
--- a/app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt
@@ -127,7 +127,7 @@ class DownloadActivity : VMBaseActivity(R.layout.activity_dow
FilePicker.selectFolder(this, exportRequestCode) {
val path = ACache.get(this@DownloadActivity).getAsString(exportBookPathKey)
if (path.isNullOrEmpty()) {
- toast("没有默认路径")
+ toast(R.string.no_default_path)
} else {
startExport(path)
}
diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
index 1a4322da6..ea4d2b61e 100644
--- a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
@@ -20,9 +20,9 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
-import io.legado.app.constant.AppConst
import io.legado.app.constant.Theme
import io.legado.app.data.entities.BookGroup
+import io.legado.app.help.AppConfig
import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
@@ -42,7 +42,7 @@ import kotlin.collections.ArrayList
class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
private lateinit var viewModel: GroupViewModel
private lateinit var adapter: GroupAdapter
- private var callBack: CallBack? = null
+ private val callBack: CallBack? get() = parentFragment as? CallBack
override fun onStart() {
super.onStart()
@@ -62,19 +62,12 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- callBack = parentFragment as? CallBack
+ tool_bar.title = getString(R.string.group_manage)
initData()
+ initMenu()
}
private fun initData() {
- tool_bar.title = getString(R.string.group_manage)
- tool_bar.inflateMenu(R.menu.book_group_manage)
- tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
- tool_bar.setOnMenuItemClickListener(this)
- tool_bar.menu.findItem(R.id.menu_group_local)
- .isChecked = AppConst.bookGroupLocalShow
- tool_bar.menu.findItem(R.id.menu_group_audio)
- .isChecked = AppConst.bookGroupAudioShow
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
@@ -90,17 +83,36 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
}
+ private fun initMenu() {
+ tool_bar.setOnMenuItemClickListener(this)
+ tool_bar.inflateMenu(R.menu.book_group_manage)
+ tool_bar.menu.let {
+ it.applyTint(requireContext(), Theme.getTheme())
+ it.findItem(R.id.menu_group_all)
+ .isChecked = AppConfig.bookGroupAllShow
+ it.findItem(R.id.menu_group_local)
+ .isChecked = AppConfig.bookGroupLocalShow
+ it.findItem(R.id.menu_group_audio)
+ .isChecked = AppConfig.bookGroupAudioShow
+ }
+ }
+
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_add -> addGroup()
+ R.id.menu_group_all -> {
+ item.isChecked = !item.isChecked
+ AppConfig.bookGroupAllShow = item.isChecked
+ callBack?.upGroup()
+ }
R.id.menu_group_local -> {
item.isChecked = !item.isChecked
- AppConst.bookGroupLocalShow = item.isChecked
+ AppConfig.bookGroupLocalShow = item.isChecked
callBack?.upGroup()
}
R.id.menu_group_audio -> {
item.isChecked = !item.isChecked
- AppConst.bookGroupAudioShow = item.isChecked
+ AppConfig.bookGroupAudioShow = item.isChecked
callBack?.upGroup()
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt
index acc3b7265..153356754 100644
--- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt
@@ -87,7 +87,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
ll_brief.visible()
tv_tag.text = item.name.substringAfterLast(".")
tv_size.text = StringUtils.toSize(item.size)
- tv_date.text = AppConst.DATE_FORMAT.format(item.date)
+ tv_date.text = AppConst.dateFormat.format(item.date)
cb_select.isChecked = selectedUris.contains(item.uri.toString())
}
tv_name.text = item.name
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
index de1f1d728..2ca4555a3 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
@@ -479,6 +479,8 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
if (!ReadBook.isLocalBook) {
tv_chapter_url.text = it.url
tv_chapter_url.visible()
+ } else {
+ tv_chapter_url.gone()
}
seek_read_page.max = it.pageSize().minus(1)
seek_read_page.progress = ReadBook.durPageIndex
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
index a19789fd3..ed1c296de 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
@@ -76,7 +76,8 @@ class PaddingConfigDialog : DialogFragment() {
dsb_footer_padding_bottom.progress = footerPaddingBottom
dsb_footer_padding_left.progress = footerPaddingLeft
dsb_footer_padding_right.progress = footerPaddingRight
- cb_show_line.isChecked = showFooterLine
+ cb_show_top_line.isChecked = showHeaderLine
+ cb_show_bottom_line.isChecked = showFooterLine
}
private fun initView() = with(ReadBookConfig) {
@@ -131,7 +132,13 @@ class PaddingConfigDialog : DialogFragment() {
footerPaddingRight = it
postEvent(EventBus.UP_CONFIG, true)
}
- cb_show_line.onCheckedChangeListener = { cb, isChecked ->
+ cb_show_top_line.onCheckedChangeListener = { cb, isChecked ->
+ if (cb.isPressed) {
+ showHeaderLine = isChecked
+ postEvent(EventBus.UP_CONFIG, true)
+ }
+ }
+ cb_show_bottom_line.onCheckedChangeListener = { cb, isChecked ->
if (cb.isPressed) {
showFooterLine = isChecked
postEvent(EventBus.UP_CONFIG, true)
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
index 68a1d11aa..f29c4ddba 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
@@ -108,7 +108,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
textBold = !textBold
tv_text_bold.isSelected = textBold
}
- postEvent(EventBus.UP_CONFIG, false)
+ postEvent(EventBus.UP_CONFIG, true)
}
tv_text_font.onClick {
FontSelectDialog().show(childFragmentManager, "fontSelectDialog")
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
index 81e483c75..65121906c 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
@@ -270,8 +270,8 @@ object ChapterProvider {
contentPaint.isAntiAlias = true
contentPaint.color = ReadBookConfig.durConfig.textColor()
contentPaint.letterSpacing = ReadBookConfig.letterSpacing
- val bold = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL
- contentPaint.typeface = Typeface.create(typeface, bold)
+ val style = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL
+ contentPaint.typeface = Typeface.create(typeface, style)
contentPaint.textSize = ReadBookConfig.textSize.sp.toFloat()
//间距
lineSpacingExtra = ReadBookConfig.lineSpacingExtra
@@ -294,5 +294,5 @@ object ChapterProvider {
}
val TextPaint.textHeight: Float
- get() = this.fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading
+ get() = fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
index 8e0bf1d55..a9a752303 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
@@ -7,7 +7,7 @@ import android.view.MotionEvent
import android.widget.FrameLayout
import com.github.houbb.opencc4j.core.impl.ZhConvertBootstrap
import io.legado.app.R
-import io.legado.app.constant.AppConst.TIME_FORMAT
+import io.legado.app.constant.AppConst.timeFormat
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.entities.TextPage
@@ -18,6 +18,8 @@ import java.util.*
class ContentView(context: Context) : FrameLayout(context) {
+ private var battery = 100
+
init {
//设置背景防止切换背景时文字重叠
setBackgroundColor(context.getCompatColor(R.color.background))
@@ -26,8 +28,7 @@ class ContentView(context: Context) : FrameLayout(context) {
upStyle()
upTime()
content_text_view.upView = {
- tv_bottom_left.text = it.title
- setPageIndex(it.index, it.pageSize)
+ setProgress(it)
}
}
@@ -60,6 +61,7 @@ class ContentView(context: Context) : FrameLayout(context) {
footerPaddingRight.dp,
footerPaddingBottom.dp
)
+ vw_top_divider.visible(showHeaderLine)
vw_bottom_divider.visible(showFooterLine)
content_text_view.upVisibleRect()
durConfig.textColor().let {
@@ -67,6 +69,14 @@ class ContentView(context: Context) : FrameLayout(context) {
tv_top_right.setTextColor(it)
tv_bottom_left.setTextColor(it)
tv_bottom_right.setTextColor(it)
+ battery_view.setColor(it)
+ }
+ if (hideStatusBar) {
+ tv_bottom_left.text = timeFormat.format(Date(System.currentTimeMillis()))
+ battery_view.visible()
+ battery_view.setBattery(battery)
+ } else {
+ battery_view.gone()
}
}
}
@@ -85,20 +95,20 @@ class ContentView(context: Context) : FrameLayout(context) {
}
fun upTime() {
- tv_top_left.text = TIME_FORMAT.format(Date(System.currentTimeMillis()))
+ if (ReadBookConfig.hideStatusBar) {
+ tv_bottom_right.text = timeFormat.format(Date(System.currentTimeMillis()))
+ }
}
fun upBattery(battery: Int) {
- tv_top_right.text = context.getString(R.string.battery_show, battery)
+ this.battery = battery
+ if (ReadBookConfig.hideStatusBar) {
+ battery_view.setBattery(battery)
+ }
}
fun setContent(textPage: TextPage) {
- tv_bottom_left.text = when (AppConfig.chineseConverterType) {
- 1 -> ZhConvertBootstrap.newInstance().toSimple(textPage.title)
- 2 -> ZhConvertBootstrap.newInstance().toTraditional(textPage.title)
- else -> textPage.title
- }
- setPageIndex(textPage.index, textPage.pageSize)
+ setProgress(textPage)
content_text_view.resetPageOffset()
content_text_view.setContent(textPage)
}
@@ -108,9 +118,19 @@ class ContentView(context: Context) : FrameLayout(context) {
}
@SuppressLint("SetTextI18n")
- fun setPageIndex(pageIndex: Int?, pageSize: Int) {
- pageIndex?.let {
- tv_bottom_right.text = "${pageIndex.plus(1)}/${pageSize}"
+ fun setProgress(textPage: TextPage) = textPage.apply {
+ val title = when (AppConfig.chineseConverterType) {
+ 1 -> ZhConvertBootstrap.newInstance().toSimple(textPage.title)
+ 2 -> ZhConvertBootstrap.newInstance().toTraditional(textPage.title)
+ else -> textPage.title
+ }
+ if (ReadBookConfig.hideStatusBar) {
+ tv_top_left.text = title
+ tv_top_right.text = readProgress
+ tv_bottom_left.text = "${index.plus(1)}/$pageSize"
+ } else {
+ tv_bottom_left.text = title
+ tv_bottom_right.text = "${index.plus(1)}/$pageSize $readProgress"
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
index f42fcdb5d..bfb8e822c 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
@@ -5,6 +5,7 @@ import android.text.StaticLayout
import io.legado.app.App
import io.legado.app.R
import io.legado.app.ui.book.read.page.ChapterProvider
+import java.text.DecimalFormat
data class TextPage(
var index: Int = 0,
@@ -84,4 +85,20 @@ data class TextPage(
lineStart += textLine.text.length
}
}
+
+ val readProgress: String
+ get() {
+ val df = DecimalFormat("0.0%")
+ if (chapterSize == 0 || pageSize == 0 && chapterIndex == 0) {
+ return "0.0%"
+ } else if (pageSize == 0) {
+ return df.format((chapterIndex + 1.0f) / chapterSize.toDouble())
+ }
+ var percent =
+ df.format(chapterIndex * 1.0f / chapterSize + 1.0f / chapterSize * (index + 1) / pageSize.toDouble())
+ if (percent == "100.0%" && (chapterIndex + 1 != chapterSize || index + 1 != pageSize)) {
+ percent = "99.9%"
+ }
+ return percent
+ }
}
diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
index c86f96dba..2d1054859 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
@@ -283,15 +283,10 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se
*/
@Synchronized
private fun upSearchItems(items: List, isMandatoryUpdate: Boolean) {
- if (!isMandatoryUpdate && System.currentTimeMillis() - refreshTime < 500) {
- return
- }
- refreshTime = System.currentTimeMillis()
- try {
+ if (isMandatoryUpdate || System.currentTimeMillis() - refreshTime > 500) {
+ refreshTime = System.currentTimeMillis()
val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), items))
adapter.setItems(items, diffResult)
- } catch (e: Exception) {
- e.printStackTrace()
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
index eba5950f4..385088640 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
@@ -85,7 +85,12 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity
R.id.menu_group_manage ->
GroupManageDialog().show(supportFragmentManager, "groupManage")
R.id.menu_import_source_local -> FilePicker
- .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json"))
+ .selectFile(
+ this,
+ importRequestCode,
+ type = "text/*",
+ allowExtensions = arrayOf("txt", "json")
+ )
R.id.menu_import_source_onLine -> showImportDialog()
}
if (item.groupId == R.id.source_group) {
diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt
index 9260fc8bd..8f5549709 100644
--- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt
+++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt
@@ -14,8 +14,13 @@ import org.jetbrains.anko.toast
@Suppress("unused")
object FilePicker {
- fun selectFolder(activity: AppCompatActivity, requestCode: Int, default: (() -> Unit)? = null) {
- activity.alert(titleResource = R.string.select_folder) {
+ fun selectFolder(
+ activity: AppCompatActivity,
+ requestCode: Int,
+ title: String = activity.getString(R.string.select_folder),
+ default: (() -> Unit)? = null
+ ) {
+ activity.alert(title = title) {
val selectList =
activity.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
@@ -45,9 +50,14 @@ object FilePicker {
}.show()
}
- fun selectFolder(fragment: Fragment, requestCode: Int, default: (() -> Unit)? = null) {
+ fun selectFolder(
+ fragment: Fragment,
+ requestCode: Int,
+ title: String = fragment.getString(R.string.select_folder),
+ default: (() -> Unit)? = null
+ ) {
fragment.requireContext()
- .alert(titleResource = R.string.select_folder) {
+ .alert(title = title) {
val selectList =
fragment.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
@@ -80,11 +90,12 @@ object FilePicker {
fun selectFile(
activity: BaseActivity,
requestCode: Int,
+ title: String = activity.getString(R.string.select_file),
type: String,
allowExtensions: Array?,
default: (() -> Unit)? = null
) {
- activity.alert(titleResource = R.string.select_file) {
+ activity.alert(title = title) {
val selectList =
activity.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
@@ -119,12 +130,13 @@ object FilePicker {
fun selectFile(
fragment: Fragment,
requestCode: Int,
+ title: String = fragment.getString(R.string.select_file),
type: String,
allowExtensions: Array,
default: (() -> Unit)? = null
) {
fragment.requireContext()
- .alert(titleResource = R.string.select_file) {
+ .alert(title = title) {
val selectList =
fragment.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
index 9cb7dc9de..3608a79ed 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
@@ -17,6 +17,7 @@ import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppConst
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookGroup
+import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
@@ -108,11 +109,13 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
synchronized(this) {
tab_layout.removeOnTabSelectedListener(this)
bookGroups.clear()
- bookGroups.add(AppConst.bookGroupAll)
- if (AppConst.bookGroupLocalShow) {
+ if (AppConfig.bookGroupAllShow) {
+ bookGroups.add(AppConst.bookGroupAll)
+ }
+ if (AppConfig.bookGroupLocalShow) {
bookGroups.add(AppConst.bookGroupLocal)
}
- if (AppConst.bookGroupAudioShow) {
+ if (AppConfig.bookGroupAudioShow) {
bookGroups.add(AppConst.bookGroupAudio)
}
bookGroups.addAll(it)
@@ -134,13 +137,17 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
override fun upGroup() {
synchronized(this) {
+ bookGroups.remove(AppConst.bookGroupAll)
bookGroups.remove(AppConst.bookGroupLocal)
bookGroups.remove(AppConst.bookGroupAudio)
- if (getPrefBoolean("bookGroupAudio", true)) {
- bookGroups.add(1, AppConst.bookGroupAudio)
+ if (AppConfig.bookGroupAudioShow) {
+ bookGroups.add(0, AppConst.bookGroupAudio)
+ }
+ if (AppConfig.bookGroupLocalShow) {
+ bookGroups.add(0, AppConst.bookGroupLocal)
}
- if (getPrefBoolean("bookGroupLocal", true)) {
- bookGroups.add(1, AppConst.bookGroupLocal)
+ if (AppConfig.bookGroupAllShow) {
+ bookGroups.add(0, AppConst.bookGroupAll)
}
bookshelfAdapter.notifyDataSetChanged()
}
@@ -196,13 +203,9 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
}.show().applyTint()
}
- override fun onTabReselected(tab: TabLayout.Tab?) {
-
- }
-
- override fun onTabUnselected(tab: TabLayout.Tab?) {
+ override fun onTabReselected(tab: TabLayout.Tab?) = Unit
- }
+ override fun onTabUnselected(tab: TabLayout.Tab?) = Unit
override fun onTabSelected(tab: TabLayout.Tab?) {
tab?.position?.let {
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
index 16ffee4a1..fd0a591b6 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
@@ -180,7 +180,12 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi
R.id.menu_del_selection -> viewModel.delSelection(adapter.getSelection())
R.id.menu_import_source_onLine -> showImportDialog()
R.id.menu_import_source_local -> FilePicker
- .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json"))
+ .selectFile(
+ this,
+ importRequestCode,
+ type = "text/*",
+ allowExtensions = arrayOf("txt", "json")
+ )
}
return super.onCompatOptionsItemSelected(item)
}
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
index 1764a1f32..79ea599d1 100644
--- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
@@ -1,37 +1,42 @@
package io.legado.app.ui.rss.read
import android.annotation.SuppressLint
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
import android.os.Bundle
-import android.view.KeyEvent
-import android.view.Menu
-import android.view.MenuItem
-import android.webkit.WebResourceRequest
-import android.webkit.WebSettings
-import android.webkit.WebView
-import android.webkit.WebViewClient
+import android.view.*
+import android.webkit.*
+import androidx.core.view.size
import androidx.lifecycle.Observer
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.lib.theme.DrawableUtils
import io.legado.app.lib.theme.primaryTextColor
-import io.legado.app.utils.NetworkUtils
-import io.legado.app.utils.getViewModel
-import io.legado.app.utils.openUrl
+import io.legado.app.ui.filechooser.FileChooserDialog
+import io.legado.app.ui.filechooser.FilePicker
+import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_rss_read.*
import kotlinx.coroutines.launch
import org.apache.commons.text.StringEscapeUtils
import org.jetbrains.anko.share
+import org.jetbrains.anko.toast
import org.jsoup.Jsoup
import org.jsoup.safety.Whitelist
+
class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_read),
+ FileChooserDialog.CallBack,
ReadRssViewModel.CallBack {
override val viewModel: ReadRssViewModel
get() = getViewModel(ReadRssViewModel::class.java)
-
+ private val savePathRequestCode = 132
+ private val imagePathKey = ""
private var starMenuItem: MenuItem? = null
private var ttsMenuItem: MenuItem? = null
+ private var customWebViewCallback: WebChromeClient.CustomViewCallback? = null
+ private var webPic: String? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
viewModel.callBack = this
@@ -41,6 +46,20 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
viewModel.initData(intent)
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ when (newConfig.orientation) {
+ Configuration.ORIENTATION_LANDSCAPE -> {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
+ window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
+ }
+ Configuration.ORIENTATION_PORTRAIT -> {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
+ window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
+ }
+ }
+ }
+
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.rss_read, menu)
return super.onCompatCreateOptionsMenu(menu)
@@ -65,6 +84,20 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
}
private fun initWebView() {
+ web_view.webChromeClient = object : WebChromeClient() {
+ override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
+ ll_view.invisible()
+ custom_web_view.addView(view)
+ customWebViewCallback = callback
+ }
+
+ override fun onHideCustomView() {
+ custom_web_view.removeAllViews()
+ ll_view.visible()
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ }
+ }
web_view.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?,
@@ -95,6 +128,30 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
domStorageEnabled = true
allowContentAccess = true
}
+ web_view.setOnLongClickListener {
+ val hitTestResult = web_view.hitTestResult
+ if (hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
+ hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
+ ) {
+ hitTestResult.extra?.let {
+ webPic = it
+ saveImage()
+ return@setOnLongClickListener true
+ }
+ }
+ return@setOnLongClickListener false
+ }
+ }
+
+ private fun saveImage() {
+ FilePicker.selectFolder(this, savePathRequestCode, getString(R.string.save_image)) {
+ val path = ACache.get(this).getAsString(imagePathKey)
+ if (path.isNullOrEmpty()) {
+ toast(R.string.no_default_path)
+ } else {
+ viewModel.saveImage(webPic, path)
+ }
+ }
}
@SuppressLint("SetJavaScriptEnabled")
@@ -113,7 +170,6 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
url
)//不想用baseUrl进else
} else {
- //webView.loadData(html, "text/html;charset=utf-8", "utf-8")//经测试可以解决中文乱码
web_view.loadDataWithBaseURL(
null,
html,
@@ -175,7 +231,10 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
event?.let {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> if (event.isTracking && !event.isCanceled && web_view.canGoBack()) {
- if (web_view.copyBackForwardList().size > 1) {
+ if (custom_web_view.size > 0) {
+ customWebViewCallback?.onCustomViewHidden()
+ return true
+ } else if (web_view.copyBackForwardList().size > 1) {
web_view.goBack()
return true
}
@@ -203,6 +262,24 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
}
}
+ override fun onFilePicked(requestCode: Int, currentPath: String) {
+ when (requestCode) {
+ savePathRequestCode -> {
+ ACache.get(this).put(imagePathKey, currentPath)
+ viewModel.saveImage(webPic, currentPath)
+ }
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ when (requestCode) {
+ savePathRequestCode -> data?.data?.let {
+ onFilePicked(requestCode, it.toString())
+ }
+ }
+ }
+
override fun onDestroy() {
super.onDestroy()
web_view.destroy()
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
index 17b66f41c..6d90fd181 100644
--- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
@@ -2,19 +2,31 @@ package io.legado.app.ui.rss.read
import android.app.Application
import android.content.Intent
+import android.net.Uri
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
+import android.util.Base64
+import android.webkit.URLUtil
+import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseViewModel
+import io.legado.app.constant.AppConst
import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource
+import io.legado.app.help.http.HttpHelper
import io.legado.app.model.Rss
import io.legado.app.model.analyzeRule.AnalyzeUrl
+import io.legado.app.utils.DocumentUtils
+import io.legado.app.utils.FileUtils
+import io.legado.app.utils.isContentPath
+import io.legado.app.utils.writeBytes
import kotlinx.coroutines.launch
+import java.io.File
import java.util.*
+
class ReadRssViewModel(application: Application) : BaseViewModel(application),
TextToSpeech.OnInitListener {
var callBack: CallBack? = null
@@ -85,6 +97,37 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
}
}
+ fun saveImage(webPic: String?, path: String) {
+ webPic ?: return
+ execute {
+ val fileName = "${AppConst.fileNameFormat.format(Date(System.currentTimeMillis()))}.jpg"
+ webData2bitmap(webPic)?.let { biteArray ->
+ if (path.isContentPath()) {
+ val uri = Uri.parse(path)
+ DocumentFile.fromTreeUri(context, uri)?.let { doc ->
+ DocumentUtils.createFileIfNotExist(doc, fileName)
+ ?.writeBytes(context, biteArray)
+ }
+ } else {
+ val file = FileUtils.createFileIfNotExist(File(path), fileName)
+ file.writeBytes(biteArray)
+ }
+ } ?: throw Throwable("NULL")
+ }.onError {
+ toast("保存图片失败:${it.localizedMessage}")
+ }.onSuccess {
+ toast("保存成功")
+ }
+ }
+
+ private suspend fun webData2bitmap(data: String): ByteArray? {
+ return if (URLUtil.isValidUrl(data)) {
+ HttpHelper.simpleGetByteAsync(data)
+ } else {
+ Base64.decode(data.split(",").toTypedArray()[1], Base64.DEFAULT)
+ }
+ }
+
fun clHtml(content: String): String {
return if (content.contains("