diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7c40894..c2d9234 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -10,10 +10,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
# 获取打包秘钥
- name: Checkout Android Keystore
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
repository: fengyuecanzhu/Key
token: ${{ secrets.KEY_TOKEN }} # 连接仓库的token,需要单独配置
@@ -27,18 +27,18 @@ jobs:
version=$VERSION_CODE
versionN=v${version:0:1}.${version:1:1}.${version:2:1}
- echo ::set-output name=need_create_release::"$CREATE_RELEASE"
- echo ::set-output name=version_name::"$versionN"
+ echo need_create_release="$CREATE_RELEASE" >> $GITHUB_OUTPUT
+ echo version_name="$versionN" >> $GITHUB_OUTPUT
echo need_create_release=$CREATE_RELEASE
echo version_name=$versionN
if [ $CREATE_RELEASE == 'true' -a ${{ github.ref }} == 'refs/heads/master' ];then
- echo ::set-output name=lanzou_folder_id::"1608604"
- echo ::set-output name=lanzou_share_url::"https://fycz.lanzoui.com/b00ngso7e"
+ echo lanzou_folder_id="1608604" >> $GITHUB_OUTPUT
+ echo lanzou_share_url="https://fycz.lanzoui.com/b00ngso7e" >> $GITHUB_OUTPUT
else
- echo ::set-output name=lanzou_folder_id::"2226473"
- echo ::set-output name=lanzou_share_url::"https://fycz.lanzoui.com/b00nu1f8d"
+ echo lanzou_folder_id="2226473" >> $GITHUB_OUTPUT
+ echo lanzou_share_url="https://fycz.lanzoui.com/b00nu1f8d" >> $GITHUB_OUTPUT
fi
# 编译打包
- name: Build With Gradle
@@ -59,7 +59,7 @@ jobs:
if [[ ${{ steps.config.outputs.need_create_release }} != 'true' ]];then
path="$GITHUB_WORKSPACE/app/build/outputs/apk/debug"
fi
- echo ::set-output name=file_path::"$path"
+ echo file_path="$path" >> $GITHUB_OUTPUT
- name: Upload Lanzou
run: |
echo "上传APP至蓝奏云"
diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml
new file mode 100644
index 0000000..5367e8e
--- /dev/null
+++ b/.idea/assetWizardSettings.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index ded8613..443bdfd 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,21 +3,35 @@
diff --git a/app/build.gradle b/app/build.gradle
index 4adf40e..f7282fc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -293,7 +293,11 @@ dependencies {
}
//https://github.com/fengyuecanzhu/Maple
- implementation("me.fycz.maple:maple:1.9")
+ implementation("me.fycz.maple:maple:2.0")
+
+ //加解密类库,有些书源使用
+ //noinspection GradleDependency,GradlePackageUpdate
+ implementation('cn.hutool:hutool-crypto:5.8.10')
}
greendao {
diff --git a/app/release.md b/app/release.md
index 87aa655..eee13ce 100644
--- a/app/release.md
+++ b/app/release.md
@@ -1,9 +1,5 @@
-* 1、修复阅读界面概率性闪退的问题
-* 2、关于界面新增插件加载结果
-* 3、修复书源订阅失败的问题
-* 4、修复字体下载失败的问题
-* 5、\[设置-缓存设置\]新增清除广告文件
-* 6、修复检查更新失败的问题
-* 7、修复获取更新链接失败的问题
-* 8、更新订阅书源链接
-* 9、修复其他已知bug
\ No newline at end of file
+* 1、[书籍详情界面]取消书籍简介展开时最大行数限制
+* 2、修复从数据库中读取章节时部分数据项缺失的bug
+* 3、目录列表添加更新时间显示
+* 4、更新部分书源接口
+* 5、修复插件加载bug
\ No newline at end of file
diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy
index da9e060..eb771b1 100644
--- a/app/src/main/assets/updatelog.fy
+++ b/app/src/main/assets/updatelog.fy
@@ -1,3 +1,11 @@
+2022.12.26
+风月读书v2.5.1
+1、[书籍详情界面]取消书籍简介展开时最大行数限制
+2、修复从数据库中读取章节时部分数据项缺失的bug
+3、目录列表添加更新时间显示
+4、更新部分书源接口
+5、修复插件加载bug
+
2022.08.02
风月读书v2.5.0
1、修复阅读界面概率性闪退的问题
diff --git a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
index 0250f26..28f3340 100644
--- a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
+++ b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
@@ -18,6 +18,7 @@
package xyz.fycz.myreader.common;
+import android.annotation.SuppressLint;
import android.os.Environment;
import android.provider.Settings;
@@ -31,13 +32,15 @@ import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.File;
import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.regex.Pattern;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
-
+@SuppressLint("SimpleDateFormat")
public class APPCONST {
public static String publicKey = "fyds2.0";//服务端公钥
@@ -163,6 +166,8 @@ public class APPCONST {
public static final String androidId = getAndroidId();
+ public static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");
+
public static String getAndroidId() {
return Settings.System.getString(App.getmContext().getContentResolver(), Settings.Secure.ANDROID_ID);
}
diff --git a/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java b/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
index bda9661..bc649f3 100644
--- a/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
+++ b/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
@@ -56,7 +56,8 @@ public class URLCONST {
public static final String QUOTATION = "https://v1.hitokoto.cn/?encode=json&charset=utf-8";
- public static final String DEFAULT_PLUGIN_CONFIG_URL = "https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/Plugin/release/config_FYReader.json";
+ //public static final String DEFAULT_PLUGIN_CONFIG_URL = "https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/Plugin/release/config_FYReader.json";
+ public static final String DEFAULT_PLUGIN_CONFIG_URL = "http://101.43.83.105:3000/fengyue/FYReader-Res/raw/branch/main/Plugin/release/config_FYReader.json";
public static String getDefaultDomain() {
return SharedPreUtils.getInstance().getString("domain", "fycz.me");
diff --git a/app/src/main/java/xyz/fycz/myreader/entity/PluginConfig.kt b/app/src/main/java/xyz/fycz/myreader/entity/PluginConfig.kt
index f6f18c4..2ed1685 100644
--- a/app/src/main/java/xyz/fycz/myreader/entity/PluginConfig.kt
+++ b/app/src/main/java/xyz/fycz/myreader/entity/PluginConfig.kt
@@ -31,5 +31,5 @@ data class PluginConfig(
val changelog: String
) {
constructor(name: String, versionCode: Int) :
- this(name, versionCode, "", "", "", "")
+ this(name, versionCode, "", "", "", "插件加载失败,当前为默认插件")
}
diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt b/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt
index 5b0fa0d..0219964 100644
--- a/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt
+++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/CacheManager.kt
@@ -19,6 +19,7 @@
package xyz.fycz.myreader.greendao.service
import android.database.Cursor
+import androidx.collection.LruCache
import xyz.fycz.myreader.application.App
import xyz.fycz.myreader.greendao.DbManager
import xyz.fycz.myreader.greendao.entity.Cache
@@ -31,6 +32,7 @@ import java.lang.Exception
object CacheManager {
private val queryTTFMap = hashMapOf>()
+ private val memoryLruCache = object : LruCache(100) {}
/**
* saveTime 单位为秒
@@ -49,6 +51,19 @@ object CacheManager {
}
}
+ fun putMemory(key: String, value: String) {
+ memoryLruCache.put(key, value)
+ }
+
+ //从内存中获取数据 使用lruCache
+ fun getFromMemory(key: String): String? {
+ return memoryLruCache.get(key)
+ }
+
+ fun deleteMemory(key: String) {
+ memoryLruCache.remove(key)
+ }
+
fun get(key: String): String? {
var str: String? = null
try {
@@ -94,6 +109,14 @@ object CacheManager {
return null
}
+ fun putFile(key: String, value: String, saveTime: Int = 0) {
+ ACache.get().put(key, value, saveTime)
+ }
+
+ fun getFile(key: String): String? {
+ return ACache.get().getAsString(key)
+ }
+
fun delete(key: String) {
DbManager.getDaoSession().cacheDao.deleteByKey(key)
ACache.get(App.getmContext()).remove(key)
diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
index 20e1bbd..2385020 100644
--- a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
+++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
@@ -66,6 +66,7 @@ public class ChapterService extends BaseService {
chapter.setContent(cursor.getString(8));
chapter.setStart(cursor.getInt(9));
chapter.setEnd(cursor.getInt(10));
+ chapter.setVariable(cursor.getString(11));
chapters.add(chapter);
}
} catch (Exception e) {
@@ -95,10 +96,14 @@ public class ChapterService extends BaseService {
if (StringHelper.isEmpty(bookId)) return new ArrayList<>();
+ return DbManager.getDaoSession().getChapterDao()
+ .queryBuilder()
+ .where(ChapterDao.Properties.BookId.eq(bookId))
+ .orderAsc(ChapterDao.Properties.Number)
+ .list();
+ /*String sql = "select * from chapter where book_id = ? order by number";
- String sql = "select * from chapter where book_id = ? order by number";
-
- return findChapters(sql, new String[]{bookId});
+ return findChapters(sql, new String[]{bookId});*/
}
/**
diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsEncodeUtils.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsEncodeUtils.kt
new file mode 100644
index 0000000..8d804aa
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsEncodeUtils.kt
@@ -0,0 +1,496 @@
+/*
+ * This file is part of FYReader.
+ * FYReader is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FYReader is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FYReader. If not, see .
+ *
+ * Copyright (C) 2020 - 2022 fengyuecanzhu
+ */
+
+package xyz.fycz.myreader.model.third3.analyzeRule
+
+import android.util.Base64
+import cn.hutool.crypto.digest.DigestUtil
+import cn.hutool.crypto.digest.HMac
+import cn.hutool.crypto.symmetric.SymmetricCrypto
+import xyz.fycz.myreader.util.utils.MD5Utils
+
+/**
+ * js加解密扩展类, 在js中通过java变量调用
+ * 添加方法,请更新文档/legado/app/src/main/assets/help/JsHelp.md
+ */
+interface JsEncodeUtils {
+
+ fun md5Encode(str: String): String {
+ return MD5Utils.md5Encode(str)
+ }
+
+ fun md5Encode16(str: String): String {
+ return MD5Utils.md5Encode16(str)
+ }
+
+
+ //******************对称加密解密************************//
+
+ /**
+ * 在js中这样使用
+ * java.createSymmetricCrypto(transformation, key, iv).decrypt(data)
+ * java.createSymmetricCrypto(transformation, key, iv).decryptStr(data)
+
+ * java.createSymmetricCrypto(transformation, key, iv).encrypt(data)
+ * java.createSymmetricCrypto(transformation, key, iv).encryptBase64(data)
+ * java.createSymmetricCrypto(transformation, key, iv).encryptHex(data)
+ */
+
+ /* 调用SymmetricCrypto key为null时使用随机密钥*/
+ fun createSymmetricCrypto(
+ transformation: String,
+ key: ByteArray?,
+ iv: ByteArray?
+ ): SymmetricCrypto {
+ val symmetricCrypto = SymmetricCrypto(transformation, key)
+ return if (iv != null && iv.isNotEmpty()) symmetricCrypto.setIv(iv) else symmetricCrypto
+ }
+
+ fun createSymmetricCrypto(
+ transformation: String,
+ key: ByteArray
+ ): SymmetricCrypto {
+ return createSymmetricCrypto(transformation, key, null)
+ }
+
+ fun createSymmetricCrypto(
+ transformation: String,
+ key: String
+ ): SymmetricCrypto {
+ return createSymmetricCrypto(transformation, key, null)
+ }
+
+ fun createSymmetricCrypto(
+ transformation: String,
+ key: String,
+ iv: String?
+ ): SymmetricCrypto {
+ return createSymmetricCrypto(
+ transformation, key.encodeToByteArray(), iv?.encodeToByteArray()
+ )
+ }
+
+ //******************对称加密解密old************************//
+
+ /////AES
+ /**
+ * AES 解码为 ByteArray
+ * @param str 传入的AES加密的数据
+ * @param key AES 解密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decrypt(str)")
+ )
+ fun aesDecodeToByteArray(
+ str: String, key: String, transformation: String, iv: String
+ ): ByteArray? {
+ return createSymmetricCrypto(transformation, key, iv).decrypt(str)
+ }
+
+ /**
+ * AES 解码为 String
+ * @param str 传入的AES加密的数据
+ * @param key AES 解密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(str)")
+ )
+ fun aesDecodeToString(
+ str: String, key: String, transformation: String, iv: String
+ ): String? {
+ return createSymmetricCrypto(transformation, key, iv).decryptStr(str)
+ }
+
+ /**
+ * AES解码为String,算法参数经过Base64加密
+ *
+ * @param data 加密的字符串
+ * @param key Base64后的密钥
+ * @param mode 模式
+ * @param padding 补码方式
+ * @param iv Base64后的加盐
+ * @return 解密后的字符串
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)")
+ )
+ fun aesDecodeArgsBase64Str(
+ data: String,
+ key: String,
+ mode: String,
+ padding: String,
+ iv: String
+ ): String? {
+ return createSymmetricCrypto(
+ "AES/${mode}/${padding}",
+ Base64.decode(key, Base64.NO_WRAP),
+ Base64.decode(iv, Base64.NO_WRAP)
+ ).decryptStr(data)
+ }
+
+ /**
+ * 已经base64的AES 解码为 ByteArray
+ * @param str 传入的AES Base64加密的数据
+ * @param key AES 解密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decrypt(str)")
+ )
+ fun aesBase64DecodeToByteArray(
+ str: String, key: String, transformation: String, iv: String
+ ): ByteArray? {
+ return createSymmetricCrypto(transformation, key, iv).decrypt(str)
+ }
+
+ /**
+ * 已经base64的AES 解码为 String
+ * @param str 传入的AES Base64加密的数据
+ * @param key AES 解密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(str)")
+ )
+ fun aesBase64DecodeToString(
+ str: String, key: String, transformation: String, iv: String
+ ): String? {
+ return createSymmetricCrypto(transformation, key, iv).decryptStr(str)
+ }
+
+ /**
+ * 加密aes为ByteArray
+ * @param data 传入的原始数据
+ * @param key AES加密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decrypt(data)")
+ )
+ fun aesEncodeToByteArray(
+ data: String, key: String, transformation: String, iv: String
+ ): ByteArray? {
+ return createSymmetricCrypto(transformation, key, iv).decrypt(data)
+ }
+
+ /**
+ * 加密aes为String
+ * @param data 传入的原始数据
+ * @param key AES加密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)")
+ )
+ fun aesEncodeToString(
+ data: String, key: String, transformation: String, iv: String
+ ): String? {
+ return createSymmetricCrypto(transformation, key, iv).decryptStr(data)
+ }
+
+ /**
+ * 加密aes后Base64化的ByteArray
+ * @param data 传入的原始数据
+ * @param key AES加密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data).toByteArray()")
+ )
+ fun aesEncodeToBase64ByteArray(
+ data: String, key: String, transformation: String, iv: String
+ ): ByteArray? {
+ return createSymmetricCrypto(transformation, key, iv).encryptBase64(data).toByteArray()
+ }
+
+ /**
+ * 加密aes后Base64化的String
+ * @param data 传入的原始数据
+ * @param key AES加密的key
+ * @param transformation AES加密的方式
+ * @param iv ECB模式的偏移向量
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)")
+ )
+ fun aesEncodeToBase64String(
+ data: String, key: String, transformation: String, iv: String
+ ): String? {
+ return createSymmetricCrypto(transformation, key, iv).encryptBase64(data)
+ }
+
+
+ /**
+ * AES加密并转为Base64,算法参数经过Base64加密
+ *
+ * @param data 被加密的字符串
+ * @param key Base64后的密钥
+ * @param mode 模式
+ * @param padding 补码方式
+ * @param iv Base64后的加盐
+ * @return 加密后的Base64
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)")
+ )
+ fun aesEncodeArgsBase64Str(
+ data: String,
+ key: String,
+ mode: String,
+ padding: String,
+ iv: String
+ ): String? {
+ return createSymmetricCrypto("AES/${mode}/${padding}", key, iv).encryptBase64(data)
+ }
+
+ /////DES
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)")
+ )
+ fun desDecodeToString(
+ data: String, key: String, transformation: String, iv: String
+ ): String? {
+ return createSymmetricCrypto(transformation, key, iv).decryptStr(data)
+ }
+
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)")
+ )
+ fun desBase64DecodeToString(
+ data: String, key: String, transformation: String, iv: String
+ ): String? {
+ return createSymmetricCrypto(transformation, key, iv).decryptStr(data)
+ }
+
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).encrypt(data)")
+ )
+ fun desEncodeToString(
+ data: String, key: String, transformation: String, iv: String
+ ): String? {
+ return String(createSymmetricCrypto(transformation, key, iv).encrypt(data))
+ }
+
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)")
+ )
+ fun desEncodeToBase64String(
+ data: String, key: String, transformation: String, iv: String
+ ): String? {
+ return createSymmetricCrypto(transformation, key, iv).encryptBase64(data)
+ }
+
+ //////3DES
+ /**
+ * 3DES解密
+ *
+ * @param data 加密的字符串
+ * @param key 密钥
+ * @param mode 模式
+ * @param padding 补码方式
+ * @param iv 加盐
+ * @return 解密后的字符串
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)")
+ )
+ fun tripleDESDecodeStr(
+ data: String,
+ key: String,
+ mode: String,
+ padding: String,
+ iv: String
+ ): String? {
+ return createSymmetricCrypto("DESede/${mode}/${padding}", key, iv).decryptStr(data)
+ }
+
+ /**
+ * 3DES解密,算法参数经过Base64加密
+ *
+ * @param data 加密的字符串
+ * @param key Base64后的密钥
+ * @param mode 模式
+ * @param padding 补码方式
+ * @param iv Base64后的加盐
+ * @return 解密后的字符串
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).decryptStr(data)")
+ )
+ fun tripleDESDecodeArgsBase64Str(
+ data: String,
+ key: String,
+ mode: String,
+ padding: String,
+ iv: String
+ ): String? {
+ return createSymmetricCrypto(
+ "DESede/${mode}/${padding}",
+ Base64.decode(key, Base64.NO_WRAP),
+ iv.encodeToByteArray()
+ ).decryptStr(data)
+ }
+
+
+ /**
+ * 3DES加密并转为Base64
+ *
+ * @param data 被加密的字符串
+ * @param key 密钥
+ * @param mode 模式
+ * @param padding 补码方式
+ * @param iv 加盐
+ * @return 加密后的Base64
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)")
+ )
+ fun tripleDESEncodeBase64Str(
+ data: String,
+ key: String,
+ mode: String,
+ padding: String,
+ iv: String
+ ): String? {
+ return createSymmetricCrypto("DESede/${mode}/${padding}", key, iv)
+ .encryptBase64(data)
+ }
+
+ /**
+ * 3DES加密并转为Base64,算法参数经过Base64加密
+ *
+ * @param data 被加密的字符串
+ * @param key Base64后的密钥
+ * @param mode 模式
+ * @param padding 补码方式
+ * @param iv Base64后的加盐
+ * @return 加密后的Base64
+ */
+ @Deprecated(
+ "过于繁琐弃用",
+ ReplaceWith("createSymmetricCrypto(transformation, key, iv).encryptBase64(data)")
+ )
+ fun tripleDESEncodeArgsBase64Str(
+ data: String,
+ key: String,
+ mode: String,
+ padding: String,
+ iv: String
+ ): String? {
+ return createSymmetricCrypto(
+ "DESede/${mode}/${padding}",
+ Base64.decode(key, Base64.NO_WRAP),
+ iv.encodeToByteArray()
+ ).encryptBase64(data)
+ }
+
+//******************消息摘要/散列消息鉴别码************************//
+
+ /**
+ * 生成摘要,并转为16进制字符串
+ *
+ * @param data 被摘要数据
+ * @param algorithm 签名算法
+ * @return 16进制字符串
+ */
+ fun digestHex(
+ data: String,
+ algorithm: String,
+ ): String {
+ return DigestUtil.digester(algorithm).digestHex(data)
+ }
+
+ /**
+ * 生成摘要,并转为Base64字符串
+ *
+ * @param data 被摘要数据
+ * @param algorithm 签名算法
+ * @return Base64字符串
+ */
+ fun digestBase64Str(
+ data: String,
+ algorithm: String,
+ ): String {
+ return Base64.encodeToString(DigestUtil.digester(algorithm).digest(data), Base64.NO_WRAP)
+ }
+
+ /**
+ * 生成散列消息鉴别码,并转为16进制字符串
+ *
+ * @param data 被摘要数据
+ * @param algorithm 签名算法
+ * @param key 密钥
+ * @return 16进制字符串
+ */
+ @Suppress("FunctionName")
+ fun HMacHex(
+ data: String,
+ algorithm: String,
+ key: String
+ ): String {
+ return HMac(algorithm, key.toByteArray()).digestHex(data)
+ }
+
+ /**
+ * 生成散列消息鉴别码,并转为Base64字符串
+ *
+ * @param data 被摘要数据
+ * @param algorithm 签名算法
+ * @param key 密钥
+ * @return Base64字符串
+ */
+ @Suppress("FunctionName")
+ fun HMacBase64(
+ data: String,
+ algorithm: String,
+ key: String
+ ): String {
+ return Base64.encodeToString(
+ HMac(algorithm, key.toByteArray()).digest(data),
+ Base64.NO_WRAP
+ )
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt
index 82cda80..98bd8b9 100644
--- a/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt
+++ b/app/src/main/java/xyz/fycz/myreader/model/third3/analyzeRule/JsExtensions.kt
@@ -22,19 +22,22 @@ import android.net.Uri
import android.util.Base64
import android.util.Log
import androidx.annotation.Keep
+import cn.hutool.core.util.HexUtil
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
-import nl.siegmann.epublib.epub.PackageDocumentBase.dateFormat
import org.jsoup.Connection
import org.jsoup.Jsoup
import xyz.fycz.myreader.application.App
import xyz.fycz.myreader.common.APPCONST
+import xyz.fycz.myreader.common.APPCONST.dateFormat
import xyz.fycz.myreader.greendao.service.CacheManager
import xyz.fycz.myreader.greendao.service.CookieStore
import xyz.fycz.myreader.model.third3.BaseSource
import xyz.fycz.myreader.model.third3.Debug
+import xyz.fycz.myreader.model.third3.NoStackTraceException
import xyz.fycz.myreader.model.third3.http.*
+import xyz.fycz.myreader.util.ToastUtils
import xyz.fycz.myreader.util.ZipUtils
import xyz.fycz.myreader.util.utils.*
import java.io.ByteArrayInputStream
@@ -54,7 +57,7 @@ import java.util.zip.ZipInputStream
*/
@Keep
@Suppress("unused")
-interface JsExtensions {
+interface JsExtensions : JsEncodeUtils {
val TAG: String?
get() = JsExtensions::class.simpleName
@@ -141,11 +144,52 @@ interface JsExtensions {
BackstageWebView(
url = url,
html = html,
- javaScript = js
+ javaScript = js,
+ headerMap = getSource()?.getHeaderMap(true),
+ tag = getSource()?.getKey()
).getStrResponse().body
}
}
+ /**
+ * 可从网络,本地文件(阅读私有缓存目录和书籍保存位置支持相对路径)导入JavaScript脚本
+ */
+ fun importScript(path: String): String {
+ val result = when {
+ path.startsWith("http") -> cacheFile(path) ?: ""
+ path.isUri() -> String(DocumentUtil.readBytes(App.getmContext(), Uri.parse(path)))
+ path.startsWith("/storage") -> FileUtils.readText(path)
+ else -> readTxtFile(path)
+ }
+ if (result.isBlank()) throw NoStackTraceException("$path 内容获取失败或者为空")
+ return result
+ }
+
+ /**
+ * 缓存以文本方式保存的文件 如.js .txt等
+ * @param urlStr 网络文件的链接
+ * @return 返回缓存后的文件内容
+ */
+ fun cacheFile(urlStr: String): String? {
+ return cacheFile(urlStr, 0)
+ }
+
+ /**
+ * 缓存以文本方式保存的文件 如.js .txt等
+ * @param saveTime 缓存时间,单位:秒
+ */
+ fun cacheFile(urlStr: String, saveTime: Int): String? {
+ val key = md5Encode16(urlStr)
+ val cache = CacheManager.getFile(key)
+ if (cache.isNullOrBlank()) {
+ log("首次下载 $urlStr")
+ val value = ajax(urlStr) ?: return null
+ CacheManager.putFile(key, value, saveTime)
+ return value
+ }
+ return cache
+ }
+
/**
* 实现16进制字符串转文件
* @param content 需要转成文件的16进制字符串
@@ -172,20 +216,45 @@ interface JsExtensions {
* js实现重定向拦截,网络访问get
*/
fun get(urlStr: String, headers: Map): Connection.Response {
- return Jsoup.connect(urlStr)
+ val response = Jsoup.connect(urlStr)
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory)
.ignoreContentType(true)
.followRedirects(false)
.headers(headers)
.method(Connection.Method.GET)
.execute()
+ val cookies = response.cookies()
+ CookieStore.mapToCookie(cookies)?.let {
+ val domain = NetworkUtils.getSubDomain(urlStr)
+ CacheManager.putMemory("${domain}_cookieJar", it)
+ }
+ return response
+ }
+
+ /**
+ * js实现重定向拦截,网络访问head,不返回Response Body更省流量
+ */
+ fun head(urlStr: String, headers: Map): Connection.Response {
+ val response = Jsoup.connect(urlStr)
+ .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory)
+ .ignoreContentType(true)
+ .followRedirects(false)
+ .headers(headers)
+ .method(Connection.Method.HEAD)
+ .execute()
+ val cookies = response.cookies()
+ CookieStore.mapToCookie(cookies)?.let {
+ val domain = NetworkUtils.getSubDomain(urlStr)
+ CacheManager.putMemory("${domain}_cookieJar", it)
+ }
+ return response
}
/**
* 网络访问post
*/
fun post(urlStr: String, body: String, headers: Map): Connection.Response {
- return Jsoup.connect(urlStr)
+ val response = Jsoup.connect(urlStr)
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory)
.ignoreContentType(true)
.followRedirects(false)
@@ -193,6 +262,12 @@ interface JsExtensions {
.headers(headers)
.method(Connection.Method.POST)
.execute()
+ val cookies = response.cookies()
+ CookieStore.mapToCookie(cookies)?.let {
+ val domain = NetworkUtils.getSubDomain(urlStr)
+ CacheManager.putMemory("${domain}_cookieJar", it)
+ }
+ return response
}
/**
@@ -241,12 +316,19 @@ interface JsExtensions {
return EncoderUtils.base64Encode(str, flags)
}
- fun md5Encode(str: String): String {
- return MD5Utils.md5Encode(str)
+ /* HexString 解码为字节数组 */
+ fun hexDecodeToByteArray(hex: String): ByteArray? {
+ return HexUtil.decodeHex(hex)
+ }
+
+ /* hexString 解码为utf8String*/
+ fun hexDecodeToString(hex: String): String? {
+ return HexUtil.decodeHexStr(hex)
}
- fun md5Encode16(str: String): String {
- return MD5Utils.md5Encode16(str)
+ /* utf8 编码为hexString */
+ fun hexEncodeToString(utf8: String): String? {
+ return HexUtil.encodeHexStr(utf8)
}
/**
@@ -499,174 +581,48 @@ interface JsExtensions {
}
/**
- * 输出调试日志
+ * 弹窗提示
*/
- fun log(msg: String): String {
- getSource()?.let {
- Debug.log(it.getKey(), msg)
- } ?: Debug.log(msg)
- if (App.isDebug()) {
- Log.d(TAG + "-" + getSource()?.getKey(), msg)
- }
- return msg
+ fun toast(msg: Any?) {
+ ToastUtils.showInfo("${getSource()?.getTag()}: ${msg.toString()}")
}
/**
- * 生成UUID
+ * 弹窗提示 停留时间较长
*/
- fun randomUUID(): String {
- return UUID.randomUUID().toString()
+ fun longToast(msg: Any?) {
+ toast(msg)
}
/**
- * AES 解码为 ByteArray
- * @param str 传入的AES加密的数据
- * @param key AES 解密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
- */
- fun aesDecodeToByteArray(
- str: String, key: String, transformation: String, iv: String
- ): ByteArray? {
- return try {
- EncoderUtils.decryptAES(
- data = str.encodeToByteArray(),
- key = key.encodeToByteArray(),
- transformation,
- iv.encodeToByteArray()
- )
- } catch (e: Exception) {
- Log.e(TAG, e.toString())
- log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
- null
- }
- }
-
- /**
- * AES 解码为 String
- * @param str 传入的AES加密的数据
- * @param key AES 解密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
- */
-
- fun aesDecodeToString(
- str: String, key: String, transformation: String, iv: String
- ): String? {
- return aesDecodeToByteArray(str, key, transformation, iv)?.let { String(it) }
- }
-
- /**
- * 已经base64的AES 解码为 ByteArray
- * @param str 传入的AES Base64加密的数据
- * @param key AES 解密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
- */
-
- fun aesBase64DecodeToByteArray(
- str: String, key: String, transformation: String, iv: String
- ): ByteArray? {
- return try {
- EncoderUtils.decryptBase64AES(
- str.encodeToByteArray(),
- key.encodeToByteArray(),
- transformation,
- iv.encodeToByteArray()
- )
- } catch (e: Exception) {
- Log.e(TAG, e.toString())
- log(e.localizedMessage ?: "aesDecodeToByteArrayERROR")
- null
- }
- }
-
- /**
- * 已经base64的AES 解码为 String
- * @param str 传入的AES Base64加密的数据
- * @param key AES 解密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
- */
-
- fun aesBase64DecodeToString(
- str: String, key: String, transformation: String, iv: String
- ): String? {
- return aesBase64DecodeToByteArray(str, key, transformation, iv)?.let { String(it) }
- }
-
- /**
- * 加密aes为ByteArray
- * @param data 传入的原始数据
- * @param key AES加密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
+ * 输出调试日志
*/
- fun aesEncodeToByteArray(
- data: String, key: String, transformation: String, iv: String
- ): ByteArray? {
- return try {
- EncoderUtils.encryptAES(
- data.encodeToByteArray(),
- key = key.encodeToByteArray(),
- transformation,
- iv.encodeToByteArray()
- )
- } catch (e: Exception) {
- Log.e(TAG, e.toString())
- log(e.localizedMessage ?: "aesEncodeToByteArrayERROR")
- null
+ fun log(msg: String): String {
+ getSource()?.let {
+ Debug.log(it.getKey(), msg)
+ } ?: Debug.log(msg)
+ if (App.isDebug()) {
+ Log.d(TAG + "-" + getSource()?.getKey(), msg)
}
+ return msg
}
/**
- * 加密aes为String
- * @param data 传入的原始数据
- * @param key AES加密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
- */
- fun aesEncodeToString(
- data: String, key: String, transformation: String, iv: String
- ): String? {
- return aesEncodeToByteArray(data, key, transformation, iv)?.let { String(it) }
- }
-
- /**
- * 加密aes后Base64化的ByteArray
- * @param data 传入的原始数据
- * @param key AES加密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
+ * 输出对象类型
*/
- fun aesEncodeToBase64ByteArray(
- data: String, key: String, transformation: String, iv: String
- ): ByteArray? {
- return try {
- EncoderUtils.encryptAES2Base64(
- data.encodeToByteArray(),
- key.encodeToByteArray(),
- transformation,
- iv.encodeToByteArray()
- )
- } catch (e: Exception) {
- Log.e(TAG, e.toString())
- log(e.localizedMessage ?: "aesEncodeToBase64ByteArrayERROR")
- null
+ fun logType(any: Any?) {
+ if (any == null) {
+ log("null")
+ } else {
+ log(any.javaClass.name)
}
}
/**
- * 加密aes后Base64化的String
- * @param data 传入的原始数据
- * @param key AES加密的key
- * @param transformation AES加密的方式
- * @param iv ECB模式的偏移向量
+ * 生成UUID
*/
- fun aesEncodeToBase64String(
- data: String, key: String, transformation: String, iv: String
- ): String? {
- return aesEncodeToBase64ByteArray(data, key, transformation, iv)?.let { String(it) }
+ fun randomUUID(): String {
+ return UUID.randomUUID().toString()
}
fun android(): String {
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
index 9572509..851d8d1 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
@@ -361,7 +361,7 @@ public class BookDetailedActivity extends BaseActivity() {
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookMarkAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookMarkAdapter.java
index dad8fa8..5d567ca 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookMarkAdapter.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/BookMarkAdapter.java
@@ -67,7 +67,6 @@ public class BookMarkAdapter extends ArrayAdapter {
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(mResourceId,null);
viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_chapter_title);
- viewHolder.vLine = (View) convertView.findViewById(R.id.v_line);
convertView.setTag(viewHolder);
}else {
viewHolder = (ViewHolder) convertView.getTag();
@@ -78,14 +77,7 @@ public class BookMarkAdapter extends ArrayAdapter {
private void initView(int postion,final ViewHolder viewHolder){
final BookMark bookMark = getItem(postion);
- assert bookMark != null;
viewHolder.tvTitle.setText(String.format("%s[%s]", bookMark.getTitle(), bookMark.getBookMarkReadPosition() + 1));
- if (ChapterService.isChapterCached(bookMark)){
- viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(), R.drawable.selector_category_load),null,null,null);
- } else {
- viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_unload),null,null,null);
- }
- viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.textSecondary));
/*if (!setting.isDayStyle()) {
viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.sys_night_word));
viewHolder.vLine.setBackground(getContext().getDrawable(R.color.sys_dialog_setting_line));
@@ -134,9 +126,7 @@ public class BookMarkAdapter extends ArrayAdapter {
}
class ViewHolder{
-
TextView tvTitle;
- View vLine;
}
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java
index 8e115b3..99f7b49 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java
@@ -19,12 +19,15 @@
package xyz.fycz.myreader.ui.adapter;
import android.content.Context;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
+import android.widget.ImageView;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
@@ -37,7 +40,6 @@ import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.ChapterService;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
@@ -72,8 +74,9 @@ public class ChapterTitleAdapter extends ArrayAdapter {
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate(mResourceId, null);
- viewHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_chapter_title);
- viewHolder.vLine = (View) convertView.findViewById(R.id.v_line);
+ viewHolder.tvTitle = convertView.findViewById(R.id.tv_chapter_title);
+ viewHolder.tvTag = convertView.findViewById(R.id.tv_tag);
+ viewHolder.ivIcon = convertView.findViewById(R.id.iv_icon);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
@@ -86,21 +89,30 @@ public class ChapterTitleAdapter extends ArrayAdapter {
final Chapter chapter = getItem(postion);
// viewHolder.tvTitle.setText("【" + chapter.getTitle() + "】");
viewHolder.tvTitle.setText(chapter.getTitle());
+ //viewHolder.ivIcon.setImageResource(R.drawable.ic_cloud_download);
if (ChapterService.isChapterCached(chapter) || chapter.getEnd() > 0) {
- viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_load), null, null, null);
+ viewHolder.ivIcon.setVisibility(View.INVISIBLE);
+ viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.textPrimary));
} else {
- viewHolder.tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_unload), null, null, null);
+ viewHolder.ivIcon.setVisibility(View.VISIBLE);
+ viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.textSecondary));
}
- viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.textSecondary));
/*if (!setting.isDayStyle()) {
viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.sys_night_word));
viewHolder.vLine.setBackground(getContext().getDrawable(R.color.sys_dialog_setting_line));
}else {
viewHolder.tvTitle.setTextColor(getContext().getColor(R.color.title_black));
}*/
-
+ if (TextUtils.isEmpty(chapter.getUpdateTime())) {
+ viewHolder.tvTag.setVisibility(View.GONE);
+ } else {
+ viewHolder.tvTag.setText(chapter.getUpdateTime());
+ viewHolder.tvTag.setVisibility(View.VISIBLE);
+ }
if (chapter.getNumber() == mBook.getHisttoryChapterNum()) {
viewHolder.tvTitle.setTextColor(getContext().getResources().getColor(R.color.colorAccent));
+ /*viewHolder.ivIcon.setImageResource(R.drawable.ic_check);
+ viewHolder.ivIcon.setVisibility(View.VISIBLE);*/
}
}
@@ -152,7 +164,8 @@ public class ChapterTitleAdapter extends ArrayAdapter {
class ViewHolder {
TextView tvTitle;
- View vLine;
+ TextView tvTag;
+ ImageView ivIcon;
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/CatalogHolder.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/CatalogHolder.java
index d73b67d..6b8f5cf 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/CatalogHolder.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/CatalogHolder.java
@@ -18,6 +18,8 @@
package xyz.fycz.myreader.ui.adapter.holder;
+import android.view.View;
+import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
@@ -34,23 +36,21 @@ import xyz.fycz.myreader.greendao.service.ChapterService;
*/
public class CatalogHolder extends ViewHolderImpl {
private TextView tvTitle;
+ private ImageView ivIcon;
@Override
protected int getItemLayoutId() {
- return R.layout.listview_chapter_title_item;
+ return R.layout.item_chapter;
}
@Override
public void initView() {
tvTitle = findById(R.id.tv_chapter_title);
+ ivIcon = findById(R.id.iv_icon);
}
@Override
public void onBind(RecyclerView.ViewHolder holder, Chapter data, int pos) {
- if (ChapterService.isChapterCached(data) || data.getEnd() > 0) {
- tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_load), null, null, null);
- } else {
- tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_unload), null, null, null);
- }
tvTitle.setText(data.getTitle());
+ ivIcon.setVisibility(View.GONE);
}
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/FindBookHolder.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/FindBookHolder.java
index 56c3783..673d3e5 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/FindBookHolder.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/FindBookHolder.java
@@ -49,6 +49,7 @@ import xyz.fycz.myreader.webapi.BookApi;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
+import xyz.fycz.myreader.webapi.crawler.source.ThirdCrawler;
import xyz.fycz.myreader.widget.CoverImageView;
/**
@@ -96,25 +97,29 @@ public class FindBookHolder extends ViewHolderImpl {
tvBookName.setText(data.getName());
if (!StringHelper.isEmpty(data.getAuthor())) {
tvAuthor.setText(data.getAuthor());
- }else {
+ } else {
tvAuthor.setText("");
}
initTagList(data);
if (!StringHelper.isEmpty(data.getNewestChapterTitle())) {
tvNewestChapter.setText(getContext().getString(R.string.newest_chapter, data.getNewestChapterTitle()));
+ tvNewestChapter.setVisibility(View.VISIBLE);
} else {
data.setNewestChapterTitle("");
tvNewestChapter.setText("");
+ tvNewestChapter.setVisibility(View.GONE);
}
if (!StringHelper.isEmpty(data.getDesc())) {
tvDesc.setText(String.format("简介:%s", data.getDesc()));
+ tvDesc.setVisibility(View.VISIBLE);
} else {
data.setDesc("");
tvDesc.setText("");
+ tvDesc.setVisibility(View.GONE);
}
if (!StringHelper.isEmpty(source.getSourceName()) && !"未知书源".equals(source.getSourceName()))
tvSource.setText(String.format("书源:%s", source.getSourceName()));
- if (needGetInfo(data) && rc instanceof BookInfoCrawler) {
+ if (rc instanceof BookInfoCrawler && needGetInfo(data)) {
Log.i(data.getName(), "initOtherInfo");
BookInfoCrawler bic = (BookInfoCrawler) rc;
BookApi.getBookInfo(data, bic).compose(RxUtils::toSimpleSingle)
@@ -132,9 +137,15 @@ public class FindBookHolder extends ViewHolderImpl {
//简介
if (StringHelper.isEmpty(tvDesc.getText().toString())) {
tvDesc.setText(String.format("简介:%s", book.getDesc()));
+ tvNewestChapter.setVisibility(View.VISIBLE);
+ } else {
+ tvNewestChapter.setVisibility(View.GONE);
}
if (StringHelper.isEmpty(tvNewestChapter.getText().toString())) {
tvNewestChapter.setText(getContext().getString(R.string.newest_chapter, book.getNewestChapterTitle()));
+ tvNewestChapter.setVisibility(View.VISIBLE);
+ } else {
+ tvNewestChapter.setVisibility(View.GONE);
}
if (!StringHelper.isEmpty(book.getAuthor())) {
tvAuthor.setText(book.getAuthor());
@@ -163,6 +174,7 @@ public class FindBookHolder extends ViewHolderImpl {
tflBookTag.setAdapter(new BookTagAdapter(getContext(), tagList, 11));
}
}
+
private boolean needGetInfo(Book bookBean) {
if (StringHelper.isEmpty(bookBean.getAuthor())) return true;
if (StringHelper.isEmpty(bookBean.getType())) return true;
@@ -170,10 +182,11 @@ public class FindBookHolder extends ViewHolderImpl {
if (StringHelper.isEmpty(bookBean.getNewestChapterTitle())) return true;
return StringHelper.isEmpty(bookBean.getImgUrl());
}
+
/**
* Here is the key method to apply the animation
*/
- protected void setAnimation(View viewToAnimate, int position){
+ protected void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on screen, it's animated
Animation animation =
AnimationUtils.loadAnimation(viewToAnimate.getContext(), R.anim.anim_recycle_item);
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java
index 88a4f1e..b0cc145 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java
@@ -37,6 +37,7 @@ import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.hjq.permissions.OnPermissionCallback;
import com.kongzue.dialogx.dialogs.BottomMenu;
import org.jetbrains.annotations.NotNull;
@@ -45,6 +46,7 @@ import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
@@ -84,6 +86,7 @@ import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.AdUtils;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
+import xyz.fycz.myreader.util.utils.StoragePermissionUtils;
/**
* @author fengyue
@@ -205,7 +208,7 @@ public class MineFragment extends BaseFragment {
@Override
protected void initClick() {
super.initClick();
- binding.mineRlUser.setOnClickListener(v -> {
+ binding.mineRlUser.setOnClickListener(v -> StoragePermissionUtils.request(this, (permissions, all) -> {
if (isLogin) {
Intent intent = new Intent(getActivity(), UserInfoActivity.class);
startActivityForResult(intent, APPCONST.REQUEST_LOGOUT);
@@ -213,7 +216,7 @@ public class MineFragment extends BaseFragment {
Intent intent = new Intent(getActivity(), LoginActivity.class);
getActivity().startActivityForResult(intent, APPCONST.REQUEST_LOGIN);
}
- });
+ }));
binding.mineRlSyn.setOnClickListener(v -> {
if (!isLogin) {
ToastUtils.showWarring("请先登录!");
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookMarkPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookMarkPresenter.java
index b3ad661..ed396bc 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookMarkPresenter.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookMarkPresenter.java
@@ -21,7 +21,6 @@ package xyz.fycz.myreader.ui.presenter;
import android.app.Activity;
import android.content.Intent;
import xyz.fycz.myreader.R;
-import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BasePresenter;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.entity.Book;
@@ -74,7 +73,7 @@ public class BookMarkPresenter implements BasePresenter {
private void initBookMarkList() {
mBookMarks = (ArrayList) mBookMarkService.findBookAllBookMarkByBookId(mBook.getId());
- mBookMarkAdapter = new BookMarkAdapter(mBookMarkFragment.getActivity(), R.layout.listview_chapter_title_item, mBookMarks);
+ mBookMarkAdapter = new BookMarkAdapter(mBookMarkFragment.getActivity(), R.layout.item_chapter, mBookMarks);
mBookMarkFragment.getLvBookmarkList().setAdapter(mBookMarkAdapter);
}
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java
index 07fcba4..ba3123e 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java
@@ -24,7 +24,6 @@ import android.view.View;
import org.jetbrains.annotations.NotNull;
-import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.BasePresenter;
@@ -137,7 +136,7 @@ public class CatalogPresenter implements BasePresenter {
//设置布局管理器
int curChapterPosition;
curChapterPosition = mBook.getHisttoryChapterNum();
- mChapterTitleAdapter = new ChapterTitleAdapter(mCatalogFragment.getContext(), R.layout.listview_chapter_title_item, mChapters, mBook);
+ mChapterTitleAdapter = new ChapterTitleAdapter(mCatalogFragment.getContext(), R.layout.item_chapter, mChapters, mBook);
mCatalogFragment.getLvChapterList().setAdapter(mChapterTitleAdapter);
mCatalogFragment.getLvChapterList().setSelection(curChapterPosition);
}
diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/ACache.kt b/app/src/main/java/xyz/fycz/myreader/util/utils/ACache.kt
index 58d8835..71ed2ad 100644
--- a/app/src/main/java/xyz/fycz/myreader/util/utils/ACache.kt
+++ b/app/src/main/java/xyz/fycz/myreader/util/utils/ACache.kt
@@ -52,7 +52,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
@JvmOverloads
fun get(
- ctx: Context,
+ ctx: Context = App.getmContext(),
cacheName: String = "ACache",
maxSize: Long = MAX_SIZE.toLong(),
maxCount: Int = MAX_COUNT,
diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
index 982c5c6..e484729 100644
--- a/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
+++ b/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
@@ -214,7 +214,9 @@ public class OkHttpUtils {
}
public static String getBakUpdateInfo() throws IOException {
- return OkHttpUtils.getHtml("https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/FYReader-Update/" +
+ return OkHttpUtils.getHtml(
+ //"https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/FYReader-Update/" +
+ "http://101.43.83.105:3000/fengyue/FYReader-Res/raw/branch/main/Plugin/" +
(App.isDebug() ? "debug" : "release") +
"/content.txt");
}
diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/PluginUtils.kt b/app/src/main/java/xyz/fycz/myreader/util/utils/PluginUtils.kt
index ba9e704..e6252ad 100644
--- a/app/src/main/java/xyz/fycz/myreader/util/utils/PluginUtils.kt
+++ b/app/src/main/java/xyz/fycz/myreader/util/utils/PluginUtils.kt
@@ -58,24 +58,27 @@ object PluginUtils {
val oldConfig = GSON.fromJsonObject(
SharedPreUtils.getInstance().getString("pluginConfig")
) ?: PluginConfig("dynamic.dex", 100)
- launch { loadAppLoader(App.getmContext(), config) }
- val configJson = getProxyClient().newCallResponseBody {
- url(pluginConfigUrl)
- }.text()
- config = GSON.fromJsonObject(configJson)
- if (config != null) {
+ try {
+ val configJson = getProxyClient().newCallResponseBody(5) {
+ url(pluginConfigUrl)
+ }.text()
+ config = GSON.fromJsonObject(configJson)
if (config!!.versionCode > oldConfig.versionCode) {
downloadPlugin(config!!)
- SharedPreUtils.getInstance().putString("pluginConfig", configJson)
}
- } else {
+ } catch (e: Exception) {
config = oldConfig
+ e.printStackTrace()
+ errorMsg = e.stackTraceToString()
}
- if (config!!.md5.lowercase(Locale.getDefault())
- != getPluginMD5(config!!)?.lowercase(Locale.getDefault())
- ) {
- downloadPlugin(config!!)
+ kotlin.runCatching {
+ if (config!!.md5.lowercase(Locale.getDefault())
+ != getPluginMD5(config!!)?.lowercase(Locale.getDefault())
+ ) {
+ downloadPlugin(config!!)
+ }
}
+ Log.d(TAG, config!!.toString())
}.onSuccess {
loadAppLoader(App.getmContext(), config)
}
@@ -117,6 +120,7 @@ object PluginUtils {
hasLoad = true
loadSuccess = true
}
+ SharedPreUtils.getInstance().putString("pluginConfig", GSON.toJson(config))
} catch (e: Exception) {
e.printStackTrace()
errorMsg = e.stackTraceToString()
diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt b/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt
index 0182804..87d191a 100644
--- a/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt
+++ b/app/src/main/java/xyz/fycz/myreader/util/utils/StringExtensions.kt
@@ -23,6 +23,7 @@ package xyz.fycz.myreader.util.utils
import android.icu.text.Collator
import android.icu.util.ULocale
import android.net.Uri
+import android.text.Editable
import java.io.File
import java.util.*
@@ -30,14 +31,19 @@ fun String?.safeTrim() = if (this.isNullOrBlank()) null else this.trim()
fun String?.isContentScheme(): Boolean = this?.startsWith("content://") == true
+fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this)
+
fun String.parseToUri(): Uri {
- return if (isContentScheme()) {
- Uri.parse(this)
- } else {
+ return if (isUri()) Uri.parse(this) else {
Uri.fromFile(File(this))
}
}
+fun String?.isUri(): Boolean {
+ this ?: return false
+ return this.startsWith("file://", true) || isContentScheme()
+}
+
fun String?.isAbsUrl() =
this?.let {
it.startsWith("http://", true) || it.startsWith("https://", true)
@@ -65,8 +71,22 @@ fun String?.isJsonArray(): Boolean =
str.startsWith("[") && str.endsWith("]")
} ?: false
-fun String.splitNotBlank(vararg delimiter: String): Array = run {
- this.split(*delimiter).map { it.trim() }.filterNot { it.isBlank() }.toTypedArray()
+fun String?.isXml(): Boolean =
+ this?.run {
+ val str = this.trim()
+ str.startsWith("<") && str.endsWith(">")
+ } ?: false
+
+fun String?.isTrue(nullIsTrue: Boolean = false): Boolean {
+ if (this.isNullOrBlank() || this == "null") {
+ return nullIsTrue
+ }
+ return !this.trim().matches("(?i)^(false|no|not|0)$".toRegex())
+}
+
+fun String.splitNotBlank(vararg delimiter: String, limit: Int = 0): Array = run {
+ this.split(*delimiter, limit = limit).map { it.trim() }.filterNot { it.isBlank() }
+ .toTypedArray()
}
fun String.splitNotBlank(regex: Regex, limit: Int = 0): Array = run {
@@ -97,3 +117,4 @@ fun String.toStringArray(): Array {
}
}
+
diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/find/Third3FindCrawler.kt b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/find/Third3FindCrawler.kt
index 3875cc8..83d74de 100644
--- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/find/Third3FindCrawler.kt
+++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/find/Third3FindCrawler.kt
@@ -18,11 +18,95 @@
package xyz.fycz.myreader.webapi.crawler.source.find
+import io.reactivex.Observable
+import xyz.fycz.myreader.application.App
+import xyz.fycz.myreader.entity.FindKind
+import xyz.fycz.myreader.entity.thirdsource.source3.ExploreKind3
import xyz.fycz.myreader.greendao.entity.rule.BookSource
+import xyz.fycz.myreader.util.utils.ACache
+import xyz.fycz.myreader.util.utils.GSON
+import xyz.fycz.myreader.util.utils.fromJsonArray
+import xyz.fycz.myreader.util.utils.isJsonArray
/**
* @author fengyue
* @date 2022/1/20 15:33
*/
class Third3FindCrawler(source: BookSource) : ThirdFindCrawler(source) {
+ override fun initData(): Observable {
+ return Observable.create { emitter ->
+ val exploreUrl = source.findRule.url
+ if (exploreUrl.isNullOrBlank()) {
+ emitter.onNext(false)
+ emitter.onComplete()
+ return@create
+ }
+ val kinds = arrayListOf()
+ var ruleStr = exploreUrl
+ kotlin.runCatching {
+ if (exploreUrl.startsWith("", false)
+ || exploreUrl.startsWith("@js:", false)
+ ) {
+ val aCache = ACache.get(cacheName = "explore")
+ ruleStr = aCache.getAsString(source.sourceUrl) ?: ""
+ if (ruleStr.isBlank()) {
+ val jsStr = if (exploreUrl.startsWith("@")) {
+ exploreUrl.substring(4)
+ } else {
+ exploreUrl.substring(4, exploreUrl.lastIndexOf("<"))
+ }
+ ruleStr = source.evalJS(jsStr).toString().trim()
+ aCache.put(source.sourceUrl, ruleStr)
+ }
+ }
+ if (ruleStr.isJsonArray()) {
+ GSON.fromJsonArray(ruleStr)?.let {
+ kinds.addAll(it)
+ }
+ } else {
+ ruleStr.split("(&&|\n)+".toRegex()).forEach { kindStr ->
+ val kindCfg = kindStr.split("::")
+ kinds.add(ExploreKind3(kindCfg.getOrNull(0) ?: "", kindCfg.getOrNull(1)))
+ }
+ }
+ var children = arrayListOf()
+ var groupName = name
+ var nameCount = 0
+ kinds.forEach {
+ if (it.title.isBlank()) {
+ if (children.size > 0) {
+ nameCount++
+ kindsMap[groupName] = children
+ children = arrayListOf()
+ }
+ groupName = "$name[$nameCount]"
+ } else if (it.url.isNullOrBlank()) {
+ if (children.size > 0) {
+ kindsMap[groupName] = children
+ children = arrayListOf()
+ }
+ groupName = it.title.replace("\\s".toRegex(), "")
+ } else {
+ val findKindBean = FindKind().apply {
+ tag = source.sourceUrl
+ name = it.title.replace("\\s".toRegex(), "")
+ url = it.url
+ }
+ children.add(findKindBean)
+ }
+ }
+ if (children.size > 0) {
+ kindsMap[groupName] = children
+ }
+ emitter.onNext(true)
+ }.onFailure {
+ emitter.onNext(false)
+ kinds.add(ExploreKind3("ERROR:${it.localizedMessage}", it.stackTraceToString()))
+ if (App.isDebug()) {
+ it.printStackTrace()
+ }
+ }
+ emitter.onComplete()
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/ic_item_category_download.png b/app/src/main/res/drawable-xhdpi/ic_item_category_download.png
deleted file mode 100644
index bb61a3c..0000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_item_category_download.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_item_category_normal.png b/app/src/main/res/drawable-xhdpi/ic_item_category_normal.png
deleted file mode 100644
index b4e9b49..0000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_item_category_normal.png and /dev/null differ
diff --git a/app/src/main/res/drawable/ic_cloud_download.xml b/app/src/main/res/drawable/ic_cloud_download.xml
new file mode 100644
index 0000000..ffd5f56
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cloud_download.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/selector_category_load.xml b/app/src/main/res/drawable/selector_category_load.xml
deleted file mode 100644
index ff1473f..0000000
--- a/app/src/main/res/drawable/selector_category_load.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/selector_category_unload.xml b/app/src/main/res/drawable/selector_category_unload.xml
deleted file mode 100644
index 15267c1..0000000
--- a/app/src/main/res/drawable/selector_category_unload.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_chapter.xml b/app/src/main/res/layout/item_chapter.xml
new file mode 100644
index 0000000..3f97495
--- /dev/null
+++ b/app/src/main/res/layout/item_chapter.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_book_detail_content.xml b/app/src/main/res/layout/layout_book_detail_content.xml
index a1d9970..364323c 100644
--- a/app/src/main/res/layout/layout_book_detail_content.xml
+++ b/app/src/main/res/layout/layout_book_detail_content.xml
@@ -64,7 +64,7 @@
android:id="@+id/book_detail_tv_catalog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="15dp"
+ android:padding="12dp"
android:text="最新章节"
android:textColor="@color/textPrimary"
android:textSize="15sp" />
@@ -73,8 +73,8 @@
android:id="@+id/book_detail_tv_catalog_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="right"
- android:padding="15dp"
+ android:gravity="end"
+ android:padding="12dp"
android:text="更多"
android:textColor="@color/textSecondary"
android:textSize="15sp" />
@@ -84,7 +84,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/book_detail_tv_catalog"
- android:foregroundGravity="center" />
+ android:foregroundGravity="center"
+ android:paddingHorizontal="4dp" />
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/version_code.properties b/app/version_code.properties
index 8a7c6ac..7ec427e 100644
--- a/app/version_code.properties
+++ b/app/version_code.properties
@@ -17,5 +17,5 @@
#
#Fri Jun 18 21:45:31 CST 2021
-VERSION_CODE=250
+VERSION_CODE=251
CREATE_RELEASE=true
diff --git a/dynamic/build.gradle b/dynamic/build.gradle
index 0e68753..587053c 100644
--- a/dynamic/build.gradle
+++ b/dynamic/build.gradle
@@ -152,7 +152,7 @@ dependencies {
compileOnly('org.apache.commons:commons-text:1.9')
//https://github.com/fengyuecanzhu/Maple
- compileOnly("me.fycz.maple:maple:1.9")
+ compileOnly("me.fycz.maple:maple:2.0")
compileOnly project(":app")
compileOnly project(":DialogX")
}
\ No newline at end of file
diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt b/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt
index 0fed8d4..33cdecd 100644
--- a/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt
+++ b/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt
@@ -57,6 +57,7 @@ class AppLoadImpl : IAppLoader {
App246Fix3::class.java,
App246Fix4::class.java,
App246Fix5::class.java,
+ App250Fix::class.java,
)
override fun onLoad(appParam: AppParam) {
diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix.kt b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix.kt
index 67ae22e..77d11f1 100644
--- a/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix.kt
+++ b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix.kt
@@ -39,7 +39,7 @@ import xyz.fycz.myreader.util.utils.ScreenUtils
* @author fengyue
* @date 2022/6/3 15:34
*/
-@AppFix([243, 244, 245, 246], ["[设置-缓存设置]新增清除广告文件"], "2022-06-03")
+@AppFix([243, 244, 245, 246, 250], ["[设置-缓存设置]新增清除广告文件"], "2022-06-03")
class App246Fix : AppFixHandle {
override fun onFix(key: String): BooleanArray {
diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix5.kt b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix5.kt
index 9d626b5..8c85034 100644
--- a/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix5.kt
+++ b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix5.kt
@@ -200,7 +200,8 @@ class App246Fix5 : AppFixHandle {
@Throws(IOException::class)
fun getBakUpdateInfo(): String {
return OkHttpUtils.getHtml(
- "https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/FYReader-Update/" +
+ //"https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/FYReader-Update/" +
+ "http://101.43.83.105:3000/fengyue/FYReader-Res/raw/branch/main/Plugin/" +
(if (App.isDebug()) "debug" else "release") +
"/content.txt"
)
diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/fix/App250Fix.kt b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App250Fix.kt
new file mode 100644
index 0000000..924e95c
--- /dev/null
+++ b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App250Fix.kt
@@ -0,0 +1,92 @@
+/*
+ * This file is part of FYReader.
+ * FYReader is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FYReader is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FYReader. If not, see .
+ *
+ * Copyright (C) 2020 - 2022 fengyuecanzhu
+ */
+
+package xyz.fycz.dynamic.fix
+
+import android.widget.TextView
+import androidx.viewbinding.ViewBinding
+import me.fycz.maple.MapleBridge
+import me.fycz.maple.MapleUtils
+import me.fycz.maple.MethodReplacement
+import xyz.fycz.myreader.greendao.DbManager
+import xyz.fycz.myreader.greendao.entity.Chapter
+import xyz.fycz.myreader.greendao.gen.ChapterDao
+import xyz.fycz.myreader.greendao.service.ChapterService
+import xyz.fycz.myreader.ui.activity.BookDetailedActivity
+
+/**
+ * @author fengyue
+ * @date 2022/8/11 16:44
+ */
+@AppFix(
+ [243, 244, 245, 246, 250],
+ ["[书籍详情界面]取消书籍简介展开时最大行数限制(无法显示完全)",
+ "修复从数据库中读取章节时部分数据项缺失的bug"],
+ "2022-08-11"
+)
+class App250Fix : AppFixHandle {
+ override fun onFix(key: String): BooleanArray {
+ return handleFix(
+ key,
+ "showMoreDesc" to { fxShowMoreDesc() },
+ "findBookAllChapterByBookId" to { fxFindBookAllChapterByBookId() },
+ )
+ }
+
+ private fun fxFindBookAllChapterByBookId() {
+ MapleUtils.findAndHookMethod(
+ ChapterService::class.java,
+ "findBookAllChapterByBookId",
+ String::class.java,
+ object : MethodReplacement() {
+ override fun replaceHookedMethod(param: MapleBridge.MethodHookParam): Any {
+ val bookId = param.args[0] as String?
+ if (bookId.isNullOrBlank()) {
+ return emptyList()
+ }
+ return DbManager.getDaoSession().chapterDao
+ .queryBuilder()
+ .where(ChapterDao.Properties.BookId.eq(bookId))
+ .orderAsc(ChapterDao.Properties.Number)
+ .list()
+ }
+ }
+ )
+ }
+
+ fun fxShowMoreDesc() {
+ MapleUtils.findAndHookMethod(
+ BookDetailedActivity::class.java,
+ "showMoreDesc",
+ object : MethodReplacement() {
+ override fun replaceHookedMethod(param: MapleBridge.MethodHookParam) {
+ val binding =
+ MapleUtils.getObjectField(param.thisObject, "binding") as ViewBinding
+ val icBinding = MapleUtils.getObjectField(binding, "ic") as ViewBinding
+ val bookDetailTvDesc =
+ MapleUtils.getObjectField(icBinding, "bookDetailTvDesc") as TextView
+ if (bookDetailTvDesc.maxLines == 5) {
+ bookDetailTvDesc.maxLines = Int.MAX_VALUE
+ } else {
+ bookDetailTvDesc.maxLines = 5
+ }
+ }
+ }
+ )
+ }
+}
\ No newline at end of file