Merge pull request #28 from fengyuecanzhu/dev

Merge dev to master
master
风月残烛 2 years ago committed by GitHub
commit d9963620a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 60
      .github/workflows/build.yml
  2. 7
      .gitignore
  3. 1
      .idea/gradle.xml
  4. 2
      .idea/misc.xml
  5. 1
      .idea/modules.xml
  6. 38
      README.md
  7. 61
      app/build.gradle
  8. 9
      app/release.md
  9. 16
      app/src/main/assets/updatelog.fy
  10. 10
      app/src/main/java/xyz/fycz/myreader/application/App.java
  11. 2
      app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
  12. 30
      app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java
  13. 36
      app/src/main/java/xyz/fycz/myreader/ui/dialog/UpdateDialog.java
  14. 13
      app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
  15. 24
      app/src/main/java/xyz/fycz/myreader/util/utils/PluginUtils.kt
  16. 53
      app/src/main/java/xyz/fycz/myreader/webapi/LanZouApi.kt
  17. 36
      app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java
  18. 3
      app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java
  19. 36
      app/src/main/res/layout/layout_about_content.xml
  20. 1
      app/src/main/res/values/strings.xml
  21. 3
      app/src/test/java/Test.java
  22. 2
      app/version_code.properties
  23. 2
      build.gradle
  24. 103
      dynamic/build.gradle
  25. 7
      dynamic/src/androidTest/java/xyz/fycz/dynamic/ExampleInstrumentedTest.kt
  26. 68
      dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt
  27. 24
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App243Fix.kt
  28. 14
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App244Fix.kt
  29. 14
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App244Fix2.kt
  30. 81
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App245Fix.kt
  31. 106
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix.kt
  32. 54
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix2.kt
  33. 139
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix3.kt
  34. 84
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix4.kt
  35. 208
      dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix5.kt
  36. 16
      dynamic/src/main/java/xyz/fycz/dynamic/fix/AppFixHandle.kt
  37. 57
      dynamic/src/main/java/xyz/fycz/dynamic/fix/AppSubSourceFix.kt
  38. 149
      dynamic/src/main/java/xyz/fycz/dynamic/utils/LanZouUtils.kt
  39. 1
      settings.gradle

@ -1,4 +1,4 @@
name: Auto build and delay
name: build
on:
push:
@ -59,47 +59,33 @@ jobs:
if [[ ${{ steps.config.outputs.need_create_release }} != 'true' ]];then
path="$GITHUB_WORKSPACE/app/build/outputs/apk/debug"
fi
files=$(ls $path)
for f in $files
do
if [[ $f == *"apk" ]]; then
file=$f
echo "[$(date -u -d '+8 hour' '+%Y.%m.%d %H:%M:%S')] 文件:$file"
break
fi
done
echo ::set-output name=file_name::"$file"
echo ::set-output name=file_path::"$path/$file"
- name: Upload App To Artifact
uses: actions/upload-artifact@v2
with:
name: FYReader-apk
path: ${{ steps.get_path.outputs.file_path }}
- name: Upload App To Lanzou
echo ::set-output name=file_path::"$path"
- name: Upload Lanzou
run: |
echo "上传APP至蓝奏云"
python3 $GITHUB_WORKSPACE/keystore/scripts/lzy.py "${{ steps.get_path.outputs.file_path }}" "${{ steps.config.outputs.lanzou_folder_id }}"
echo "[$(date -u -d '+8 hour' '+%Y.%m.%d %H:%M:%S')] 分享链接: ${{ steps.config.outputs.lanzou_share_url }}"
- name: Create Release
id: create_release
tag_name=${{ steps.config.outputs.version_name }}
if [ ${{ steps.config.outputs.need_create_release }} != 'true' -a ${{ github.ref }} != 'refs/heads/master' ];then
gitHead=$(git rev-parse --short HEAD)
tag_name=${{ steps.config.outputs.version_name }}-$gitHead
fi
python3 $GITHUB_WORKSPACE/keystore/scripts/lzy.py "${{ steps.get_path.outputs.file_path }}/风月读书$tag_name.apk" "${{ steps.config.outputs.lanzou_folder_id }}" "$tag_name"
# 发布正式版
- name: Release
if: ${{ steps.config.outputs.need_create_release == 'true' && github.ref == 'refs/heads/master' }}
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TZ: Asia/Shanghai
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.config.outputs.version_name }}
release_name: FYReader_${{ steps.config.outputs.version_name }}
body_path: ${{ github.workspace }}/app/release.md
body_path: ./app/release.md
prerelease: false
draft: false
- name: Upload Release Asset
id: upload_release_asset
if: ${{ steps.config.outputs.need_create_release == 'true' && steps.create_release.outputs.upload_url }}
uses: actions/upload-release-asset@v1
tag_name: ${{ steps.config.outputs.version_name }}
name: FYReader_${{ steps.config.outputs.version_name }}
files: |
${{ steps.get_path.outputs.file_path }}/风月读书${{ steps.config.outputs.version_name }}.apk
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 上传Artifact
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.get_path.outputs.file_path }}
asset_name: FYReader_${{ steps.config.outputs.version_name }}.apk
asset_content_type: application/vnd.android.package-archive
name: app
path: ${{ steps.get_path.outputs.file_path }}/风月读书*.apk

7
.gitignore vendored

@ -29,10 +29,3 @@ keystore
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
<<<<<<< HEAD
>>>>>>> 2c33f0b33a0411efd93a4810de5061a473835ff5
=======
>>>>>>> eee131cea39d27218fc7d1c3d641d617d377af42

@ -14,6 +14,7 @@
<option value="$PROJECT_DIR$/DialogX" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/dynamic" />
<option value="$PROJECT_DIR$/plugin" />
<option value="$PROJECT_DIR$/user" />
</set>
</option>

@ -3,6 +3,8 @@
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="..\:/android/FYReader/app/src/main/res/layout/activity_about.xml" value="0.2296195652173913" />
<entry key="..\:/android/FYReader/app/src/main/res/layout/activity_ad_setting.xml" value="0.12132725430597771" />
<entry key="..\:/android/FYReader/app/src/main/res/layout/activity_donate.xml" value="0.264" />
<entry key="..\:/android/FYReader/app/src/main/res/layout/activity_more_setting.xml" value="0.2" />
<entry key="..\:/android/FYReader/app/src/main/res/layout/activity_read.xml" value="0.12132725430597771" />

@ -15,6 +15,7 @@
<module fileurl="file://$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.androidTest.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.main.iml" filepath="$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.main.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.unitTest.iml" filepath="$PROJECT_DIR$/.idea/modules/dynamic/FYReader.dynamic.unitTest.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/plugin/FYReader.plugin.iml" filepath="$PROJECT_DIR$/.idea/modules/plugin/FYReader.plugin.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/user/FYReader.user.iml" filepath="$PROJECT_DIR$/.idea/modules/user/FYReader.user.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/user/FYReader.user.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/user/FYReader.user.androidTest.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/user/FYReader.user.main.iml" filepath="$PROJECT_DIR$/.idea/modules/user/FYReader.user.main.iml" />

@ -14,10 +14,10 @@
<table>
<tr>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/p1.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/p2.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/p3.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/p4.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/p1.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/p2.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/p3.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/p4.png" ></center></td>
</tr>
</table>
@ -62,9 +62,9 @@
<table>
<tr>
<td><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/Donate/wx_zsm.jpg" width=300>微信赞赏码</center></td>
<td><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/Donate/zfb_skm.jpg" width=300>支付宝收款码</center></td>
<td><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/Donate/qq_skm.jpg" width=300>QQ收款码</center></td>
<td><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/Donate/wx_zsm.jpg" width=300>微信赞赏码</center></td>
<td><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/Donate/zfb_skm.jpg" width=300>支付宝收款码</center></td>
<td><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/Donate/qq_skm.jpg" width=300>QQ收款码</center></td>
</tr>
</table>
@ -74,21 +74,21 @@
<table>
<tr>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/1.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/2.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/3.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/4.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/1.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/2.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/3.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/4.png" ></center></td>
</tr>
<tr>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/5.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/6.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/7.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/8.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/5.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/6.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/7.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/8.png" ></center></td>
</tr>
<tr>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/9.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/10.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/11.png" ></center></td>
<td ><center><img src="https://fyreader.coding.net/p/img/d/img/git/raw/master/FYReader/12.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/9.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/10.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/11.png" ></center></td>
<td ><center><img src="https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/img/FYReader/12.png" ></center></td>
</tr>
</table>

@ -20,54 +20,28 @@ apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'
apply plugin: 'kotlin-android'
import com.android.build.OutputFile
def releaseTime() {
return new Date().format("yy.MMddHH", TimeZone.getTimeZone("GMT+08:00"))
}
def commitId = 'git rev-parse --short HEAD'.execute().text.trim()
def getVersionCode() {
def getVersionC() {
def versionCodeFile = file('version_code.properties')
if (versionCodeFile.canRead()) {
Properties properties = new Properties()
properties.load(new FileInputStream(versionCodeFile))
def versionCode = properties['VERSION_CODE'].toInteger()//version_code.properties文件存放的版本号
def runTasks = gradle.startParameter.taskNames
def task = ':app:assembleRelease'
if (task in runTasks) {
properties['VERSION_CODE'] = (++versionCode).toString()
properties.store(versionCodeFile.newWriter(), null)
}
return versionCode
return properties['VERSION_CODE'].toInteger()
} else {
throw new FileNotFoundException("无法读取version_code.properties文件!")
}
}
def getVersionCodeNotIncrease() {
def versionCodeFile = file('version_code.properties')
if (versionCodeFile.canRead()) {
Properties properties = new Properties()
properties.load(new FileInputStream(versionCodeFile))
def versionCode = properties['VERSION_CODE'].toInteger()//version_code.properties文件存放的版本号
return versionCode
} else {
throw new FileNotFoundException("无法读取version_code.properties文件!")
return 200
}
}
def name = "风月读书"
def version = getVersionCode()
def getVersionName(){
def version = getVersionCodeNotIncrease()
def hun = (version / 100).toInteger()
def ten = (version / 10).toInteger() % 10
def one = version % 10
return hun + "." + ten + "." + one
}
def versionN = getVersionName()
def versionC = getVersionC()
def versionN = versionC.toString()
versionN = versionN[0] + "." + versionN[1] + "." + versionN[2]
// keystore.properties
def keyProps = new Properties()
@ -84,7 +58,7 @@ android {
applicationId "xyz.fycz.myreader"
minSdkVersion 21
targetSdkVersion 29
versionCode version
versionCode versionC
versionName versionN
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
@ -104,6 +78,15 @@ android {
viewBinding true
}
/*splits {
abi {
reset()
enable true
universalApk true // If true, also generate a universal APK
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}*/
buildTypes {
release {
minifyEnabled false
@ -136,6 +119,14 @@ android {
variant.outputs.all {
outputFileName = fileName
}
/*variant.outputs.each { output ->
def abi = output.getFilter(OutputFile.ABI)
if (abi == null) {
abi = "universal"
}
def fileName = "${name}v${defaultConfig.versionName}-${abi}.apk"
output.outputFileName = fileName
}*/
}
}
sourceSets.main {
@ -302,7 +293,7 @@ dependencies {
}
//https://github.com/fengyuecanzhu/Maple
implementation("me.fycz.maple:maple:1.8")
implementation("me.fycz.maple:maple:1.9")
}
greendao {

@ -1 +1,8 @@
* 1、修复书源过多时搜索界面卡死的问题
* 1、修复阅读界面概率性闪退的问题
* 2、关于界面新增插件加载结果
* 3、修复书源订阅失败的问题
* 4、修复字体下载失败的问题
* 5、\[设置-缓存设置\]新增清除广告文件
* 6、修复检查更新失败的问题
* 7、修复获取更新链接失败的问题
* 8、更新订阅书源链接

@ -1,3 +1,19 @@
2022.08.02
风月读书v2.4.7
1、修复阅读界面概率性闪退的问题
2、关于界面新增插件加载结果
3、修复书源订阅失败的问题
4、修复字体下载失败的问题
5、[设置-缓存设置]新增清除广告文件
6、修复检查更新失败的问题
7、修复获取更新链接失败的问题
8、更新订阅书源链接
2022.05.28
风月读书v2.4.6
更新内容:
1、修复书源过多时搜索界面卡死的问题
2022.05.22
风月读书v2.4.5
更新内容:

@ -282,15 +282,9 @@ public class App extends Application {
public static void checkVersionByServer(final AppCompatActivity activity, final boolean isManualCheck) {
App.getApplication().newThread(() -> {
try {
String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read";
if (debug) {
url = "https://shimo.im/docs/zfzpda7MUGskOC9v/read";
}
String html = OkHttpUtils.getHtml(url);
Document doc = Jsoup.parse(html);
String content = doc.getElementsByClass("ql-editor").text();
String content = OkHttpUtils.getUpdateInfo();
if (StringHelper.isEmpty(content)) {
content = OkHttpUtils.getUpdateInfo();
content = OkHttpUtils.getBakUpdateInfo();
if (StringHelper.isEmpty(content)) {
if (isManualCheck || NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showError("检查更新失败!");

@ -56,7 +56,7 @@ 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://fyreader.coding.net/p/img/d/Plugin/git/raw/master/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 String getDefaultDomain() {
return SharedPreUtils.getInstance().getString("domain", "fycz.me");

@ -23,6 +23,7 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
@ -42,6 +43,7 @@ import xyz.fycz.myreader.base.observer.MyObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.databinding.ActivityAboutBinding;
import xyz.fycz.myreader.entity.PluginConfig;
import xyz.fycz.myreader.model.user.UserService;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.LoadingDialog;
@ -53,6 +55,7 @@ import xyz.fycz.myreader.util.ZipUtils;
import xyz.fycz.myreader.util.utils.AdUtils;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.util.utils.PluginUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.webapi.LanZouApi;
@ -62,6 +65,8 @@ import xyz.fycz.myreader.webapi.LanZouApi;
*/
public class AboutActivity extends BaseActivity<ActivityAboutBinding> {
private PluginConfig pluginConfig;
@Override
protected void bindView() {
binding = ActivityAboutBinding.inflate(getLayoutInflater());
@ -75,11 +80,21 @@ public class AboutActivity extends BaseActivity<ActivityAboutBinding> {
getSupportActionBar().setTitle("关于");
}
@Override
protected void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
pluginConfig = PluginUtils.INSTANCE.getConfig();
}
@Override
protected void initWidget() {
super.initWidget();
binding.il.tvVersionName.setText(String.format("风月读书v%s", App.getStrVersionName()));
binding.il.tvPlugin.setText(PluginUtils.INSTANCE.getLoadSuccess() ?
getString(R.string.plugin_version,
pluginConfig != null ? pluginConfig.getVersion() : "插件加载失败")
: "插件加载失败"
);
binding.il.rlLanZou.setVisibility(App.isDebug() ? View.VISIBLE : View.GONE);
binding.il.rlResetPangle.setVisibility(App.isDebug() ? View.VISIBLE : View.GONE);
}
@ -87,6 +102,21 @@ public class AboutActivity extends BaseActivity<ActivityAboutBinding> {
@Override
protected void initClick() {
super.initClick();
binding.il.rlPlugin.setOnClickListener(v -> {
String tip = "";
if (pluginConfig == null) {
tip = "插件配置读取失败";
} else {
if (PluginUtils.INSTANCE.getLoadSuccess()) {
tip = PluginUtils.INSTANCE.getPluginLoadInfo();
} else {
tip = PluginUtils.INSTANCE.getErrorMsg();
}
}
DialogCreator.createTipDialog(this, binding.il.tvPlugin.getText().toString(), tip);
});
ClipboardManager mClipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
binding.il.rlUpdate.setOnClickListener(v -> App.checkVersionByServer(this, true));
binding.il.rlUpdateLog.setOnClickListener(v -> DialogCreator.createAssetTipDialog(this, "更新日志", "updatelog.fy"));

@ -18,10 +18,8 @@
package xyz.fycz.myreader.ui.dialog;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -37,14 +35,12 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.liulishuo.filedownloader.BaseDownloadTask;
import com.liulishuo.filedownloader.FileDownloadLargeFileListener;
@ -60,11 +56,12 @@ import java.util.List;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.observer.MyObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.databinding.FragmentUpdateBinding;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.webapi.LanZousApi;
import xyz.fycz.myreader.webapi.ResultCallback;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.webapi.LanZouApi;
public class UpdateDialog extends Fragment {
@ -290,13 +287,13 @@ public class UpdateDialog extends Fragment {
.setListener(new FileDownloadLargeFileListener() {
@Override
protected void pending(BaseDownloadTask task, long soFarBytes, long totalBytes) {
if (debug) Log.e("downloadApk", "pending-------");
if (debug) Log.d("downloadApk", "pending-------");
}
@Override
protected void progress(BaseDownloadTask task, long soFarBytes, long totalBytes) {
float percent = 1f * soFarBytes / totalBytes * 100;
if (debug) Log.e("downloadApk", "progress-------" + percent);
if (debug) Log.d("downloadApk", "progress-------" + percent);
if (percent >= 3) {
binding.barPercentView.setPercentage(percent);
binding.tvProgress.setText((int) percent + "%");
@ -305,12 +302,12 @@ public class UpdateDialog extends Fragment {
@Override
protected void paused(BaseDownloadTask task, long soFarBytes, long totalBytes) {
if (debug) Log.e("downloadApk", "paused-------");
if (debug) Log.d("downloadApk", "paused-------");
}
@Override
protected void completed(BaseDownloadTask task) {
if (debug) Log.e("downloadApk", "completed-------");
if (debug) Log.d("downloadApk", "completed-------");
binding.barPercentView.setPercentage(100);
binding.tvProgress.setText("100%");
install(new File(path), mActivity);
@ -375,20 +372,21 @@ public class UpdateDialog extends Fragment {
private void downloadWithLanzous(String apkUrl) {
binding.tvProgress.setText("正在获取下载链接...");
binding.barPercentView.setPercentage(0);
LanZousApi.getUrl(apkUrl, new ResultCallback() {
LanZouApi.INSTANCE.getFileUrl(apkUrl)
.compose(RxUtils::toSimpleSingle)
.subscribe(new MyObserver<String>() {
@Override
public void onFinish(Object o, int code) {
String downloadUrl = (String) o;
if (downloadUrl == null) {
App.runOnUiThread(() -> error());
return;
public void onNext(String directUrl) {
if (directUrl == null) {
error();
}else {
downloadApkNormal(directUrl);
}
App.runOnUiThread(() -> downloadApkNormal(downloadUrl));
}
@Override
public void onError(Exception e) {
App.runOnUiThread(() -> error());
public void onError(Throwable e) {
error();
}
});
}

@ -176,7 +176,7 @@ public class OkHttpUtils {
return response.body().byteStream();
}
public static String getUpdateInfo() throws IOException, JSONException {
public static String getUpdateInfo() throws IOException {
String key = "ryvwiq";
if (App.isDebug()) {
key = "sgak2h";
@ -197,6 +197,7 @@ public class OkHttpUtils {
if (body == null) {
return "";
} else {
try {
String bodyStr = new String(body.bytes(), StandardCharsets.UTF_8);
JSONObject jsonObj = new JSONObject(bodyStr);
jsonObj = jsonObj.getJSONObject("data");
@ -205,9 +206,19 @@ public class OkHttpUtils {
content = doc.text();
Log.d("Http -> UpdateInfo", content);
return content;
}catch (JSONException e){
e.printStackTrace();
return "";
}
}
}
public static String getBakUpdateInfo() throws IOException {
return OkHttpUtils.getHtml("https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/FYReader-Update/" +
(App.isDebug() ? "debug" : "release") +
"/content.txt");
}
public static Observable<StrResponse> getStrResponse(AnalyzeUrl analyzeUrl) {
return Observable.create(emitter -> {
StringBuilder sb = new StringBuilder();

@ -21,10 +21,8 @@ package xyz.fycz.myreader.util.utils
import android.content.Context
import android.util.Log
import dalvik.system.DexClassLoader
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import me.fycz.maple.MapleUtils
import xyz.fycz.dynamic.AppParam
import xyz.fycz.dynamic.IAppLoader
import xyz.fycz.myreader.application.App
@ -33,11 +31,9 @@ import xyz.fycz.myreader.common.URLCONST.DEFAULT_PLUGIN_CONFIG_URL
import xyz.fycz.myreader.entity.PluginConfig
import xyz.fycz.myreader.model.third3.Coroutine
import xyz.fycz.myreader.model.third3.http.getProxyClient
import xyz.fycz.myreader.model.third3.http.newCallResponse
import xyz.fycz.myreader.model.third3.http.newCallResponseBody
import xyz.fycz.myreader.model.third3.http.text
import xyz.fycz.myreader.util.SharedPreUtils
import xyz.fycz.myreader.util.ToastUtils
import java.io.File
import java.util.*
@ -49,12 +45,15 @@ import java.util.*
object PluginUtils {
val TAG = PluginUtils.javaClass.simpleName
private var appLoader: IAppLoader? = null
var config: PluginConfig? = null
var hasLoad = false
var loadSuccess = false
var errorMsg = ""
fun init() {
val pluginConfigUrl =
SharedPreUtils.getInstance().getString("pluginConfigUrl", DEFAULT_PLUGIN_CONFIG_URL)
var config: PluginConfig? = null
Coroutine.async {
val oldConfig = GSON.fromJsonObject<PluginConfig>(
SharedPreUtils.getInstance().getString("pluginConfig")
@ -108,7 +107,7 @@ object PluginUtils {
)
try {
val libClazz = dexClassLoader.loadClass("xyz.fycz.dynamic.AppLoadImpl")
val appLoader = libClazz.newInstance() as IAppLoader?
appLoader = libClazz.newInstance() as IAppLoader?
appLoader?.run {
val appParam = AppParam()
appParam.classLoader = context.classLoader
@ -116,13 +115,24 @@ object PluginUtils {
appParam.appInfo = context.applicationInfo
onLoad(appParam)
hasLoad = true
loadSuccess = true
}
} catch (e: Exception) {
e.printStackTrace()
errorMsg = e.stackTraceToString()
}
} else {
errorMsg = pluginPath + "文件不存在"
Log.d(TAG, pluginPath + "文件不存在")
}
}
}
fun getPluginLoadInfo(): String {
return try {
MapleUtils.callMethod(appLoader, "getPluginLoadInfo") as String
} catch (e: Exception) {
""
}
}
}

@ -97,9 +97,17 @@ object LanZouApi {
): Coroutine<List<LanZouFile>?> {
return Coroutine.async {
val params = if (page == 1) {
getFoldParams(getProxyClient().newCallResponseBody { url(foldUrl) }.text(), page, pwd)
getFoldParams(
getProxyClient().newCallResponseBody { url(foldUrl) }.text(),
page,
pwd
)
} else {
paramCathe[foldUrl] ?: getFoldParams(getProxyClient().newCallResponseBody { url(foldUrl) }.text(), page, pwd)
paramCathe[foldUrl] ?: getFoldParams(getProxyClient().newCallResponseBody {
url(
foldUrl
)
}.text(), page, pwd)
}
params["pg"] = page
paramCathe[foldUrl] = params
@ -158,12 +166,17 @@ object LanZouApi {
* @param url
* @param password
*/
private fun getFileUrl(url: String, password: String = ""): Observable<String> {
fun getFileUrl(url: String, password: String = ""): Observable<String> {
return Observable.create {
val html = OkHttpUtils.getHtml(url)
var html = OkHttpUtils.getHtml(url)
val url2 = if (password.isEmpty()) {
val url1 = getUrl1(html)
val key = getKey(OkHttpUtils.getHtml(url1))
html = OkHttpUtils.getHtml(url1)
val data = getDataString(html)
Log.d("LanZouUtils", "data:$data")
val key = getKeyValueByKey(html, data, "sign") +
"&" + getKeyValueByKey(html, data, "websignkey")
Log.d("LanZouUtils", "key:$key")
getUrl2(key, url1)
} else {
getUrl2(StringHelper.getSubString(html, "sign=", "&"), url, password)
@ -177,27 +190,31 @@ object LanZouApi {
}
}
private fun getUrl1(html: String): String {
fun getDataString(html: String): String {
val start = html.lastIndexOf("data :") + "data :".length
val end = html.indexOf("},", start) + 1
return html.substring(start, end)
}
fun getUrl1(html: String): String {
val doc = Jsoup.parse(html)
return URLCONST.LAN_ZOU_URL + doc.getElementsByTag("iframe").attr("src")
}
private fun getKey(html: String): String {
var lanzousKeyStart = "var pposturl = '"
val keyName = StringHelper.getSubString(html, "'sign':", ",")
lanzousKeyStart = if (keyName.endsWith("'")) {
"'sign':'"
fun getKeyValueByKey(html: String, data: String, key: String): String {
val keyName = StringHelper.getSubString(data, "'$key':", ",")
return if (keyName.endsWith("'")) {
key + "=" + keyName.replace("'", "")
} else {
"var $keyName = '"
val lanzousKeyStart = "var $keyName = '"
key + "=" + StringHelper.getSubString(html, lanzousKeyStart, "'")
}
return StringHelper.getSubString(html, lanzousKeyStart, "'")
}
private fun getUrl2(key: String, referer: String, password: String = ""): String {
fun getUrl2(key: String, referer: String, password: String = ""): String {
val mediaType = "application/x-www-form-urlencoded".toMediaTypeOrNull()
val body = if (password.isEmpty()) {
"action=downprocess&sign=$key&ves=1"
"action=downprocess&signs=?ctdf&websign=&ves=1&$key"
} else {
"action=downprocess&sign=$key&p=$password"
}
@ -213,7 +230,7 @@ object LanZouApi {
return getUrl2(html)
}
private fun getUrl2(o: String): String {
fun getUrl2(o: String): String {
/*val info = o.split(",").toTypedArray()
val zt = info[0].substring(info[0].indexOf(":") + 1)
if (!"1".endsWith(zt)) {
@ -239,7 +256,7 @@ object LanZouApi {
*
* @param path
*/
private fun getRedirectUrl(path: String): String {
fun getRedirectUrl(path: String): String {
val conn = URL(path)
.openConnection() as HttpURLConnection
conn.instanceFollowRedirects = false

@ -32,6 +32,7 @@ import io.reactivex.SingleOnSubscribe;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.observer.MyObserver;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.util.help.StringHelper;
@ -51,40 +52,23 @@ public class LanZousApi {
* @param callback
*/
public static void getUrl(final String lanZouUrl, final ResultCallback callback) {
LanZousApi.getUrl1(lanZouUrl, new ResultCallback() {
@Override
public void onFinish(final Object o, int code) {
LanZousApi.getKey((String) o, new ResultCallback() {
final String referer = (String) o;
@Override
public void onFinish(Object o, int code) {
LanZousApi.getUrl2((String) o, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
LanZousApi.getRedirectUrl((String) o, callback);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
}, referer);
getUrl(lanZouUrl, "", callback);
}
public static void getUrl(final String lanZouUrl, String password, final ResultCallback callback) {
LanZouApi.INSTANCE.getFileUrl(lanZouUrl, "")
.compose(RxUtils::toSimpleSingle)
.subscribe(new MyObserver<String>() {
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
public void onNext(String s) {
callback.onFinish(s, 1);
}
@Override
public void onError(Exception e) {
callback.onError(e);
public void onError(Throwable e) {
callback.onError((Exception) e);
}
});
}

@ -491,9 +491,10 @@ public class PageView extends View {
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mPageAnim != null) {
mPageAnim.abortAnim();
mPageAnim.clear();
}
mPageLoader = null;
mPageAnim = null;
}

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ 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
@ -55,6 +54,37 @@
</LinearLayout>
<RelativeLayout
android:id="@+id/rl_plugin"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:gravity="center"
android:paddingStart="20dp"
android:paddingEnd="10dp">
<TextView
android:id="@+id/tv_plugin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/plugin_version"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_normal_size" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
app:srcCompat="@drawable/ic_right_arrow"
app:tint="@color/textSecondary" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp" />
<RelativeLayout
android:id="@+id/rl_update"
@ -355,6 +385,7 @@
<View
android:layout_width="match_parent"
android:layout_height="10dp" />
<RelativeLayout
android:id="@+id/rl_lan_zou"
android:layout_width="match_parent"
@ -380,6 +411,7 @@
app:srcCompat="@drawable/ic_right_arrow"
app:tint="@color/textSecondary" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_reset_pangle"
android:layout_width="match_parent"

@ -123,6 +123,7 @@
<string name="git_hub">GitHub</string>
<string name="share_log">分享崩溃日志</string>
<string name="this_github_url">https://github.com/fengyuecanzhu/FYReader</string>
<string name="plugin_version">插件版本:%s</string>
<string name="update">检查更新</string>
<string name="thanks">致谢名单</string>
<string name="join_qq_channel">加入QQ频道</string>

@ -21,6 +21,7 @@ import org.seimicrawler.xpath.JXDocument;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import xyz.fycz.myreader.model.sourceAnalyzer.BaseAnalyzer;
@ -135,7 +136,7 @@ public class Test {
@org.junit.Test
public void md5(){
File file = new File("D:\\Java\\AndroidSdk\\build-tools\\29.0.3\\dynamic_v1.0.5.dex");
File file = new File("D:\\Java\\AndroidSdk\\build-tools\\29.0.3\\dynamic_v1.0.6.dex");
System.out.println(MD5Utils.INSTANCE.getFileMD5s(file, 32));
}
}

@ -17,5 +17,5 @@
#
#Fri Jun 18 21:45:31 CST 2021
VERSION_CODE=246
VERSION_CODE=247
CREATE_RELEASE=true

@ -33,7 +33,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.github.megatronking.stringfog:gradle-plugin:3.0.0'

@ -48,12 +48,111 @@ android {
}
dependencies {
compileOnly("androidx.core:core-ktx:$kotlin_version")
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
compileOnly("me.fycz.maple:maple:1.7")
// androidx
compileOnly "androidx.core:core-ktx:$kotlin_version"
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compileOnly 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
compileOnly 'androidx.appcompat:appcompat:1.3.1'
compileOnly 'androidx.constraintlayout:constraintlayout:2.1.3'
//anko
def anko_version = '0.10.8'
compileOnly "org.jetbrains.anko:anko-sdk27:$anko_version"
compileOnly "org.jetbrains.anko:anko-sdk27-listeners:$anko_version"
//Glide
compileOnly 'com.github.bumptech.glide:glide:4.13.1'
compileOnly 'com.squareup.okhttp3:okhttp:4.9.3'
compileOnly 'com.google.code.gson:gson:2.9.0'
compileOnly 'com.journeyapps:zxing-android-embedded:3.5.0'
compileOnly 'org.greenrobot:greendao:3.3.0'
compileOnly 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'
//JSoup
compileOnly 'org.jsoup:jsoup:1.14.3'
compileOnly 'cn.wanghaomiao:JsoupXpath:2.5.1'
compileOnly 'com.jayway.jsonpath:json-path:2.7.0'
//SmartRefreshLayout
compileOnly 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.2'
compileOnly 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.2'
compileOnly 'com.google.android.material:material:1.4.0'
//Scroller
compileOnly 'com.futuremind.recyclerfastscroll:fastscroll:0.2.5'
//Toasty
compileOnly 'com.github.GrenderG:Toasty:1.5.0'
//
compileOnly 'net.ricecode:string-similarity:1.0.0'
compileOnly 'com.jayway.jsonpath:json-path:2.7.0'
//RxAndroid
compileOnly 'io.reactivex.rxjava2:rxjava:2.2.19'
compileOnly 'io.reactivex.rxjava2:rxandroid:2.1.1'
//ImmersionBar
compileOnly 'com.gyf.immersionbar:immersionbar:3.0.0'
//
compileOnly 'com.luhuiguo:chinese-utils:1.0'
//
compileOnly 'com.jaredrummler:colorpicker:1.1.0'
//
compileOnly 'cn.bingoogolapple:bga-qrcode-zxing:1.3.7'
//
compileOnly 'com.github.albfernandez:juniversalchardet:2.4.0'
// https://github.com/hongyangAndroid/FlowLayout
compileOnly 'com.hyman:flowlayout-lib:1.1.2'
compileOnly 'com.liulishuo.filedownloader:library:1.7.7'
//SwipeBackLayout
compileOnly 'me.imid.swipebacklayout.lib:library:1.1.0'
//JS
//noinspection GradleDependency
compileOnly 'com.github.gedoor:rhino-android:1.6'
//XXPermissions
compileOnly 'com.github.getActivity:XXPermissions:11.2'
//epub
compileOnly('com.positiondev.epublib:epublib-core:3.1') {
exclude group: 'org.slf4j'
exclude group: 'xmlpull'
}
compileOnly 'com.nshmura:recyclertablayout:1.5.0'
// https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j
compileOnly group: 'net.lingala.zip4j', name: 'zip4j', version: '2.9.1'
//
def coroutines_version = '1.5.1'
compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version")
//apache
compileOnly('org.apache.commons:commons-text:1.9')
//https://github.com/fengyuecanzhu/Maple
compileOnly("me.fycz.maple:maple:1.9")
compileOnly project(":app")
compileOnly project(":DialogX")
}

@ -18,14 +18,14 @@
package xyz.fycz.dynamic
import android.util.Base64
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import me.fycz.maple.MapleUtils
import org.junit.Test
import org.junit.runner.RunWith
import xyz.fycz.dynamic.fix.App243Fix
import xyz.fycz.myreader.application.App
/**
* Instrumented test, which will execute on an Android device.
@ -37,5 +37,6 @@ class ExampleInstrumentedTest {
@Test
fun testFix() {
// Context of the app under test.
Log.d("asdas", Base64.encodeToString("asdasd".toByteArray(), Base64.NO_WRAP))
}
}

@ -18,7 +18,6 @@
package xyz.fycz.dynamic
import android.app.AlertDialog
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
@ -28,7 +27,11 @@ import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodHook
import xyz.fycz.dynamic.fix.*
import xyz.fycz.myreader.application.App
import xyz.fycz.myreader.entity.PluginConfig
import xyz.fycz.myreader.ui.activity.MainActivity
import xyz.fycz.myreader.util.SharedPreUtils
import xyz.fycz.myreader.util.utils.GSON
import xyz.fycz.myreader.util.utils.fromJsonObject
/**
* @author fengyue
@ -40,41 +43,40 @@ class AppLoadImpl : IAppLoader {
private const val spuName = "FYReader_plugin"
val spu: SharedPreferences =
App.getmContext().getSharedPreferences(spuName, Context.MODE_PRIVATE)
val allFixInfoSb = StringBuilder()
}
private val fixList = listOf(
App243Fix::class.java,
App244Fix::class.java,
App244Fix2::class.java,
App245Fix::class.java,
App246Fix::class.java,
AppSubSourceFix::class.java,
App246Fix2::class.java,
App246Fix3::class.java,
App246Fix4::class.java,
App246Fix5::class.java,
)
override fun onLoad(appParam: AppParam) {
val sb = StringBuilder()
fixList.forEach {
val annotation = it.getAnnotation(AppFix::class.java)!!
if (annotation.versions.isEmpty()) {
fix(sb, annotation, it)
} else {
for (version in annotation.versions) {
if (App.getVersionCode() == version) {
val fix = it.newInstance()
val fixResult = fix.onFix(annotation.date)
if (!spu.getBoolean(annotation.date, false)) {
if (sb.isNotEmpty()) sb.append("\n")
sb.append("${annotation.date}\n")
fixResult.forEachIndexed { i, b ->
sb.append("${i + 1}${annotation.fixLog[i]}${if (b) "成功" else "失败"}\n")
}
spu.edit().run {
putBoolean(annotation.date, true)
apply()
}
}
fix(sb, annotation, it)
break
}
}
}
}
if (sb.isNotEmpty()) {
if (sb.endsWith("\n")) sb.substring(0, sb.length - 1)
val key = "fix244-2"
val key = "fix2022-07-02"
val hasRead = spu.getBoolean(key, false)
if (!hasRead) {
announce("插件更新", "更新内容:\n$sb")
@ -86,6 +88,33 @@ class AppLoadImpl : IAppLoader {
}
}
private fun fix(
sb: StringBuilder,
annotation: AppFix,
fixClz: Class<out AppFixHandle>
) {
val fix = fixClz.newInstance()
val fixResult = fix.onFix(annotation.date)
//所有修复结果信息
if (allFixInfoSb.isNotEmpty()) allFixInfoSb.append("\n")
allFixInfoSb.append("${annotation.date}\n")
fixResult.forEachIndexed { i, b ->
allFixInfoSb.append("${i + 1}${annotation.fixLog[i]}${if (b) "成功" else "失败"}\n")
}
//需要提示的修复结果信息
if (!spu.getBoolean(annotation.date, false)) {
if (sb.isNotEmpty()) sb.append("\n")
sb.append("${annotation.date}\n")
fixResult.forEachIndexed { i, b ->
sb.append("${i + 1}${annotation.fixLog[i]}${if (b) "成功" else "失败"}\n")
}
spu.edit().run {
putBoolean(annotation.date, true)
apply()
}
}
}
private fun announce(title: String, msg: String) {
try {
MapleUtils.findAndHookMethod(
@ -105,4 +134,11 @@ class AppLoadImpl : IAppLoader {
MapleUtils.log(e)
}
}
fun getPluginLoadInfo(): String {
val pluginConfig = GSON.fromJsonObject<PluginConfig>(
SharedPreUtils.getInstance().getString("pluginConfig")
) ?: PluginConfig("dynamic.dex", 100)
return "当前版本更新日志:\n${pluginConfig.changelog}\n\n插件加载结果:\n" + allFixInfoSb.toString()
}
}

@ -39,25 +39,11 @@ import xyz.fycz.myreader.util.utils.AdUtils
class App243Fix : AppFixHandle {
override fun onFix(key: String): BooleanArray {
var fx1 = false
var fx2 = false
try {
fixGetAllNoLocalSource()
fx1 = true
fixResult(key, "getAllNoLocalSource", true)
} catch (e: Exception) {
MapleUtils.log(e)
fixResult(key, "getAllNoLocalSource", false)
}
try {
fixAdTimeout()
fx2 = true
fixResult(key, "adTimeout", true)
} catch (e: Exception) {
MapleUtils.log(e)
fixResult(key, "adTimeout", false)
}
return booleanArrayOf(fx1, fx2)
return handleFix(
key,
"getAllNoLocalSource" to { fixGetAllNoLocalSource() },
"adTimeout" to { fixAdTimeout() },
)
}
private fun getAllNoLocalSource(): List<BookSource> {

@ -36,16 +36,10 @@ import java.io.*
@AppFix([243, 244], ["修复书籍无法导出缓存的问题"], "2022-04-26")
class App244Fix: AppFixHandle {
override fun onFix(key: String): BooleanArray {
var fx = false
try {
fixUnionChapterCathe()
fx = true
fixResult(key, "unionChapterCathe", true)
} catch (e: Exception) {
MapleUtils.log(e)
fixResult(key, "unionChapterCathe", false)
}
return booleanArrayOf(fx)
return handleFix(
key,
"unionChapterCathe" to { fixUnionChapterCathe() },
)
}
private fun fixUnionChapterCathe() {

@ -32,16 +32,10 @@ import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler
@AppFix([243, 244], ["修复搜索时当前分组不存在时无法搜索的问题"], "2022-05-11")
class App244Fix2 : AppFixHandle{
override fun onFix(key: String): BooleanArray {
var fx = false
try {
fixGetEnableReadCrawlers()
fx = true
fixResult(key, "getEnableReadCrawlers", true)
} catch (e: Exception) {
MapleUtils.log(e)
fixResult(key, "getEnableReadCrawlers", false)
}
return booleanArrayOf(fx)
return handleFix(
key,
"getEnableReadCrawlers" to { fixGetEnableReadCrawlers() },
)
}
private fun fixGetEnableReadCrawlers() {

@ -1,81 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.fix
import me.fycz.maple.MapleBridge
import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodReplacement
import xyz.fycz.myreader.application.App
import xyz.fycz.myreader.model.user.UserService
import xyz.fycz.myreader.util.AppInfoUtils
import xyz.fycz.myreader.util.utils.EncoderUtils
/**
* @author fengyue
* @date 2022/5/23 18:20
*/
@AppFix([243, 244, 245], ["修改用户服务验证机制(beta)"], "2022-05-23")
class App245Fix : AppFixHandle {
override fun onFix(key: String): BooleanArray {
val result = try {
fixMakeAuth()
true
} catch (e: Exception) {
MapleUtils.log(e)
false
}
fixResult(key, "makeAuth", result)
return booleanArrayOf(result)
}
private fun fixMakeAuth() {
MapleUtils.findAndHookMethod(
UserService::class.java,
"makeAuth",
object : MethodReplacement() {
override fun replaceHookedMethod(param: MapleBridge.MethodHookParam): Any {
return makeAuth()
}
}
)
}
fun makeAuth(): String {
var auth = "signal=" + AppInfoUtils.getSingInfo(
App.getmContext(),
App.getApplication().packageName,
AppInfoUtils.SHA1
) + "&appVersion=" + App.getVersionCode()
auth = try {
EncoderUtils.encryptAES2Base64(
auth.toByteArray(), DO_FILTER_KEY,
"AES/ECB/PKCS5Padding"
)?.let { String(it) }.toString()
} catch (e: Exception) {
""
}
return "&auth=$auth" +
"&deviceId=" + UserService.getUUID() +
"&isDebug=" + App.isDebug()
}
val DO_FILTER_KEY = "79qdunN8534y44T3".toByteArray()
}

@ -0,0 +1,106 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.fix
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.get
import androidx.viewbinding.ViewBinding
import me.fycz.maple.MapleBridge
import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodHook
import xyz.fycz.myreader.R
import xyz.fycz.myreader.base.adapter2.onClick
import xyz.fycz.myreader.ui.activity.MoreSettingActivity
import xyz.fycz.myreader.util.ToastUtils
import xyz.fycz.myreader.util.utils.FileUtils
import xyz.fycz.myreader.util.utils.ScreenUtils
/**
* @author fengyue
* @date 2022/6/3 15:34
*/
@AppFix([243, 244, 245, 246], ["[设置-缓存设置]新增清除广告文件"], "2022-06-03")
class App246Fix : AppFixHandle {
override fun onFix(key: String): BooleanArray {
return handleFix(
key,
"adFile" to { fxAdFile() },
)
}
fun fxAdFile() {
MapleUtils.findAndHookMethod(
MoreSettingActivity::class.java,
"initWidget",
object : MethodHook() {
override fun afterHookedMethod(param: MapleBridge.MethodHookParam) {
val binding =
MapleUtils.getObjectField(param.thisObject, "binding") as ViewBinding
val rootLayout =
(MapleUtils.getObjectField(
binding,
"svContent"
) as ScrollView)[0] as ViewGroup
addDeleteAdFileView(rootLayout, rootLayout.childCount)
}
}
)
}
fun addDeleteAdFileView(rootLayout: ViewGroup, index: Int) {
val context = rootLayout.context
val resources = context.resources
val rlDeleteAdFile = RelativeLayout(context)
val layoutParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ScreenUtils.dpToPx(50)
)
rlDeleteAdFile.setPadding(
ScreenUtils.dpToPx(20),
0,
ScreenUtils.dpToPx(20),
0
)
rlDeleteAdFile.background =
ContextCompat.getDrawable(context, R.drawable.selector_common_bg)
rlDeleteAdFile.id = R.id.rl_delete_ad_file
val textview = TextView(context)
val textViewParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
addRule(RelativeLayout.CENTER_VERTICAL)
}
textview.setText(R.string.delete_ad_file)
textview.setTextColor(resources.getColor(R.color.textSecondary))
textview.textSize =
ScreenUtils.pxToSp(resources.getDimension(R.dimen.text_normal_size).toInt()).toFloat()
rlDeleteAdFile.addView(textview, textViewParams)
rlDeleteAdFile.onClick {
FileUtils.deleteFile(FileUtils.getFilePath())
ToastUtils.showSuccess("广告文件删除成功")
}
rootLayout.addView(rlDeleteAdFile, index, layoutParams)
}
}

@ -0,0 +1,54 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.fix
import me.fycz.maple.MapleBridge
import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodHook
import xyz.fycz.myreader.widget.page.PageView
/**
* @author fengyue
* @date 2022/6/23 20:51
*/
@AppFix([243, 244, 245, 246], ["修复阅读界面概率性闪退的问题"], "2022-06-23")
class App246Fix2 : AppFixHandle {
override fun onFix(key: String): BooleanArray {
return handleFix(
key,
"pageView" to { fxPageView() },
)
}
private fun fxPageView() {
MapleUtils.findAndHookMethod(
PageView::class.java,
"onDetachedFromWindow",
object : MethodHook(){
override fun beforeHookedMethod(param: MapleBridge.MethodHookParam) {
val mPageAnim = MapleUtils.getObjectField(param.thisObject, "mPageAnim")
if (mPageAnim == null){
MapleUtils.setObjectField(param.thisObject, "mPageLoader", null)
param.result = null
}
}
}
)
}
}

@ -0,0 +1,139 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.fix
import android.annotation.SuppressLint
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
import androidx.core.view.get
import androidx.core.widget.NestedScrollView
import androidx.viewbinding.ViewBinding
import me.fycz.maple.MapleBridge
import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodHook
import xyz.fycz.dynamic.AppLoadImpl
import xyz.fycz.myreader.R
import xyz.fycz.myreader.base.adapter2.onClick
import xyz.fycz.myreader.entity.PluginConfig
import xyz.fycz.myreader.ui.activity.AboutActivity
import xyz.fycz.myreader.ui.dialog.DialogCreator
import xyz.fycz.myreader.util.SharedPreUtils
import xyz.fycz.myreader.util.utils.GSON
import xyz.fycz.myreader.util.utils.ScreenUtils
import xyz.fycz.myreader.util.utils.fromJsonObject
/**
* @author fengyue
* @date 2022/6/28 19:22
*/
@AppFix([243, 244, 245, 246], ["关于界面新增插件加载结果"], "2022-06-28")
class App246Fix3 : AppFixHandle {
override fun onFix(key: String): BooleanArray {
return handleFix(
key,
"pluginView" to { fxPluginView() },
)
}
private fun fxPluginView() {
MapleUtils.findAndHookMethod(
AboutActivity::class.java,
"initWidget",
object : MethodHook() {
override fun afterHookedMethod(param: MapleBridge.MethodHookParam) {
val binding =
MapleUtils.getObjectField(param.thisObject, "binding") as ViewBinding
val ilBinding = MapleUtils.getObjectField(binding, "il") as ViewBinding
val rootLayout = (ilBinding.root as NestedScrollView)[0] as ViewGroup
addPluginView(rootLayout, 1)
}
}
)
}
@SuppressLint("SetTextI18n")
fun addPluginView(rootLayout: ViewGroup, index: Int) {
val pluginConfig = GSON.fromJsonObject<PluginConfig>(
SharedPreUtils.getInstance().getString("pluginConfig")
) ?: PluginConfig("dynamic.dex", 100)
val context = rootLayout.context
val rlPlugin = RelativeLayout(context)
val pluginLayoutParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ScreenUtils.dpToPx(50)
)
rlPlugin.background = ContextCompat.getDrawable(context, R.drawable.selector_common_bg)
rlPlugin.gravity = Gravity.CENTER
rlPlugin.id = R.id.rl_update
rlPlugin.setPadding(
ScreenUtils.dpToPx(20),
0,
ScreenUtils.dpToPx(10),
0
)
val textview = TextView(context)
val textviewLayoutParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
addRule(RelativeLayout.CENTER_VERTICAL)
}
textview.text = "插件版本:${pluginConfig.version}"
textview.setTextColor(context.resources.getColor(R.color.textPrimary))
textview.textSize =
ScreenUtils.pxToSp(
context.resources.getDimension(R.dimen.text_normal_size).toInt()
).toFloat()
rlPlugin.addView(textview, textviewLayoutParams)
val imageView = AppCompatImageView(context)
val imageViewLayoutParams = RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
addRule(RelativeLayout.ALIGN_PARENT_END)
addRule(RelativeLayout.CENTER_VERTICAL)
}
imageView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_right_arrow))
imageView.drawable.setTint(context.resources.getColor(R.color.textPrimary))
rlPlugin.addView(imageView, imageViewLayoutParams)
rlPlugin.onClick {
DialogCreator.createTipDialog(
context,
"插件版本:${pluginConfig.version}",
"当前版本更新日志:\n${pluginConfig.changelog}\n\n插件加载结果:\n" +
AppLoadImpl.allFixInfoSb.toString()
)
}
rootLayout.addView(rlPlugin, index, pluginLayoutParams)
val view = View(context)
val layoutParams = ViewGroup.MarginLayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ScreenUtils.dpToPx(10)
)
rootLayout.addView(view, index + 1, layoutParams)
}
}

@ -0,0 +1,84 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.fix
import me.fycz.maple.MapleBridge
import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodReplacement
import xyz.fycz.dynamic.utils.LanZouUtils
import xyz.fycz.myreader.base.observer.MyObserver
import xyz.fycz.myreader.util.utils.RxUtils
import xyz.fycz.myreader.webapi.LanZouApi
import xyz.fycz.myreader.webapi.LanZousApi
import xyz.fycz.myreader.webapi.ResultCallback
import java.lang.Exception
/**
* @author fengyue
* @date 2022/6/30 20:40
*/
@AppFix([243, 244, 245, 246], ["修复书源订阅失败的问题", "修复字体下载失败的问题"], "2022-06-30")
class App246Fix4: AppFixHandle {
override fun onFix(key: String): BooleanArray {
return handleFix(
key,
"lanZouApi" to { fxLanZouApi() },
"fontLanZouApi" to { fxFontLanZouApi() },
)
}
private fun fxLanZouApi() {
MapleUtils.findAndHookMethod(
LanZouApi::class.java,
"getFileUrl",
String::class.java,
String::class.java,
object : MethodReplacement(){
override fun replaceHookedMethod(param: MapleBridge.MethodHookParam): Any {
return LanZouUtils.getFileUrl(param.args[0] as String, param.args[1] as String)
}
}
)
}
private fun fxFontLanZouApi(){
MapleUtils.findAndHookMethod(
LanZousApi::class.java,
"getUrl",
String::class.java,
ResultCallback::class.java,
object : MethodReplacement(){
override fun replaceHookedMethod(param: MapleBridge.MethodHookParam) {
val callback = param.args[1] as ResultCallback
LanZouUtils.getFileUrl(param.args[0] as String)
.compose { RxUtils.toSimpleSingle(it) }
.subscribe(object : MyObserver<String>(){
override fun onNext(t: String) {
callback.onFinish(t, 1)
}
override fun onError(e: Throwable) {
callback.onError(e as Exception)
}
})
}
}
)
}
}

@ -0,0 +1,208 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.fix
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import io.reactivex.Observable
import me.fycz.maple.MapleBridge
import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodReplacement
import xyz.fycz.myreader.R
import xyz.fycz.myreader.application.App
import xyz.fycz.myreader.application.SysManager
import xyz.fycz.myreader.base.observer.MyObserver
import xyz.fycz.myreader.common.URLCONST
import xyz.fycz.myreader.ui.dialog.UpdateDialog
import xyz.fycz.myreader.util.SharedPreUtils
import xyz.fycz.myreader.util.ToastUtils
import xyz.fycz.myreader.util.help.StringHelper
import xyz.fycz.myreader.util.utils.NetworkUtils
import xyz.fycz.myreader.util.utils.OkHttpUtils
import xyz.fycz.myreader.util.utils.RxUtils
import xyz.fycz.myreader.webapi.LanZouApi.getFileUrl
import xyz.fycz.myreader.widget.BarPercentView
import java.io.IOException
/**
* @author fengyue
* @date 2022/7/1 15:45
*/
@AppFix([243, 244, 245, 246], ["修复检查更新失败的问题", "修复获取更新链接失败的问题"], "2022-07-02")
class App246Fix5 : AppFixHandle {
override fun onFix(key: String): BooleanArray {
return handleFix(
key,
"checkUpdateUrl" to { fxCheckUpdateUrl() },
"downloadLanzou" to { fxDownloadLanzou() },
)
}
fun fxCheckUpdateUrl() {
MapleUtils.findAndHookMethod(
App::class.java,
"checkVersionByServer",
AppCompatActivity::class.java,
Boolean::class.java,
object : MethodReplacement() {
override fun replaceHookedMethod(param: MapleBridge.MethodHookParam) {
checkUpdate(param.args[0] as AppCompatActivity, param.args[1] as Boolean)
}
}
)
}
fun checkUpdate(activity: AppCompatActivity, isManualCheck: Boolean) {
App.getApplication().newThread {
try {
var content = OkHttpUtils.getUpdateInfo()
if (StringHelper.isEmpty(content)) {
content = getBakUpdateInfo()
if (StringHelper.isEmpty(content)) {
if (isManualCheck || NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showError("检查更新失败!")
}
return@newThread
}
}
val contents = content.split(";".toRegex())
val newestVersion = contents[0].substring(contents[0].indexOf(":") + 1).toInt()
var isForceUpdate = contents[1].substring(contents[1].indexOf(":") + 1).toBoolean()
val downloadLink =
contents[2].substring(contents[2].indexOf(":") + 1).trim { it <= ' ' }
val updateContent = contents[3].substring(contents[3].indexOf(":") + 1)
SharedPreUtils.getInstance().putString(
App.getmContext().getString(R.string.lanzousKeyStart),
contents[4].substring(contents[4].indexOf(":") + 1)
)
val newSplashTime = contents[5].substring(contents[5].indexOf(":") + 1)
val oldSplashTime = SharedPreUtils.getInstance().getString("splashTime")
SharedPreUtils.getInstance()
.putBoolean("needUdSI", oldSplashTime != newSplashTime)
SharedPreUtils.getInstance().putString(
"splashTime",
contents[5].substring(contents[5].indexOf(":") + 1)
)
SharedPreUtils.getInstance().putString(
"splashImageUrl",
contents[6].substring(contents[6].indexOf(":") + 1)
)
SharedPreUtils.getInstance().putString(
"splashImageMD5",
contents[7].substring(contents[7].indexOf(":") + 1)
)
val forceUpdateVersion = contents[8].substring(contents[8].indexOf(":") + 1).toInt()
SharedPreUtils.getInstance().putInt("forceUpdateVersion", forceUpdateVersion)
val domain = contents[9].substring(contents[9].indexOf(":") + 1)
SharedPreUtils.getInstance().putString("domain", domain)
val pluginConfigUrl = contents[10].substring(contents[10].indexOf(":") + 1)
SharedPreUtils.getInstance().putString("pluginConfigUrl", pluginConfigUrl)
val versionCode = App.getVersionCode()
isForceUpdate = isForceUpdate && forceUpdateVersion > versionCode
if (!StringHelper.isEmpty(downloadLink)) {
SharedPreUtils.getInstance()
.putString(App.getmContext().getString(R.string.downloadLink), downloadLink)
} else {
SharedPreUtils.getInstance().putString(
App.getmContext().getString(R.string.downloadLink),
URLCONST.APP_DIR_URL
)
}
val updateContents = updateContent.split("/".toRegex())
val s = StringBuilder()
updateContents.forEach {
s.append(it)
s.append("<br>")
}
Log.i("检查更新,最新版本", newestVersion.toString() + "")
if (newestVersion > versionCode) {
val setting = SysManager.getSetting()
if (isManualCheck || setting.newestVersionCode < newestVersion || isForceUpdate) {
setting.newestVersionCode = newestVersion
SysManager.saveSetting(setting)
App.getApplication().updateApp2(
activity, downloadLink, newestVersion, s.toString(), isForceUpdate
)
}
} else if (isManualCheck) {
ToastUtils.showSuccess("已经是最新版本!")
}
} catch (e: Exception) {
e.printStackTrace()
Log.e("检查更新失败!", "" + e.localizedMessage)
if (isManualCheck || NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showError("检查更新失败!")
}
}
}
}
private fun fxDownloadLanzou() {
MapleUtils.findAndHookMethod(
UpdateDialog::class.java,
"downloadWithLanzous",
String::class.java,
object : MethodReplacement() {
override fun replaceHookedMethod(param: MapleBridge.MethodHookParam) {
val updateDialog = param.thisObject as UpdateDialog
val binding = MapleUtils.getObjectField(updateDialog, "binding")
as ViewBinding
val tvProgress = MapleUtils.getObjectField(binding, "tvProgress")
as TextView
val barPercentView = MapleUtils.getObjectField(binding, "barPercentView")
as BarPercentView
tvProgress.text = "正在获取下载链接..."
barPercentView.setPercentage(0F)
val apkUrl = param.args[0] as String
getFileUrl(apkUrl)
.compose { RxUtils.toSimpleSingle(it) }
.subscribe(object : MyObserver<String>() {
override fun onNext(directUrl: String) {
MapleUtils.callMethod(
updateDialog,
"downloadApkNormal",
arrayOf(String::class.java),
directUrl
)
}
override fun onError(e: Throwable) {
MapleUtils.callMethod(
updateDialog,
"error"
)
}
})
}
}
)
}
@Throws(IOException::class)
fun getBakUpdateInfo(): String {
return OkHttpUtils.getHtml(
"https://gitlab.com/fengyuecanzhu/fyreader-resource/-/raw/main/FYReader-Update/" +
(if (App.isDebug()) "debug" else "release") +
"/content.txt"
)
}
}

@ -36,6 +36,22 @@ interface AppFixHandle {
fun onFix(key: String): BooleanArray
fun handleFix(key: String, vararg fix: Pair<String, () -> Unit>): BooleanArray {
val results = mutableListOf<Boolean>()
fix.forEach {
val result = try {
it.second()
true
} catch (e: Exception) {
MapleUtils.log(e)
false
}
results.add(result)
fixResult(key, it.first, result)
}
return results.toBooleanArray()
}
fun fixResult(key: String, name: String, success: Boolean) {
val res = if (success) "Success" else "Failed"
if (!spu.getBoolean("$key-$name-$res", false)) {

@ -0,0 +1,57 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.fix
import me.fycz.maple.MapleBridge
import me.fycz.maple.MapleUtils
import me.fycz.maple.MethodHook
import xyz.fycz.myreader.common.URLCONST
import xyz.fycz.myreader.webapi.LanZouApi
/**
* @author fengyue
* @date 2022/6/21 18:30
*/
@AppFix([], ["更新订阅书源链接,仅支持v2.4.3版本及以上版本"], "2022-06-21")
class AppSubSourceFix : AppFixHandle {
override fun onFix(key: String): BooleanArray {
return handleFix(
key,
"subSource" to { fxSubSource() },
)
}
private fun fxSubSource() {
MapleUtils.findAndHookMethod(
LanZouApi::class.java,
"getFoldFiles",
String::class.java,
Int::class.java,
String::class.java,
object : MethodHook() {
override fun beforeHookedMethod(param: MapleBridge.MethodHookParam) {
if (param.args[0] == URLCONST.SUB_SOURCE_URL) {
param.args[0] = "https://fycz.lanzoum.com/b00pucrch"
param.args[2] = "b0ox"
}
}
}
)
}
}

@ -0,0 +1,149 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 - 2022 fengyuecanzhu
*/
package xyz.fycz.dynamic.utils
import android.util.Log
import io.reactivex.Observable
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.Jsoup
import xyz.fycz.myreader.common.URLCONST
import xyz.fycz.myreader.entity.lanzou.LanZouParseBean
import xyz.fycz.myreader.util.help.StringHelper
import xyz.fycz.myreader.util.utils.GSON
import xyz.fycz.myreader.util.utils.OkHttpUtils
import xyz.fycz.myreader.util.utils.StringUtils
import xyz.fycz.myreader.util.utils.fromJsonObject
import java.net.HttpURLConnection
import java.net.URL
/**
* @author fengyue
* @date 2022/1/22 18:50
*/
object LanZouUtils {
/**
* 通过api获取蓝奏云可下载直链
*
* @param url
* @param password
*/
fun getFileUrl(url: String, password: String = ""): Observable<String> {
return Observable.create {
var html = OkHttpUtils.getHtml(url)
val url2 = if (password.isEmpty()) {
val url1 = getUrl1(html)
html = OkHttpUtils.getHtml(url1)
val data = getDataString(html)
Log.d("LanZouUtils", "data:$data")
val key = getKeyValueByKey(html, data, "sign") +
"&" + getKeyValueByKey(html, data, "websignkey")
Log.d("LanZouUtils", "key:$key")
getUrl2(key, url1)
} else {
getUrl2(StringHelper.getSubString(html, "sign=", "&"), url, password)
}
if (url2.contains("file")) {
it.onNext(getRedirectUrl(url2))
} else {
it.onError(Throwable(url2))
}
it.onComplete()
}
}
fun getUrl1(html: String): String {
val doc = Jsoup.parse(html)
return URLCONST.LAN_ZOU_URL + doc.getElementsByTag("iframe").attr("src")
}
fun getKeyValueByKey(html: String, data: String, key: String): String {
val keyName = StringHelper.getSubString(data, "'$key':", ",")
return if (keyName.endsWith("'")) {
key + "=" + keyName.replace("'", "")
} else {
val lanzousKeyStart = "var $keyName = '"
key + "=" + StringHelper.getSubString(html, lanzousKeyStart, "'")
}
}
fun getUrl2(key: String, referer: String, password: String = ""): String {
val mediaType = "application/x-www-form-urlencoded".toMediaTypeOrNull()
val body = if (password.isEmpty()) {
"action=downprocess&signs=?ctdf&websign=&ves=1&$key"
} else {
"action=downprocess&sign=$key&p=$password"
}
val requestBody = body.toRequestBody(mediaType)
val headers = HashMap<String, String>()
headers["Referer"] = referer
val html = OkHttpUtils.getHtml(
URLCONST.LAN_ZOU_URL + "/ajaxm.php", requestBody,
"UTF-8", headers
)
return getUrl2(html)
}
private fun getUrl2(o: String): String {
val lanZouBean = GSON.fromJsonObject<LanZouParseBean>(o)
lanZouBean?.run {
return if (zt == 1) {
"$dom/file/$url"
} else {
"解析失败\n信息:$inf"
}
}
return ""
}
/**
* 获取重定向地址
*
* @param path
*/
fun getRedirectUrl(path: String): String {
val conn = URL(path)
.openConnection() as HttpURLConnection
conn.instanceFollowRedirects = false
conn.connectTimeout = 5000
conn.setRequestProperty(
"User-Agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"
)
conn.setRequestProperty("Accept-Language", "zh-cn")
conn.setRequestProperty("Connection", "Keep-Alive")
conn.setRequestProperty(
"Accept",
"image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, */*"
)
conn.connect()
val redirectUrl = conn.getHeaderField("Location")
conn.disconnect()
return redirectUrl
}
fun getDataString(html: String): String {
val start = html.lastIndexOf("data :") + "data :".length
val end = html.indexOf("},", start) + 1
return html.substring(start, end)
}
}

@ -20,3 +20,4 @@ include ':app'
include ':DialogX'
include ':dynamic'
include ':user'
include ':plugin'

Loading…
Cancel
Save