@ -1,4 +1,4 @@ |
||||
<resources> |
||||
<string name="app_name">阅读.debug</string> |
||||
<string name="receiving_shared_label">阅读.debug·搜索</string> |
||||
<string name="app_name">阅读·D</string> |
||||
<string name="receiving_shared_label">阅读·D·搜索</string> |
||||
</resources> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
|
||||
<string name="app_name">閱讀Pro</string> |
||||
|
||||
</resources> |
@ -0,0 +1,6 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
|
||||
<string name="app_name">阅读Pro</string> |
||||
|
||||
</resources> |
@ -0,0 +1,129 @@ |
||||
## 常见问题 |
||||
|
||||
1.为什么第一次安装好之后什么东西都没有? |
||||
* 因为阅读只是一个转码工具,不提供内容,第一次安装app,需要自己手动导入书源,可以从QQ群、公众号“开源阅读软件”、酷安评论里获取由书友制作分享的书源。 |
||||
|
||||
2.如何导入本地书源文件? |
||||
* 下载群文件里的书源文件(书源格式后缀有txt、json,其中json文件某些情况下无法导入,需要修改后缀为txt格式才可导入); |
||||
* 打开阅读软件; |
||||
* 我的 - 点击“书源管理”; |
||||
* 点击右上角选择“本地导入”; |
||||
* 左下角选择书源文件所在的路径; |
||||
* 点击书源文件导入; |
||||
* 导入后返回书源管理界面; |
||||
* 新版qq下载路径:Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/ |
||||
|
||||
3.如何新建大佬发的单独书源? |
||||
* 复制书源代码; |
||||
* 打开阅读软件; |
||||
* 我的 - 点击“书源管理”; |
||||
* 右上角选择“新建书源”; |
||||
* 进入新建书源后点击右上角“粘贴源”; |
||||
* 粘贴书源完成后点击上方保存; |
||||
* 本次新建单独书源操作完成。 |
||||
* 注:如果书源有错误或者复制不全会显示格式错误,请重新复制。 |
||||
|
||||
4.为什么导入2.0书源后看不了书? |
||||
* 2.0部分书源并不适用3.0,建议导入后进行筛选。 |
||||
|
||||
5.阅读2.0数据如何导入阅读3.0? |
||||
* 先对阅读2.0的数据进行备份,然后进入阅读3.0,点击“我的”,选择“备份与恢复”,再点击“导入旧版本数据”。 |
||||
|
||||
6.如何给朋友分享我的书源? |
||||
* 打开阅读软件; |
||||
* 点击备份; |
||||
* 打开手机自带的文件管理; |
||||
* 手机自带内存根目录找到YueDu3.0文件夹; |
||||
* 找到myBookSource.json长按选择分享; |
||||
* 选择微信分享或者QQ分享; |
||||
* 选择你要分享的好友点击发送; |
||||
* 好友接收后在手机自带内存根目录找到myBookSource.json文件(QQ在tencent--QQfile_recv微信在Tencent--MicroMsg--Download); |
||||
* 复制该文件到手机自带内存根目录找到YueDu3.0文件夹(如已有该文件请先删除该文件或者备份到其他地方再复制到文件夹); |
||||
* 打开阅读软件点击恢复。 |
||||
* 注:备份路径如已修改过请在修改后的路径下查找书源文件。 |
||||
|
||||
7.目前阅读支持哪些格式的本地书籍? |
||||
* 目前支持TXT、EPUB格式(只支持显示EPUB里的文本内容,还不支持显示图片)。 |
||||
|
||||
8.如何刷新书架? |
||||
* 在书架界面下拉即可刷新。 |
||||
|
||||
9.书架界面书籍右上角的红色或者灰色背景小数字代表什么? |
||||
* 红色代表书籍有更新,灰色代表无更新,数字代表未读章节。 |
||||
|
||||
10.如何查看书籍详情? |
||||
* 长按书籍。 |
||||
|
||||
11.如何对书架上的书进行删除、切换书架的操作? |
||||
* 书籍详情页操作即可。 |
||||
|
||||
12.如何禁止或允许某本书更新? |
||||
* 书籍详情页,点击右上角 - “允许更新”。 |
||||
|
||||
13.如何更换小说封面、名字、作者或简介? |
||||
* 书籍详情页,点击右上角修改按钮。 |
||||
|
||||
14.怎么使用自定义字体? |
||||
* 阅读界面 - 字体-点击右上角选择字体文件路径。 |
||||
|
||||
15.目前支持哪些格式的字体文件? |
||||
* 目前支持ttf、otf格式。 |
||||
|
||||
16.书籍经常“正在加载中”怎么办? |
||||
* 在线书籍出现这个问题通常是由于源质量不好或不兼容引起的,可以换其它源多试试;本地书籍出现这个问题大概率是目录规则问题,手动切换规则可以解决。 |
||||
|
||||
17.书籍内容只有标题,正文内容是路径怎么办? |
||||
* 通常是缓存路径引起的,更换缓存路径即可。 |
||||
|
||||
18.效验书源显示失效就说明书源不能用了吗? |
||||
* 效验书源只是测试书源,可以做个参考,失效了不代表书源不能用了。 |
||||
|
||||
19.发现和正版书源能不能使用? |
||||
* 发现和正版书源只能用来找书,看排行榜,不能用来看书,如需看书请切换书源。 |
||||
|
||||
20.替换净化是什么? |
||||
* 替换净化可以去除书籍内容里的广告、错别字、屏蔽词等。 |
||||
|
||||
21.如何自己填写净化替换规则? |
||||
* 第一行:替换规则名称 - 根据自己需求对替换净化规则进行命名; |
||||
* 第二行:分组 - 净化规则的分组组别; |
||||
* 第三行:替换规则 - 填写需要被替换的内容; |
||||
* 第四行:替换为 - 填写想替换成的内容(如不填则默认表示删除第二行里填写的内容); |
||||
* 第五行:替换范围,选填书名或者源名 - 填写此替换净化规则需要对哪本书籍或者哪个书源生效(如不填则对所有书籍和书源生效)。 |
||||
* 注:如常规去除方法去除不掉,则需要勾选“使用正则表达式”,同时第二行里的替换规则也需要按照正则表达式来填写(正则表达式填写方法可自行百度学习)。 |
||||
|
||||
22.如何听书? |
||||
* 可以使用手机自带的朗读引擎,也可使用第三方如谷歌、小米等朗读引擎。 |
||||
* 【具体操作:安装-系统设置-其他高级设置-辅助功能-TTS输出-选择安装的朗读引擎(不同品牌手机的操作方法及步骤也不同,视情况而定)。】 |
||||
|
||||
23.如何设置屏幕方向、屏幕显示时长、显示/隐藏状态栏、显示/隐藏导航栏、音量键翻页、长按选择文本、点击总是翻下一页、自定义翻页案件? |
||||
* 阅读界面,设置(可上划,下面还有其他设置)。 |
||||
|
||||
24.搜索的时候感觉手机卡顿,如何解决? |
||||
* 我的 - 其他设置 - “更新和搜索线程数”调低。 |
||||
|
||||
25.更新前有什么注意事项? |
||||
* 要做好备份。 |
||||
|
||||
26.看书时如遇到“目录为空”、“加载失败”和长串英文等情况怎么办? |
||||
* 一般是书源问题,切换书源即可。 |
||||
|
||||
27.为什么书源这么多,发现里却只有一点点? |
||||
* 书源想要在发现界面里显示需要在书源里添加发现规则,并不是所有书源都有发现规则。 |
||||
|
||||
28.云备份在哪? |
||||
* 我的 - 备份与恢复 - WebDav设置。 |
||||
|
||||
29.如何操作进行云备份? |
||||
* 侧栏设置,WebDav设置; |
||||
* 正确填写WebDAV 服务器地址、WebDAV 账号、WebDAV 密码;(要获得这三项的信息,需要注册一个坚果云账号,如果直接在手机上注册,坚果云会让你下载app,过程比较麻烦,为了一步到位,最好是在电脑上打开这个注册链接:https://www.jianguoyun.com/d/signup;注册后,进入坚果云;点击右上角账户名处选择 “账户信息”,然后选择“安全选项”;在“安全选项” 中找到“第三方应用管理”,并选择“添加应用”,输入名称如“阅读”后,会生成密码,选择完成;其中https://dav.jianguoyun.com/dav/就是填入“WebDAV 服务器地址”的内容,“使用情况”后面的邮箱地址就是你的“WebDAV 账号”,点击显示密码后得到的密码就是你的“WebDAV 密码”。) |
||||
* 无需操作,APP默认每天自动云备份一次。 |
||||
|
||||
30.关于云备份的相关说明 |
||||
* 在正确设置好云备份的情况下,APP默认每天自动云备份一次,当日多次手动云备份会对当日的旧云备份文件进行覆盖,并不会覆盖之前及之后不同日期的备份文件,每天所自动云备份的文件会按照日期进行命名。 |
||||
|
||||
31.本地备份和云备份都能备份哪些东西? |
||||
* 书架、看书进度、搜索记录、书源、替换、APP设置等都会备份,基本涵盖所有内容。 |
||||
|
||||
32.出现某些未知bug怎么办? |
||||
* 清除软件数据试试看,不行再进行反馈。 |
@ -0,0 +1 @@ |
||||
#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50;margin:0;height:100%} |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 799 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 5.8 KiB |
@ -0,0 +1 @@ |
||||
{"name":"yd-web-tool","short_name":"yd-web-tool","theme_color":"#4DBA87","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":".","display":"standalone","background_color":"#000000"} |
@ -0,0 +1,74 @@ |
||||
package io.legado.app.help |
||||
|
||||
import io.legado.app.App |
||||
import io.legado.app.R |
||||
import io.legado.app.utils.getPrefBoolean |
||||
import io.legado.app.utils.getPrefInt |
||||
import io.legado.app.utils.putPrefBoolean |
||||
import io.legado.app.utils.putPrefInt |
||||
|
||||
object ReadTipConfig { |
||||
val tipArray: Array<String> = App.INSTANCE.resources.getStringArray(R.array.read_tip) |
||||
const val none = 0 |
||||
const val chapterTitle = 1 |
||||
const val time = 2 |
||||
const val battery = 3 |
||||
const val page = 4 |
||||
const val totalProgress = 5 |
||||
const val pageAndTotal = 6 |
||||
|
||||
val tipHeaderLeftStr: String get() = tipArray.getOrElse(tipHeaderLeft) { tipArray[none] } |
||||
val tipHeaderMiddleStr: String get() = tipArray.getOrElse(tipHeaderMiddle) { tipArray[none] } |
||||
val tipHeaderRightStr: String get() = tipArray.getOrElse(tipHeaderRight) { tipArray[none] } |
||||
val tipFooterLeftStr: String get() = tipArray.getOrElse(tipFooterLeft) { tipArray[none] } |
||||
val tipFooterMiddleStr: String get() = tipArray.getOrElse(tipFooterMiddle) { tipArray[none] } |
||||
val tipFooterRightStr: String get() = tipArray.getOrElse(tipFooterRight) { tipArray[none] } |
||||
|
||||
var tipHeaderLeft: Int |
||||
get() = App.INSTANCE.getPrefInt("tipHeaderLeft", time) |
||||
set(value) { |
||||
App.INSTANCE.putPrefInt("tipHeaderLeft", value) |
||||
} |
||||
|
||||
var tipHeaderMiddle: Int |
||||
get() = App.INSTANCE.getPrefInt("tipHeaderMiddle", none) |
||||
set(value) { |
||||
App.INSTANCE.putPrefInt("tipHeaderMiddle", value) |
||||
} |
||||
|
||||
var tipHeaderRight: Int |
||||
get() = App.INSTANCE.getPrefInt("tipHeaderRight", battery) |
||||
set(value) { |
||||
App.INSTANCE.putPrefInt("tipHeaderRight", value) |
||||
} |
||||
|
||||
var tipFooterLeft: Int |
||||
get() = App.INSTANCE.getPrefInt("tipFooterLeft", chapterTitle) |
||||
set(value) { |
||||
App.INSTANCE.putPrefInt("tipFooterLeft", value) |
||||
} |
||||
|
||||
var tipFooterMiddle: Int |
||||
get() = App.INSTANCE.getPrefInt("tipFooterMiddle", none) |
||||
set(value) { |
||||
App.INSTANCE.putPrefInt("tipFooterMiddle", value) |
||||
} |
||||
|
||||
var tipFooterRight: Int |
||||
get() = App.INSTANCE.getPrefInt("tipFooterRight", pageAndTotal) |
||||
set(value) { |
||||
App.INSTANCE.putPrefInt("tipFooterRight", value) |
||||
} |
||||
|
||||
var hideHeader: Boolean |
||||
get() = App.INSTANCE.getPrefBoolean("hideHeader", true) |
||||
set(value) { |
||||
App.INSTANCE.putPrefBoolean("hideHeader", value) |
||||
} |
||||
|
||||
var hideFooter: Boolean |
||||
get() = App.INSTANCE.getPrefBoolean("hideFooter", false) |
||||
set(value) { |
||||
App.INSTANCE.putPrefBoolean("hideFooter", value) |
||||
} |
||||
} |
@ -0,0 +1,93 @@ |
||||
package io.legado.app.model |
||||
|
||||
import io.legado.app.App |
||||
import io.legado.app.data.entities.SearchBook |
||||
import io.legado.app.help.AppConfig |
||||
import io.legado.app.help.coroutine.Coroutine |
||||
import io.legado.app.utils.getPrefString |
||||
import kotlinx.coroutines.CoroutineScope |
||||
import kotlinx.coroutines.Dispatchers.IO |
||||
import kotlinx.coroutines.asCoroutineDispatcher |
||||
import java.util.concurrent.Executors |
||||
|
||||
class SearchBookModel(private val scope: CoroutineScope, private val callBack: CallBack) { |
||||
private var searchPool = |
||||
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() |
||||
private var mSearchId = System.currentTimeMillis() |
||||
private var searchPage = 1 |
||||
private var searchKey: String = "" |
||||
private var task: Coroutine<*>? = null |
||||
|
||||
private fun initSearchPool() { |
||||
searchPool = |
||||
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() |
||||
} |
||||
|
||||
fun search(searchId: Long, key: String) { |
||||
if (searchId != mSearchId) { |
||||
task?.cancel() |
||||
searchPool.close() |
||||
initSearchPool() |
||||
mSearchId = searchId |
||||
searchPage = 1 |
||||
if (key.isEmpty()) { |
||||
return |
||||
} else { |
||||
this.searchKey = key |
||||
} |
||||
} else { |
||||
searchPage++ |
||||
} |
||||
task = Coroutine.async(scope, searchPool) { |
||||
val searchGroup = App.INSTANCE.getPrefString("searchGroup") ?: "" |
||||
val bookSourceList = if (searchGroup.isBlank()) { |
||||
App.db.bookSourceDao().allEnabled |
||||
} else { |
||||
App.db.bookSourceDao().getEnabledByGroup(searchGroup) |
||||
} |
||||
for (item in bookSourceList) { |
||||
//task取消时自动取消 by (scope = this@execute) |
||||
WebBook(item).searchBook( |
||||
searchKey, |
||||
searchPage, |
||||
scope = this, |
||||
context = searchPool |
||||
) |
||||
.timeout(30000L) |
||||
.onSuccess(IO) { |
||||
if (searchId == mSearchId) { |
||||
callBack.onSearchSuccess(it) |
||||
} |
||||
} |
||||
} |
||||
}.onStart { |
||||
callBack.onSearchStart() |
||||
} |
||||
|
||||
task?.invokeOnCompletion { |
||||
if (searchId == mSearchId) { |
||||
callBack.onSearchFinish() |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun cancelSearch() { |
||||
task?.cancel() |
||||
mSearchId = 0 |
||||
callBack.onSearchCancel() |
||||
} |
||||
|
||||
fun close() { |
||||
task?.cancel() |
||||
mSearchId = 0 |
||||
searchPool.close() |
||||
} |
||||
|
||||
interface CallBack { |
||||
fun onSearchStart() |
||||
fun onSearchSuccess(searchBooks: ArrayList<SearchBook>) |
||||
fun onSearchFinish() |
||||
fun onSearchCancel() |
||||
} |
||||
|
||||
} |