diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml index 1bcda71..d748352 100644 --- a/.idea/assetWizardSettings.xml +++ b/.idea/assetWizardSettings.xml @@ -3,11 +3,6 @@ - - - - - @@ -19,8 +14,8 @@ - - + + diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 8e5bb38..55032de 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 681f41a..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - xmlns:android - - ^$ - - - - - - - - - xmlns:.* - - ^$ - - - BY_NAME - - - - - - - .*:id - - http://schemas.android.com/apk/res/android - - - - - - - - - .*:name - - http://schemas.android.com/apk/res/android - - - - - - - - - name - - ^$ - - - - - - - - - style - - ^$ - - - - - - - - - .* - - ^$ - - - BY_NAME - - - - - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - - - - - - - .* - - .* - - - BY_NAME - - - - - - - \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml deleted file mode 100644 index 214996a..0000000 --- a/.idea/dataSources.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - mysql.8 - true - com.mysql.cj.jdbc.Driver - jdbc:mysql://47.105.152.62:3306/fyreader - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 4b8f40c..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 9bba60d..23a89bb 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -7,6 +7,7 @@ + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 178e71f..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 703cc0f..eb2873e 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -26,35 +26,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator-enh.xml b/.idea/markdown-navigator-enh.xml deleted file mode 100644 index a8fcc84..0000000 --- a/.idea/markdown-navigator-enh.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml deleted file mode 100644 index a2fc086..0000000 --- a/.idea/markdown-navigator.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 74c8e4b..190586c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,57 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index c9f6d5e..86610f9 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml deleted file mode 100644 index 5931864..0000000 --- a/.idea/navEditor.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml deleted file mode 100644 index 8bc84c2..0000000 --- a/.idea/sqldialects.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml deleted file mode 100644 index e96534f..0000000 --- a/.idea/uiDesigner.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index f9b7f1e..ef9a0c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -125,8 +125,8 @@ dependencies { implementation "org.jetbrains.anko:anko-sdk27-listeners:$anko_version" //Glide - implementation 'com.github.bumptech.glide:glide:4.9.0' - annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' + implementation 'com.github.bumptech.glide:glide:4.8.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' implementation 'com.squareup.okhttp3:okhttp:3.14.7' @@ -190,6 +190,16 @@ dependencies { // 标签 https://github.com/hongyangAndroid/FlowLayout implementation 'com.hyman:flowlayout-lib:1.1.2' + + implementation 'com.liulishuo.filedownloader:library:1.7.7' + + // 如使用视频广告必须依赖,否则会导致视频播放崩溃 + implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8' + implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8' + implementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8' + + //SwipeBackLayout + implementation 'me.imid.swipebacklayout.lib:library:1.1.0' } greendao { diff --git a/app/debug/output-metadata.json b/app/debug/output-metadata.json new file mode 100644 index 0000000..84b2138 --- /dev/null +++ b/app/debug/output-metadata.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "xyz.fycz.myreader", + "variantName": "processDebugResources", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "versionCode": 193, + "versionName": "1.9.3", + "outputFile": "风月读书v1.9.3.apk" + } + ] +} \ No newline at end of file diff --git a/app/debug/风月读书v1.9.3.apk b/app/debug/风月读书v1.9.3.apk new file mode 100644 index 0000000..e68b6b3 Binary files /dev/null and b/app/debug/风月读书v1.9.3.apk differ diff --git a/app/libs/ddsdk_4.1.6_custom_2021-04-09_0948.aar b/app/libs/ddsdk_4.1.6_custom_2021-04-09_0948.aar new file mode 100644 index 0000000..235a9b7 Binary files /dev/null and b/app/libs/ddsdk_4.1.6_custom_2021-04-09_0948.aar differ diff --git a/app/libs/mintegral_appwall.aar b/app/libs/mintegral_appwall.aar new file mode 100644 index 0000000..e80c68b Binary files /dev/null and b/app/libs/mintegral_appwall.aar differ diff --git a/app/libs/mintegral_appwallext.aar b/app/libs/mintegral_appwallext.aar new file mode 100644 index 0000000..d546ea5 Binary files /dev/null and b/app/libs/mintegral_appwallext.aar differ diff --git a/app/libs/mintegral_chinacommon.aar b/app/libs/mintegral_chinacommon.aar new file mode 100644 index 0000000..1adb3e7 Binary files /dev/null and b/app/libs/mintegral_chinacommon.aar differ diff --git a/app/libs/mintegral_interactiveads.aar b/app/libs/mintegral_interactiveads.aar new file mode 100644 index 0000000..a48a54d Binary files /dev/null and b/app/libs/mintegral_interactiveads.aar differ diff --git a/app/libs/mintegral_interstitial.aar b/app/libs/mintegral_interstitial.aar new file mode 100644 index 0000000..b212d5e Binary files /dev/null and b/app/libs/mintegral_interstitial.aar differ diff --git a/app/libs/mintegral_interstitialvideo.aar b/app/libs/mintegral_interstitialvideo.aar new file mode 100644 index 0000000..aa54ec3 Binary files /dev/null and b/app/libs/mintegral_interstitialvideo.aar differ diff --git a/app/libs/mintegral_mtgbanner.aar b/app/libs/mintegral_mtgbanner.aar new file mode 100644 index 0000000..de0a3b7 Binary files /dev/null and b/app/libs/mintegral_mtgbanner.aar differ diff --git a/app/libs/mintegral_mtgbid.aar b/app/libs/mintegral_mtgbid.aar new file mode 100644 index 0000000..3568dbf Binary files /dev/null and b/app/libs/mintegral_mtgbid.aar differ diff --git a/app/libs/mintegral_mtgdownloads.aar b/app/libs/mintegral_mtgdownloads.aar new file mode 100644 index 0000000..094a0e2 Binary files /dev/null and b/app/libs/mintegral_mtgdownloads.aar differ diff --git a/app/libs/mintegral_mtgjscommon.aar b/app/libs/mintegral_mtgjscommon.aar new file mode 100644 index 0000000..9c5d75b Binary files /dev/null and b/app/libs/mintegral_mtgjscommon.aar differ diff --git a/app/libs/mintegral_mtgnative.aar b/app/libs/mintegral_mtgnative.aar new file mode 100644 index 0000000..894d028 Binary files /dev/null and b/app/libs/mintegral_mtgnative.aar differ diff --git a/app/libs/mintegral_mtgnativeadvanced.aar b/app/libs/mintegral_mtgnativeadvanced.aar new file mode 100644 index 0000000..52c9c86 Binary files /dev/null and b/app/libs/mintegral_mtgnativeadvanced.aar differ diff --git a/app/libs/mintegral_mtgsplash.aar b/app/libs/mintegral_mtgsplash.aar new file mode 100644 index 0000000..7912ff3 Binary files /dev/null and b/app/libs/mintegral_mtgsplash.aar differ diff --git a/app/libs/mintegral_nativeex.aar b/app/libs/mintegral_nativeex.aar new file mode 100644 index 0000000..1425bf5 Binary files /dev/null and b/app/libs/mintegral_nativeex.aar differ diff --git a/app/libs/mintegral_playercommon.aar b/app/libs/mintegral_playercommon.aar new file mode 100644 index 0000000..b72af76 Binary files /dev/null and b/app/libs/mintegral_playercommon.aar differ diff --git a/app/libs/mintegral_reward.aar b/app/libs/mintegral_reward.aar new file mode 100644 index 0000000..22574d9 Binary files /dev/null and b/app/libs/mintegral_reward.aar differ diff --git a/app/libs/mintegral_videocommon.aar b/app/libs/mintegral_videocommon.aar new file mode 100644 index 0000000..ebe963d Binary files /dev/null and b/app/libs/mintegral_videocommon.aar differ diff --git a/app/libs/mintegral_videojs.aar b/app/libs/mintegral_videojs.aar new file mode 100644 index 0000000..3f19f98 Binary files /dev/null and b/app/libs/mintegral_videojs.aar differ diff --git a/app/libs/windAd-2.25.2.aar b/app/libs/windAd-2.25.2.aar new file mode 100644 index 0000000..9a71d84 Binary files /dev/null and b/app/libs/windAd-2.25.2.aar differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..63fbbd0 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "xyz.fycz.myreader", + "variantName": "processReleaseResources", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "versionCode": 194, + "versionName": "1.9.4", + "outputFile": "风月读书v1.9.4.apk" + } + ] +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8f97fd8..dc5f2eb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,118 +1,145 @@ + xmlns:tools="http://schemas.android.com/tools" + package="xyz.fycz.myreader"> + + + + + + + + - + - + - + - + - + - + - + - + - - + + - + - + - - + + + + android:name=".application.App" + android:allowBackup="true" + android:alwaysRetainTaskState="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:networkSecurityConfig="@xml/network_security_config" + android:requestLegacyExternalStorage="true" + android:roundIcon="@mipmap/ic_launcher" + android:supportsRtl="true" + android:theme="@style/MAppTheme"> - - + + + android:name="android.app.shortcuts" + android:resource="@xml/shortcuts" /> + android:name="androidx.core.content.FileProvider" + android:authorities="xyz.fycz.myreader.fileprovider" + android:exported="false" + android:grantUriPermissions="true"> + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/file_paths" /> - - + android:name=".ui.activity.MainActivity" + android:alwaysRetainTaskState="true" + android:launchMode="singleTask" /> + + + android:name=".ui.activity.ReadActivity" + android:launchMode="singleTask" + android:windowSoftInputMode="stateAlwaysHidden"> - + + + - + - + + - - + - + + - - + + + + + + + + + - - - - - - + + - - - + \ No newline at end of file diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy index 2b8005a..5391ac6 100644 --- a/app/src/main/assets/updatelog.fy +++ b/app/src/main/assets/updatelog.fy @@ -1,3 +1,18 @@ +2021.04.25 +风月读书v1.9.4 +更新内容: +1、息屏时间新增跟随系统选项 +2、新增支持作者界面 +3、修复书源天籁小说、九桃小说、搜小说网、书海阁、笔下文学、搜书网 +4、新增左滑返回操作 +5、新增书籍信息编辑界面(支持修改书名、作者、封面、简介) +6、优化更新提示对话框 +7、书源函数新增列表分组反转函数 + +无法访问的书源:100本小说、传奇小说、老幺小说、言情楼、米趣小说 +无法搜索的书源:红尘小说、峡谷文学、星星小说、红甘泉、时光小说 +需要cookie搜索的书源:搜小说网、书海阁、笔下文学、搜书网 + 2021.03.13 风月读书v1.9.3 更新内容: diff --git a/app/src/main/java/xyz/fycz/myreader/ai/BookWordCountPre.java b/app/src/main/java/xyz/fycz/myreader/ai/BookWordCountPre.java new file mode 100644 index 0000000..f725308 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ai/BookWordCountPre.java @@ -0,0 +1,182 @@ +package xyz.fycz.myreader.ai; + +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import xyz.fycz.myreader.greendao.entity.Book; +import xyz.fycz.myreader.greendao.entity.Chapter; +import xyz.fycz.myreader.greendao.service.ChapterService; + +/** + * 预测书籍字数 + * + * @author fengyue + * @date 2021/4/18 16:54 + */ +public class BookWordCountPre { + private static final String TAG = BookWordCountPre.class.getSimpleName(); + + private double lr = 0.001; + private Book book; + private List chapters; + private double[][] trainData; + private double[][] target; + private double[][] weights1; + //private double[][] weights2; + private double[][] testData; + private double median; + private static Random rand = new Random(); + + + public BookWordCountPre(Book book) { + this.book = book; + this.chapters = ChapterService.getInstance().findBookAllChapterByBookId(book.getId()); + } + + //进行训练 + public boolean train() { + if (!preData()) { + Log.i(TAG, String.format("《%s》缓存章节数量过少,无法进行训练", book.getName())); + return false; + } + Log.i(TAG, String.format("《%s》开始进行训练", book.getName())); + double loss = 0; + double eps = 0.0000000001; + double[][] gradient1; + //double[][] gradient2; + double[][] adagrad1 = new double[trainData[0].length][1]; + //double[][] adagrad2 = new double[trainData[0].length][1]; + //double[][] dl_dw = new double[trainData[0].length][1]; + int maxEpoch; + maxEpoch = 1000 / trainData.length; + if (maxEpoch < 10) maxEpoch = 10; + for (int epoch = 0; epoch < maxEpoch; epoch++) { + shuffle(trainData, target); + for (int j = 0; j < trainData.length; j++) { + double[][] oneData = MatrixUtil.to2dMatrix(trainData[j], false); + double[][] oneTarget = MatrixUtil.to2dMatrix(target[j], true); + double[][] out = getOut(oneData); + loss = Math.sqrt(MatrixUtil.sum(MatrixUtil.pow(MatrixUtil.sub(out, oneTarget), 2)) / 2); + /*dl_dw = MatrixUtil.sub( + MatrixUtil.add( + MatrixUtil.dot(MatrixUtil.pow(oneData, 2), weights2), + MatrixUtil.dot(oneData, weights2)), + oneTarget + );*/ + + gradient1 = MatrixUtil.dot(MatrixUtil.transpose(oneData), MatrixUtil.sub(out, oneTarget)); + //gradient1 = MatrixUtil.dot(MatrixUtil.transpose(MatrixUtil.pow(oneData, 2)), dl_dw); + //gradient2 = MatrixUtil.dot(MatrixUtil.transpose(oneData), dl_dw); + adagrad1 = MatrixUtil.add(adagrad1, MatrixUtil.pow(gradient1, 2)); + //adagrad2 = MatrixUtil.add(adagrad1, MatrixUtil.pow(gradient2, 2)); + weights1 = MatrixUtil.sub(weights1, MatrixUtil.divide(MatrixUtil.dot(gradient1, lr), + MatrixUtil.sqrt(MatrixUtil.add(adagrad1, eps)))); + /*weights2 = MatrixUtil.sub(weights2, MatrixUtil.divide(MatrixUtil.dot(gradient2, lr), + MatrixUtil.sqrt(MatrixUtil.add(adagrad2, eps))));*/ + } + Log.i(TAG, String.format("《%s》-> epoch=%d,loss=%f", book.getName(), epoch, loss)); + } + return true; + } + + //进行预测并获得书籍总字数 + public int predict() { + double[][] pre = getOut(testData); + double[] preVec = MatrixUtil.toVector(pre); + Arrays.sort(preVec); + int k = (int) (preVec[preVec.length / 2 + 1] / median); + //int k = (int) ((MatrixUtil.sum(pre) / pre.length) / median); + pre = MatrixUtil.divide(pre, k); + /*for (int i = 0; i < pre.length; i++) { + pre[i][0] = median; + }*/ + Log.i(TAG, String.format("k=%d->《%s》的预测数据%s", k, book.getName(), + Arrays.toString(MatrixUtil.toVector(pre)))); + return (int) (MatrixUtil.sum(pre) + MatrixUtil.sum(target)); + } + + private double[][] getOut(double[][] data) { + /*return MatrixUtil.add(MatrixUtil.dot(MatrixUtil.pow(data, 2), weights2), + MatrixUtil.dot(data, weights1));*/ + return MatrixUtil.dot(data, weights1); + } + + //准备训练数据 + private boolean preData() { + rand.setSeed(10); + List catheChapters = new ArrayList<>(); + List unCatheChapters = new ArrayList<>(); + //章节最长标题长度 + int maxTitleLen = 0; + //获取已缓存章节 + for (Chapter chapter : chapters) { + if (ChapterService.isChapterCached(book.getId(), chapter.getTitle())) { + catheChapters.add(chapter); + } else { + unCatheChapters.add(chapter); + } + if (maxTitleLen < chapter.getTitle().length()) { + maxTitleLen = chapter.getTitle().length(); + } + } + Log.i(TAG, String.format("《%s》已缓存章节数量:%d,最大章节标题长度:%d", + book.getName(), catheChapters.size(), maxTitleLen)); + if (catheChapters.size() <= 10) return false; + //创建训练数据 + trainData = new double[catheChapters.size()][maxTitleLen + 1]; + //创建测试数据 + testData = new double[chapters.size() - catheChapters.size()][maxTitleLen + 1]; + //创建权重矩阵 + weights1 = new double[maxTitleLen + 1][1]; + //weights2 = new double[maxTitleLen + 1][1]; + //创建目标矩阵 + target = new double[catheChapters.size()][1]; + for (int i = 0; i < catheChapters.size(); i++) { + Chapter chapter = catheChapters.get(i); + char[] charArr = chapter.getTitle().replaceAll("[((【{]", "").toCharArray(); + for (int j = 0; j < charArr.length; j++) { + trainData[i][j] = charArr[j]; + } + trainData[i][maxTitleLen] = 1; + target[i][0] = ChapterService.countChar(book.getId(), chapter.getTitle()); + } + for (int i = 0; i < maxTitleLen + 1; i++) { + weights1[i][0] = rand.nextDouble(); + //weights2[i][0] = Math.random(); + } + for (int i = 0; i < unCatheChapters.size(); i++) { + Chapter chapter = unCatheChapters.get(i); + char[] charArr = chapter.getTitle().toCharArray(); + for (int j = 0; j < charArr.length; j++) { + testData[i][j] = charArr[j]; + } + testData[i][maxTitleLen] = 1; + } + /*double[] tem = MatrixUtil.toVector(target); + Arrays.sort(tem); + median = tem[tem.length / 2 + 1];*/ + median = MatrixUtil.sum(target) / target.length; + return true; + } + + + public static void swap(T[] a, int i, int j) { + T temp = a[i]; + a[i] = a[j]; + a[j] = temp; + } + + public static void shuffle(T[]... arr) { + int length = arr[0].length; + for (int i = length; i > 0; i--) { + int randInd = rand.nextInt(i); + for (T[] ts : arr) { + swap(ts, randInd, i - 1); + } + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ai/MatrixUtil.java b/app/src/main/java/xyz/fycz/myreader/ai/MatrixUtil.java new file mode 100644 index 0000000..60960a7 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ai/MatrixUtil.java @@ -0,0 +1,295 @@ +package xyz.fycz.myreader.ai; + +/** + * @author fengyue + * @date 2021/4/7 16:10 + */ +public class MatrixUtil { + //矩阵加法 C=A+B + public static double[][] add(double[][] m1, double[][] m2) { + if (m1 == null || m2 == null || + m1.length != m2.length || + m1[0].length != m2[0].length) { + return null; + } + + double[][] m = new double[m1.length][m1[0].length]; + + for (int i = 0; i < m.length; ++i) { + for (int j = 0; j < m[i].length; ++j) { + m[i][j] = m1[i][j] + m2[i][j]; + } + } + + return m; + } + + public static double[][] add(double[][] m, double a) { + if (m == null) { + return null; + } + + double[][] retM = new double[m.length][m[0].length]; + + for (int i = 0; i < retM.length; ++i) { + for (int j = 0; j < retM[i].length; ++j) { + retM[i][j] = m[i][j] + a; + } + } + + return retM; + } + + public static double[][] sub(double[][] m1, double[][] m2) { + if (m1 == null || m2 == null || + m1.length != m2.length || + m1[0].length != m2[0].length) { + return null; + } + + double[][] m = new double[m1.length][m1[0].length]; + + for (int i = 0; i < m.length; ++i) { + for (int j = 0; j < m[i].length; ++j) { + m[i][j] = m1[i][j] - m2[i][j]; + } + } + + return m; + } + + //矩阵转置 + public static double[][] transpose(double[][] m) { + if (m == null) return null; + double[][] mt = new double[m[0].length][m.length]; + for (int i = 0; i < m.length; ++i) { + for (int j = 0; j < m[i].length; ++j) { + mt[j][i] = m[i][j]; + } + } + return mt; + } + + //矩阵相乘 C=A*B + public static double[][] dot(double[][] m1, double[][] m2) { + if (m1 == null || m2 == null || m1[0].length != m2.length) + return null; + + double[][] m = new double[m1.length][m2[0].length]; + for (int i = 0; i < m1.length; ++i) { + for (int j = 0; j < m2[0].length; ++j) { + for (int k = 0; k < m1[i].length; ++k) { + m[i][j] += m1[i][k] * m2[k][j]; + } + } + } + + return m; + } + + //数乘矩阵 + public static double[][] dot(double[][] m, double k) { + if (m == null) return null; + double[][] retM = new double[m.length][m[0].length]; + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[0].length; j++) { + retM[i][j] = m[i][j] * k; + } + } + return retM; + } + + //同型矩阵除法 + public static double[][] divide(double[][] m1, double[][] m2) { + if (m1 == null || m2 == null || + m1.length != m2.length || + m1[0].length != m2[0].length) { + return null; + } + double[][] retM = new double[m1.length][m1[0].length]; + for (int i = 0; i < retM.length; ++i) { + for (int j = 0; j < retM[i].length; ++j) { + retM[i][j] = m1[i][j] / m2[i][j]; + } + } + return retM; + } + + //矩阵除数 + public static double[][] divide(double[][] m, double k) { + if (m == null) return null; + double[][] retM = new double[m.length][m[0].length]; + for (int i = 0; i < m.length; i++) { + for (int j = 0; j < m[0].length; j++) { + retM[i][j] = m[i][j] / k; + } + } + return retM; + } + + //求矩阵行列式(需为方阵) + public static double det(double[][] m) { + if (m == null || m.length != m[0].length) + return 0; + + if (m.length == 1) + return m[0][0]; + else if (m.length == 2) + return det2(m); + else if (m.length == 3) + return det3(m); + else { + int re = 0; + for (int i = 0; i < m.length; ++i) { + re += (((i + 1) % 2) * 2 - 1) * det(companion(m, i, 0)) * m[i][0]; + } + return re; + } + } + + //求二阶行列式 + public static double det2(double[][] m) { + if (m == null || m.length != 2 || m[0].length != 2) + return 0; + + return m[0][0] * m[1][1] - m[1][0] * m[0][1]; + } + + //求三阶行列式 + public static double det3(double[][] m) { + if (m == null || m.length != 3 || m[0].length != 3) + return 0; + + double re = 0; + for (int i = 0; i < 3; ++i) { + int temp1 = 1; + for (int j = 0, k = i; j < 3; ++j, ++k) { + temp1 *= m[j][k % 3]; + } + re += temp1; + temp1 = 1; + for (int j = 0, k = i; j < 3; ++j, --k) { + if (k < 0) k += 3; + temp1 *= m[j][k]; + } + re -= temp1; + } + + return re; + } + + //求矩阵的逆(需方阵) + public static double[][] inv(double[][] m) { + if (m == null || m.length != m[0].length) + return null; + + double A = det(m); + double[][] mi = new double[m.length][m[0].length]; + for (int i = 0; i < m.length; ++i) { + for (int j = 0; j < m[i].length; ++j) { + double[][] temp = companion(m, i, j); + mi[j][i] = (((i + j + 1) % 2) * 2 - 1) * det(temp) / A; + } + } + + return mi; + } + + //求方阵代数余子式 + public static double[][] companion(double[][] m, int x, int y) { + if (m == null || m.length <= x || m[0].length <= y || + m.length == 1 || m[0].length == 1) + return null; + + double[][] cm = new double[m.length - 1][m[0].length - 1]; + + int dx = 0; + for (int i = 0; i < m.length; ++i) { + if (i != x) { + int dy = 0; + for (int j = 0; j < m[i].length; ++j) { + if (j != y) { + cm[dx][dy++] = m[i][j]; + } + } + ++dx; + } + } + return cm; + } + + //生成全为0的矩阵 + public static double[][] zeros(int rows, int cols){ + return new double[rows][cols]; + } + + //生成全为1的矩阵 + public static double[][] ones(int rows, int cols){ + return add(zeros(rows, cols), 1); + } + + public static double sum(double[][] matrix){ + double sum = 0; + for (double[] doubles : matrix) { + for (double aDouble : doubles) { + sum += aDouble; + } + } + return sum; + } + + public static double[][] pow(double[][] matrix, int exponent){ + if (matrix == null) return null; + double[][] retM = new double[matrix.length][matrix[0].length]; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + retM[i][j] = Math.pow(matrix[i][j], exponent); + } + } + return retM; + } + + public static double[][] sqrt(double[][] matrix){ + if (matrix == null) return null; + double[][] retM = new double[matrix.length][matrix[0].length]; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[i].length; j++) { + retM[i][j] = Math.sqrt(matrix[i][j]); + } + } + return matrix; + } + + public static double[][] to2dMatrix(double[] vector, boolean isCol){ + if (vector == null) return null; + double[][] retM; + if (isCol) { + retM = new double[vector.length][1]; + }else { + retM = new double[1][vector.length]; + } + for (int i = 0; i < vector.length; i++) { + if (isCol) { + retM[i][0] = vector[i]; + }else { + retM[0][i] = vector[i]; + } + } + return retM; + } + + public static double[] toVector(double[][] matrix){ + double[] retV = null; + if (matrix.length == 1){ + retV = new double[matrix[0].length]; + double[] doubles = matrix[0]; + System.arraycopy(doubles, 0, retV, 0, doubles.length); + }else if (matrix[0].length == 1){ + retV = new double[matrix.length]; + for (int i = 0; i < matrix.length; i++) { + retV[i] = matrix[i][0]; + } + } + return retV; + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/application/App.java b/app/src/main/java/xyz/fycz/myreader/application/App.java index e1f97b1..6fe974b 100644 --- a/app/src/main/java/xyz/fycz/myreader/application/App.java +++ b/app/src/main/java/xyz/fycz/myreader/application/App.java @@ -4,6 +4,7 @@ package xyz.fycz.myreader.application; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; +import android.app.ActivityManager; import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -18,10 +19,19 @@ import android.os.Build; import android.os.Environment; import android.os.Handler; import android.util.Log; +import android.webkit.WebView; +import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; +import com.liulishuo.filedownloader.FileDownloader; +import com.liulishuo.filedownloader.connection.FileDownloadUrlConnection; +import com.weaction.ddsdk.base.DdSdkHelper; +import com.weaction.ddsdk.util.GsonUtil; + +import org.json.JSONException; +import org.json.JSONObject; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -38,24 +48,37 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import io.reactivex.Single; +import io.reactivex.SingleEmitter; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.annotations.NonNull; import io.reactivex.internal.functions.Functions; import io.reactivex.plugins.RxJavaPlugins; +import okhttp3.MediaType; +import okhttp3.RequestBody; import xyz.fycz.myreader.R; +import xyz.fycz.myreader.base.observer.MySingleObserver; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.common.URLCONST; import xyz.fycz.myreader.entity.Setting; +import xyz.fycz.myreader.model.backup.UserService; import xyz.fycz.myreader.model.source.BookSourceManager; import xyz.fycz.myreader.ui.activity.MainActivity; +import xyz.fycz.myreader.ui.activity.SplashActivity; import xyz.fycz.myreader.ui.dialog.APPDownloadTip; import xyz.fycz.myreader.ui.dialog.DialogCreator; import xyz.fycz.myreader.ui.fragment.BookcaseFragment; +import xyz.fycz.myreader.util.DateHelper; import xyz.fycz.myreader.util.HttpUtil; import xyz.fycz.myreader.util.SharedPreUtils; import xyz.fycz.myreader.util.StringHelper; import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.util.utils.AdUtils; import xyz.fycz.myreader.util.utils.FileUtils; import xyz.fycz.myreader.util.utils.NetworkUtils; import xyz.fycz.myreader.util.utils.OkHttpUtils; +import xyz.fycz.myreader.ui.dialog.UpdateDialog; +import xyz.fycz.myreader.util.utils.RxUtils; public class App extends Application { @@ -65,7 +88,6 @@ public class App extends Application { private static App application; private ExecutorService mFixedThreadPool; - @Override public void onCreate() { super.onCreate(); @@ -73,6 +95,16 @@ public class App extends Application { firstInit(); HttpUtil.trustAllHosts();//信任所有证书 RxJavaPlugins.setErrorHandler(Functions.emptyConsumer()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + webviewSetPath(this); + } + FileDownloader.setupOnApplicationOnCreate(this) + .connectionCreator(new FileDownloadUrlConnection + .Creator(new FileDownloadUrlConnection.Configuration() + .connectTimeout(15_000) // set connection timeout. + .readTimeout(15_000) // set read timeout. + )) + .commit(); // handleSSLHandshake(); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel(); @@ -84,16 +116,16 @@ public class App extends Application { private void firstInit() { SharedPreUtils sru = SharedPreUtils.getInstance(); - if (!sru.getBoolean("firstInit")){ + if (!sru.getBoolean("firstInit")) { BookSourceManager.initDefaultSources(); sru.putBoolean("firstInit", true); } } public void initNightTheme() { - if (isNightFS()){ + if (isNightFS()) { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); - }else { + } else { if (isNightTheme()) { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); } else { @@ -112,6 +144,7 @@ public class App extends Application { /** * 设置夜间模式 + * * @param isNightMode */ public void setNightTheme(boolean isNightMode) { @@ -199,7 +232,7 @@ public class App extends Application { handler.post(runnable); } - public static Handler getHandler(){ + public static Handler getHandler() { return handler; } @@ -249,6 +282,7 @@ public class App extends Application { /** * 获取apk包的信息:版本号,名称,图标等 + * * @param absPath apk包的绝对路径 */ public static int apkInfo(String absPath) { @@ -266,8 +300,7 @@ public class App extends Application { /** * 检查更新 */ - public static void checkVersionByServer(final AppCompatActivity activity, final boolean isManualCheck, - final BookcaseFragment mBookcaseFragment) { + public static void checkVersionByServer(final AppCompatActivity activity, final boolean isManualCheck) { App.getApplication().newThread(() -> { try { String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read"; @@ -318,17 +351,16 @@ public class App extends Application { String[] updateContents = updateContent.split("/"); for (String string : updateContents) { s.append(string); - s.append("\n"); + s.append(""); } Log.i("检查更新,最新版本", newestVersion + ""); if (newestVersion > versionCode) { - App m = new App(); Setting setting = SysManager.getSetting(); if (isManualCheck || setting.getNewestVersionCode() < newestVersion || isForceUpdate) { setting.setNewestVersionCode(newestVersion); SysManager.saveSetting(setting); - m.updateApp(activity, downloadLink, newestVersion, s.toString(), isForceUpdate, - mBookcaseFragment); + getApplication().updateApp2(activity, downloadLink, newestVersion, s.toString(), isForceUpdate + ); } } else if (isManualCheck) { ToastUtils.showSuccess("已经是最新版本!"); @@ -370,11 +402,11 @@ public class App extends Application { activity.finish(); } }, (dialog, which) -> { - if (activity instanceof MainActivity){ + if (activity instanceof MainActivity) { MainActivity mainActivity = (MainActivity) activity; mainActivity.getViewPagerMain().setCurrentItem(0); String filePath = APPCONST.UPDATE_APK_FILE_DIR + "FYReader.apk"; - if (apkInfo(filePath) == versionCode){ + if (apkInfo(filePath) == versionCode) { mainActivity.installApk(FileUtils.getFile(filePath), isForceUpdate); return; } @@ -400,6 +432,25 @@ public class App extends Application { }); } + public void updateApp2(final AppCompatActivity activity, final String url, final int versionCode, String message, + final boolean isForceUpdate) { + //String version = (versionCode / 100 % 10) + "." + (versionCode / 10 % 10) + "." + (versionCode % 10); + int hun = versionCode / 100; + int ten = versionCode / 10 % 10; + int one = versionCode % 10; + String versionName = "v" + hun + "." + ten + "." + one; + UpdateDialog updateDialog = new UpdateDialog.Builder() + .setVersion(versionName) + .setContent(message) + .setCancelable(!isForceUpdate) + .setDownloadUrl(url) + .setContentHtml(true) + .setDebug(true) + .build(); + + updateDialog.showUpdateDialog(activity); + } + private void goDownload(Activity activity, String url) { String downloadLink = url; if (url == null || "".equals(url)) { @@ -425,6 +476,7 @@ public class App extends Application { /** * 判断Activity是否Destroy + * * @param mActivity * @return */ @@ -453,4 +505,26 @@ public class App extends Application { return false; } } + + @RequiresApi(api = 28) + public void webviewSetPath(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + String processName = getProcessName(context); + + if (!getApplicationContext().getPackageName().equals(processName)) {//判断不等于默认进程名称 + WebView.setDataDirectorySuffix(processName); + } + } + } + + public String getProcessName(Context context) { + if (context == null) return null; + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) { + if (processInfo.pid == android.os.Process.myPid()) { + return processInfo.processName; + } + } + return null; + } } diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java index 4f35b6a..b97fddb 100644 --- a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; +import me.imid.swipebacklayout.lib.app.SwipeBackActivity; import xyz.fycz.myreader.ActivityManage; import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; @@ -30,7 +31,7 @@ import xyz.fycz.myreader.util.StatusBarUtil; * @author fengyue * @date 2020/8/12 20:02 */ -public abstract class BaseActivity extends AppCompatActivity { +public abstract class BaseActivity extends SwipeBackActivity { private static final int INVALID_VAL = -1; protected CompositeDisposable mDisposable; @@ -43,9 +44,10 @@ public abstract class BaseActivity extends AppCompatActivity { * 绑定视图 */ protected abstract void bindView(); + /************************init area************************************/ - protected void addDisposable(Disposable d){ - if (mDisposable == null){ + protected void addDisposable(Disposable d) { + if (mDisposable == null) { mDisposable = new CompositeDisposable(); } mDisposable.add(d); @@ -54,29 +56,44 @@ public abstract class BaseActivity extends AppCompatActivity { /** * 配置Toolbar + * * @param toolbar */ - protected void setUpToolbar(Toolbar toolbar){ + protected void setUpToolbar(Toolbar toolbar) { + } + + /** + * 是否开启左滑手势 + * + * @return + */ + protected boolean initSwipeBackEnable() { + return true; } - protected void initData(Bundle savedInstanceState){ + protected void initData(Bundle savedInstanceState) { } + /** * 初始化零件 */ protected void initWidget() { } + /** * 初始化点击事件 */ - protected void initClick(){ + protected void initClick() { } + /** * 逻辑使用区 */ - protected void processLogic(){ + protected void processLogic() { } + + /** * @return 是否夜间模式 */ @@ -86,6 +103,7 @@ public abstract class BaseActivity extends AppCompatActivity { /** * 设置夜间模式 + * * @param isNightMode */ protected void setNightTheme(boolean isNightMode) { @@ -95,8 +113,6 @@ public abstract class BaseActivity extends AppCompatActivity { } - - /*************************lifecycle area*****************************************************/ @Override @@ -105,6 +121,7 @@ public abstract class BaseActivity extends AppCompatActivity { initTheme(); ActivityManage.addActivity(this); bindView(); + setSwipeBackEnable(initSwipeBackEnable()); initData(savedInstanceState); initToolbar(); initWidget(); @@ -112,10 +129,10 @@ public abstract class BaseActivity extends AppCompatActivity { processLogic(); } - private void initToolbar(){ + private void initToolbar() { //更严谨是通过反射判断是否存在Toolbar - mToolbar = findViewById(R.id.toolbar); - if (mToolbar != null){ + mToolbar = (Toolbar) findViewById(R.id.toolbar); + if (mToolbar != null) { supportActionBar(mToolbar); setUpToolbar(mToolbar); } @@ -124,7 +141,7 @@ public abstract class BaseActivity extends AppCompatActivity { @Override protected void onResume() { super.onResume(); - if (isThemeChange()){ + if (isThemeChange()) { recreate(); } } @@ -133,16 +150,17 @@ public abstract class BaseActivity extends AppCompatActivity { protected void onDestroy() { super.onDestroy(); ActivityManage.removeActivity(this); - if (mDisposable != null){ + if (mDisposable != null) { mDisposable.dispose(); } } + /** * 初始化主题 */ public void initTheme() { //if (isNightTheme()) { - //setTheme(R.style.AppNightTheme); + //setTheme(R.style.AppNightTheme); curNightMode = AppCompatDelegate.getDefaultNightMode(); /*} else { //curNightMode = false; @@ -150,20 +168,21 @@ public abstract class BaseActivity extends AppCompatActivity { }*/ } - protected boolean isThemeChange(){ + protected boolean isThemeChange() { return curNightMode != AppCompatDelegate.getDefaultNightMode(); } + /**************************used method area*******************************************/ - protected void startActivity(Class extends AppCompatActivity> activity){ + protected void startActivity(Class extends AppCompatActivity> activity) { Intent intent = new Intent(this, activity); startActivity(intent); } - protected ActionBar supportActionBar(Toolbar toolbar){ + protected ActionBar supportActionBar(Toolbar toolbar) { setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); - if (actionBar != null){ + if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayShowHomeEnabled(true); } @@ -173,7 +192,7 @@ public abstract class BaseActivity extends AppCompatActivity { return actionBar; } - protected void setStatusBarColor(int statusColor, boolean dark){ + protected void setStatusBarColor(int statusColor, boolean dark) { //沉浸式代码配置 //当FitsSystemWindows设置 true 时,会在屏幕最上方预留出状态栏高度的 padding StatusBarUtil.setRootViewFitsSystemWindows(this, true); @@ -190,7 +209,9 @@ public abstract class BaseActivity extends AppCompatActivity { StatusBarUtil.setStatusBarColor(this, 0x55000000); } } - } /** + } + + /** * 设置MENU图标颜色 */ @Override diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java b/app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java index 113113e..4a506d4 100644 --- a/app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java @@ -32,8 +32,8 @@ public abstract class BaseTabActivity extends BaseActivity { @Override protected void bindView() { - mTlIndicator = findViewById(R.id.tab_tl_indicator); - mVp = findViewById(R.id.tab_vp); + mTlIndicator = (TabLayout) findViewById(R.id.tab_tl_indicator); + mVp = (ViewPager) findViewById(R.id.tab_vp); } /*****************rewrite method***************************/ 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 8fc7436..776bc5a 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java @@ -79,6 +79,8 @@ public class APPCONST { public static final int REQUEST_IMPORT_BOOK_SOURCE = 1009; public static final int REQUEST_EDIT_BOOK_SOURCE = 1010; public static final int REQUEST_BOOK_SOURCE = 1011; + public static final int REQUEST_SELECT_COVER = 1012; + public static final int REQUEST_EDIT_BOOK = 1013; public static final int REQUEST_READ = 1; 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 7fa4cbe..81515c2 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java @@ -14,7 +14,7 @@ public class URLCONST { //字体下载 public static final String FONT_DOWNLOAD_URL = "https://novel.fycz.xyz/app/fonts/"; - public static final String APP_WEB_URL = "http://fyreader.fycz.xyz:8080/FYReader/"; + public static final String APP_WEB_URL = "https://fyreader.fycz.xyz/"; public static final String BAI_DU_SEARCH = "https://m.baidu.com/s?word={key}"; @@ -22,5 +22,14 @@ public class URLCONST { public static final String YOU_DAO_SEARCH = "http://m.youdao.com/dict?le=eng&q={key}"; + public static final String FY_READER_URL = "https://fyreader.fycz.tk"; + + public static final String AD_URL = FY_READER_URL + "/ad"; + + public static final String DONATE = "https://gitee.com/fengyuecanzhu/Donate/raw/master"; + + public static final String WX_ZSM = DONATE + "/wx_zsm.png"; + public static final String ZFB_SKM = DONATE + "/zfb_skm.jpg"; + public static final String QQ_SKM = DONATE + "/qq_skm.png"; } diff --git a/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt b/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt index c17ea55..4f54c51 100644 --- a/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt +++ b/app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt @@ -28,8 +28,8 @@ object EditEntityUtil { "搜索关键词以{key}进行占位;post请求以“,”分隔url,“,”前是搜索地址,“,”后是请求体")) add(EditEntity("charset", searchRule?.charset, R.string.r_search_charset, "默认使用书源字符编码")) add(EditEntity("list", searchRule?.list, R.string.r_book_list, - "对于Matcher解析器:此处填写书籍列表所在区间,支持普通函数;" + - "\n对于Xpath/JsonPath解析器:此处填写书籍列表规则,不支持普通函数,规则后接##!加数字可以跳过列表前几个")) + "对于Matcher解析器:此处填写书籍列表所在区间,仅支持普通函数;" + + "\n对于Xpath/JsonPath解析器:此处填写书籍列表规则,仅支持列表函数")) add(EditEntity("name", searchRule?.name, R.string.r_book_name)) add(EditEntity("author", searchRule?.author, R.string.r_author)) add(EditEntity("type", searchRule?.type, R.string.rule_book_type)) @@ -70,11 +70,11 @@ object EditEntityUtil { add(EditEntity("chapterBaseUrl", tocRule?.chapterBaseUrl, R.string.rule_chapter_base_url, "如果章节URL(一般为相对路径)无法定位章节,可填写此规则获取,默认为书源URL")) add(EditEntity("chapterList", tocRule?.chapterList, R.string.rule_chapter_list, - "对于Mathcer解析器:此处填写书籍列表所在区间,支持普通函数;" + - "\n对于Xpath/JsonPath解析器:此处填写书籍列表规则,不支持普通函数,规则后接##!加数字可以跳过列表前几个")) + "对于Mathcer解析器:此处填写书籍列表所在区间,仅支持普通函数;" + + "\n对于Xpath/JsonPath解析器:此处填写书籍列表规则,仅支持列表函数")) add(EditEntity("chapterName", tocRule?.chapterName, R.string.rule_chapter_name, - "对于Mathcer解析器:此处填写章节名称和URL规则,其中章节名称以占位,章节URL以占位,不支持普通函数,规则后接##!加数字可以跳过列表前几个\n" + - "对于Xpath/JsonPath解析器:此处填写章节名称,支持普通函数")) + "对于Mathcer解析器:此处填写章节名称和URL规则,其中章节名称以占位,章节URL以占位,仅支持列表函数\n" + + "对于Xpath/JsonPath解析器:此处填写章节名称,仅支持普通函数")) add(EditEntity("chapterUrl", tocRule?.chapterUrl, R.string.rule_chapter_url, "对于Mathcer解析器:此处不用填写\n" + "对于Xpath/JsonPath解析器:此处填写章节URL规则")) 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 4c3d3d3..7152c3c 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 @@ -286,6 +286,27 @@ public class ChapterService extends BaseService { return file.exists(); } + public static int countChar(String folderName, String fileName) {//统计字符数 + int charnum = 0;//字符数 + File file = new File(APPCONST.BOOK_CACHE_PATH + folderName + + File.separator + fileName + FileUtils.SUFFIX_FY); + int x; + FileReader fReader = null; + try { + fReader = new FileReader(file); + while ((x = fReader.read()) != -1) {//按字符读文件,判断,符合则字符加一 + char a = (char) x; + if (a != '\n' && a != '\r') { + charnum++; + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + IOUtils.close(fReader); + } + return charnum;//返回结果 + } private void deleteAllChapterCacheFile(String bookId) { FileUtils.deleteFile(APPCONST.BOOK_CACHE_PATH + bookId); diff --git a/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java b/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java index 7b03409..44774b1 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java +++ b/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java @@ -317,7 +317,7 @@ public class UserService { } - private static String makeSignalParam(){ + public static String makeSignalParam(){ return "&signal=" + AppInfoUtils.getSingInfo(App.getmContext(), App.getApplication().getPackageName(), AppInfoUtils.SHA1); } diff --git a/app/src/main/java/xyz/fycz/myreader/model/source/BaseAnalyzer.java b/app/src/main/java/xyz/fycz/myreader/model/source/BaseAnalyzer.java index eba0fe0..36656c1 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/source/BaseAnalyzer.java +++ b/app/src/main/java/xyz/fycz/myreader/model/source/BaseAnalyzer.java @@ -2,6 +2,8 @@ package xyz.fycz.myreader.model.source; import android.util.Log; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import xyz.fycz.myreader.util.StringHelper; @@ -238,6 +240,78 @@ public abstract class BaseAnalyzer { return content; } + /** + * 执行列表(特殊)函数 + * 1、!n:跳过列表前n个 + * 2、%n:以n为分组长度进行分组,并对各组进行反转 + * @param funs + * @param list + * @return + */ + protected List evalListFunction(String funs, List list) { + if (funs.endsWith(";")) funs = funs.substring(0, funs.length() - 1); + String[] funArr = funs.split(";"); + for (String fun : funArr) { + if (fun.contains("!")) { + list = evalListSkip(fun, list); + } else if (fun.contains("%")) { + list = evalListGroupReverse(fun, list); + } + } + return new ArrayList(list); + } + + /** + * 执行列表跳过函数 + * @param rule + * @param list + * @return + */ + protected List evalListSkip(String rule, List list) { + int skip = 0; + if (rule.matches("!([0-9]+)")) { + rule = rule.substring(1); + } + try { + skip = Integer.parseInt(rule); + Log.d("evalListSkip", "执行成功"); + } catch (Exception ignored) { + } + if (skip > list.size()) skip = 0; + return list.subList(skip, list.size()); + } + + /** + * 执行列表分组反转函数 + * @param rule + * @param list + * @return + */ + protected List evalListGroupReverse(String rule, List list){ + int groupSize = 0; + if (rule.matches("%([0-9]+)")) { + rule = rule.substring(1); + } + try { + groupSize = Integer.parseInt(rule); + Log.d("evalListGroupReverse", "执行成功"); + } catch (Exception ignored) { + } + if (groupSize < 2) return list; + List reverseList = new ArrayList(); + int groupNum = list.size() / groupSize; + if (list.size() % groupSize != 0) groupNum++; + for (int i = 0; i < groupNum; i++) { + int start = i * groupSize; + int end = (i + 1) * groupSize; + if (end > list.size()) end = list.size(); + List subList = list.subList(start, end); + Collections.reverse(subList); + reverseList.addAll(subList); + } + return reverseList; + } + public static String getCharset(String charset) { if (StringHelper.isEmpty(charset)) { charset = "UTF-8"; diff --git a/app/src/main/java/xyz/fycz/myreader/model/source/JsonPathAnalyzer.java b/app/src/main/java/xyz/fycz/myreader/model/source/JsonPathAnalyzer.java index cc75d5b..5800bab 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/source/JsonPathAnalyzer.java +++ b/app/src/main/java/xyz/fycz/myreader/model/source/JsonPathAnalyzer.java @@ -58,13 +58,11 @@ public class JsonPathAnalyzer extends BaseAnalyzer { */ public List getReadContextList(String rule, ReadContext rc) { List list = new ArrayList<>(); - int skip = 0; - if (rule.contains("##!")) { - try { - skip = Integer.parseInt(rule.substring(rule.indexOf("##!") + 3)); - } catch (Exception ignored) { - } - rule = rule.split("##")[0]; + boolean hasFunction = rule.contains("##"); + String funs = ""; + if (hasFunction) { + funs = rule.substring(rule.indexOf("##") + 2); + rule = rule.substring(0, rule.indexOf("##")); } if (StringHelper.isEmpty(rule)) return list; JsonArray temp = rc.read(rule); @@ -73,7 +71,7 @@ public class JsonPathAnalyzer extends BaseAnalyzer { if (str.startsWith("\"")) str = str.substring(1, str.length() - 1); list.add(getReadContext(str)); } - return list.subList(skip, list.size()); + return !hasFunction ? list : evalListFunction(funs, list); } public ReadContext getReadContext(Object obj) { diff --git a/app/src/main/java/xyz/fycz/myreader/model/source/MatcherAnalyzer.java b/app/src/main/java/xyz/fycz/myreader/model/source/MatcherAnalyzer.java index f04780a..9126261 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/source/MatcherAnalyzer.java +++ b/app/src/main/java/xyz/fycz/myreader/model/source/MatcherAnalyzer.java @@ -130,17 +130,15 @@ public class MatcherAnalyzer extends BaseAnalyzer{ */ public ArrayList matchChapters(String rule, String html, String baseUrl) { ArrayList chapters = new ArrayList<>(); + boolean hasFunction = rule.contains("##"); + String funs = ""; + if (hasFunction) { + funs = rule.substring(rule.indexOf("##") + 2); + rule = rule.substring(0, rule.indexOf("##")); + } if (StringHelper.isEmpty(rule)) return chapters; if (!rule.contains("") || !rule.contains("")) return chapters; rule = rule.replace("(*)", ".*"); - int skip = 0; - if (rule.contains("##!")) { - try { - skip = Integer.parseInt(rule.substring(rule.indexOf("##!") + 3)); - } catch (Exception ignored) { - } - rule = rule.split("##")[0]; - } int linkI = 1; int titleI = 2; if (rule.indexOf("") > rule.indexOf("")) { @@ -152,10 +150,8 @@ public class MatcherAnalyzer extends BaseAnalyzer{ rule = rule.replace("", "(.*?)"); Matcher matcher = Pattern.compile(rule).matcher(html); String lastTile = null; - int i = 0; int j = 0; while (matcher.find()) { - if (i++ < skip) continue; String title = matcher.group(titleI); if (!StringHelper.isEmpty(lastTile) && lastTile.equals(title)) continue; Chapter chapter = new Chapter(); @@ -165,7 +161,7 @@ public class MatcherAnalyzer extends BaseAnalyzer{ chapters.add(chapter); lastTile = title; } - return chapters; + return !hasFunction ? chapters : (ArrayList) evalListFunction(funs, chapters); } /** diff --git a/app/src/main/java/xyz/fycz/myreader/model/source/XpathAnalyzer.java b/app/src/main/java/xyz/fycz/myreader/model/source/XpathAnalyzer.java index 6d1246c..3bd106c 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/source/XpathAnalyzer.java +++ b/app/src/main/java/xyz/fycz/myreader/model/source/XpathAnalyzer.java @@ -84,33 +84,28 @@ public class XpathAnalyzer extends BaseAnalyzer { public List getJXNodeList(String rule, JXDocument JXDoc) { List list = new ArrayList<>(); - int skip = 0; - if (rule.contains("##!")) { - try { - skip = Integer.parseInt(rule.substring(rule.indexOf("##!") + 3)); - } catch (Exception ignored) { - } - rule = rule.split("##")[0]; + boolean hasFunction = rule.contains("##"); + String funs = ""; + if (hasFunction) { + funs = rule.substring(rule.indexOf("##") + 2); + rule = rule.substring(0, rule.indexOf("##")); } if (StringHelper.isEmpty(rule)) return list; - List selN = JXDoc.selN(rule); - list = selN.subList(skip, selN.size()); - return list; + list = JXDoc.selN(rule); + return !hasFunction ? list : evalListFunction(funs, list); } public List getJXNodeList(String rule, JXNode jxNode) { List list = new ArrayList<>(); - if (StringHelper.isEmpty(rule)) return list; - int skip = 0; - if (rule.contains("##!")) { - try { - skip = Integer.parseInt(rule.substring(rule.indexOf("##!") + 3)); - } catch (Exception ignored) { - } - rule = rule.split("##")[0]; + boolean hasFunction = rule.contains("##"); + String funs = ""; + if (hasFunction) { + funs = rule.substring(rule.indexOf("##") + 2); + rule = rule.substring(0, rule.indexOf("##")); } - List selN = jxNode.sel(rule); - list = selN.subList(skip, selN.size()); + if (StringHelper.isEmpty(rule)) return list; + list = jxNode.sel(rule); + if (hasFunction) list = evalListFunction(funs, list); return list; } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java index 7cca91e..c6bd143 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java @@ -59,7 +59,7 @@ public class AboutActivity extends BaseActivity { }); binding.il.vwShare.setOnClickListener(v -> ShareUtils.share(this, getString(R.string.share_text) + SharedPreUtils.getInstance().getString(getString(R.string.downloadLink), URLCONST.LAN_ZOUS_URL))); - binding.il.vwUpdate.setOnClickListener(v -> App.checkVersionByServer(this, true, null)); + binding.il.vwUpdate.setOnClickListener(v -> App.checkVersionByServer(this, true)); binding.il.vwUpdateLog.setOnClickListener(v -> DialogCreator.createAssetTipDialog(this, "更新日志", "updatelog.fy")); binding.il.vwQq.setOnClickListener(v -> { if (!App.joinQQGroup(this,"8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn")){ diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/AdSettingActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/AdSettingActivity.java new file mode 100644 index 0000000..1411644 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/AdSettingActivity.java @@ -0,0 +1,171 @@ +package xyz.fycz.myreader.ui.activity; + +import android.os.Bundle; +import android.view.View; + +import androidx.appcompat.widget.Toolbar; + +import io.reactivex.Single; +import io.reactivex.annotations.NonNull; +import io.reactivex.disposables.Disposable; +import xyz.fycz.myreader.R; +import xyz.fycz.myreader.application.SysManager; +import xyz.fycz.myreader.base.BaseActivity; +import xyz.fycz.myreader.base.observer.MySingleObserver; +import xyz.fycz.myreader.databinding.ActivityAdSettingBinding; +import xyz.fycz.myreader.ui.dialog.DialogCreator; +import xyz.fycz.myreader.ui.dialog.LoadingDialog; +import xyz.fycz.myreader.ui.dialog.MyAlertDialog; +import xyz.fycz.myreader.util.DateHelper; +import xyz.fycz.myreader.util.SharedPreUtils; +import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.util.utils.AdUtils; +import xyz.fycz.myreader.util.utils.FileUtils; + +/** + * @author fengyue + * @date 2021/4/23 12:51 + */ +public class AdSettingActivity extends BaseActivity { + + private ActivityAdSettingBinding binding; + private LoadingDialog loadingDialog; + private SharedPreUtils spu; + private int curAdTimes; + private int curAdCount; + private boolean bookDetailAd; + private Disposable cancelDis; + + @Override + protected void bindView() { + binding = ActivityAdSettingBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + } + + @Override + protected void setUpToolbar(Toolbar toolbar) { + super.setUpToolbar(toolbar); + setStatusBarColor(R.color.colorPrimary, true); + getSupportActionBar().setTitle(getString(R.string.ad_setting)); + } + + @Override + protected void initData(Bundle savedInstanceState) { + spu = SharedPreUtils.getInstance(); + curAdTimes = spu.getInt("curAdTimes", 3); + String splashAdCount = spu.getString("splashAdCount"); + String[] splashAdCounts = splashAdCount.split(":"); + String today = DateHelper.getYearMonthDay1(); + if (today.equals(splashAdCounts[0])){ + curAdCount = Integer.parseInt(splashAdCounts[1]); + }else { + curAdCount = 0; + } + bookDetailAd = spu.getBoolean("bookDetailAd", false); + } + + @Override + protected void initWidget() { + loadingDialog = new LoadingDialog(this, "正在加载", () -> { + if (cancelDis != null && !cancelDis.isDisposed()) { + cancelDis.dispose(); + } + }); + loadingDialog.show(); + AdUtils.checkHasAd().subscribe(new MySingleObserver() { + @Override + public void onSubscribe(Disposable d) { + cancelDis = d; + } + + @Override + public void onSuccess(@NonNull Boolean aBoolean) { + binding.scAd.setChecked(aBoolean); + if (aBoolean) { + binding.llAdSetting.setVisibility(View.VISIBLE); + } + loadingDialog.dismiss(); + } + + @Override + public void onError(Throwable e) { + loadingDialog.dismiss(); + } + }); + String curAdTimesStr = getAdTimesStr(curAdTimes); + binding.tvSplashCurAdTimes.setText(getString(R.string.splash_cur_ad_times, curAdTimesStr, curAdCount + "次")); + binding.scBookDetailAd.setChecked(bookDetailAd); + } + + @Override + protected void initClick() { + binding.llSplashAdTimes.setOnClickListener(v -> { + loadingDialog.show(); + AdUtils.adTimes().subscribe(new MySingleObserver() { + @Override + public void onSubscribe(Disposable d) { + cancelDis = d; + } + + @Override + public void onSuccess(@NonNull int[] ints) { + loadingDialog.dismiss(); + int checked = 0; + CharSequence[] adTimes = new CharSequence[ints.length]; + for (int i = 0; i < ints.length; i++) { + int k = ints[i]; + adTimes[i] = getAdTimesStr(k); + if (k == curAdTimes) { + checked = i; + } + } + MyAlertDialog.build(AdSettingActivity.this) + .setTitle(getString(R.string.splash_ad_times)) + .setSingleChoiceItems(adTimes, checked, (dialog, which) -> { + curAdTimes = ints[which]; + spu.putInt("curAdTimes", curAdTimes); + binding.tvSplashCurAdTimes.setText(getString(R.string.splash_cur_ad_times, adTimes[which], curAdCount + "次")); + dialog.dismiss(); + }).setNegativeButton("取消", null).show(); + } + + @Override + public void onError(Throwable e) { + loadingDialog.dismiss(); + } + }); + }); + binding.rlBookDetailAd.setOnClickListener(v -> { + bookDetailAd = !bookDetailAd; + spu.putBoolean("bookDetailAd", bookDetailAd); + binding.scBookDetailAd.setChecked(bookDetailAd); + }); + binding.rlDeleteAdFile.setOnClickListener(v -> { + FileUtils.deleteFile(FileUtils.getFilePath()); + ToastUtils.showSuccess("广告文件删除成功"); + }); + } + + @Override + protected void processLogic() { + super.processLogic(); + } + + @Override + protected void onDestroy() { + if (loadingDialog != null) { + loadingDialog.dismiss(); + } + super.onDestroy(); + } + + private String getAdTimesStr(int adTimes) { + if (adTimes == -1) { + return "一直显示"; + } else if (adTimes == 0) { + return "不显示"; + } else { + return adTimes + "次"; + } + } +} 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 9a8dc16..f2a21a1 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 @@ -26,6 +26,7 @@ import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.request.RequestOptions; import com.google.zxing.EncodeHintType; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.weaction.ddsdk.ad.DdSdkFlowAd; import java.io.File; import java.io.FileOutputStream; @@ -38,6 +39,7 @@ import io.reactivex.Single; import io.reactivex.SingleOnSubscribe; import io.reactivex.annotations.NonNull; import xyz.fycz.myreader.R; +import xyz.fycz.myreader.ai.BookWordCountPre; import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.application.SysManager; import xyz.fycz.myreader.base.BaseActivity; @@ -63,6 +65,7 @@ import xyz.fycz.myreader.util.ShareUtils; import xyz.fycz.myreader.util.SharedPreUtils; import xyz.fycz.myreader.util.StringHelper; import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.util.utils.AdUtils; import xyz.fycz.myreader.util.utils.BitmapUtil; import xyz.fycz.myreader.util.utils.BlurTransformation; import xyz.fycz.myreader.util.utils.FileUtils; @@ -83,6 +86,7 @@ import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; public class BookDetailedActivity extends BaseActivity { private ActivityBookDetailBinding binding; + private static final String TAG = BookDetailedActivity.class.getSimpleName(); private Book mBook; private ArrayList aBooks; @@ -223,6 +227,45 @@ public class BookDetailedActivity extends BaseActivity { } mSourceDialog.setABooks(aBooks); mSourceDialog.setSourceIndex(sourceIndex); + + initAd(); + } + /** + * type 信息流类型,只能填 1、4、6、3 (样式请看下表) + * count 请求返回的信息流广告条目数,最小为 1,最大为 5,通常情况下建议一次返回一条 + */ + private void initAd() { + if (SharedPreUtils.getInstance().getBoolean("bookDetailAd", false)) { + AdUtils.checkHasAd().subscribe(new MySingleObserver() { + @Override + public void onSuccess(@NonNull Boolean aBoolean) { + if (aBoolean){ + AdUtils.initAd(); + new DdSdkFlowAd().getFlowViews(BookDetailedActivity.this, 6, 1, new DdSdkFlowAd.FlowCallback() { + // 信息流广告拉取完毕后返回的 views + @Override + public void getFlowViews(List views) { + Log.i(TAG, "信息流广告拉取完毕后返回了" + views.size() + "个view"); + binding.ic.getRoot().addView(views.get(0), 2); + } + + // 信息流广告展示后调用 + @Override + public void show() { + AdUtils.adRecord("flow","adShow"); + Log.i(TAG, "信息流广告展示成功"); + } + + // 广告拉取失败调用 + @Override + public void error(String msg) { + Log.i(TAG, "广告拉取失败\n" + msg); + } + }); + } + } + }); + } } @Override @@ -253,6 +296,7 @@ public class BookDetailedActivity extends BaseActivity { binding.ib.bookDetailTvAdd.setText("加入书架"); binding.ib.bookDetailTvOpen.setText("开始阅读"); } + invalidateOptionsMenu(); }); binding.ib.flOpenBook.setOnClickListener(view -> goReadActivity()); @@ -363,6 +407,7 @@ public class BookDetailedActivity extends BaseActivity { }); } else { initOtherInfo(); + //predictBookCount(); } } @@ -521,17 +566,22 @@ public class BookDetailedActivity extends BaseActivity { public boolean onPrepareOptionsMenu(Menu menu) { if ("本地书籍".equals(mBook.getType())) { MenuItem groupSetting = menu.findItem(R.id.action_group_setting); + MenuItem edit = menu.findItem(R.id.action_edit); groupSetting.setVisible(isCollected); + edit.setVisible(isCollected); } else { MenuItem isUpdate = menu.findItem(R.id.action_is_update); MenuItem groupSetting = menu.findItem(R.id.action_group_setting); + MenuItem edit = menu.findItem(R.id.action_edit); if (isCollected) { isUpdate.setVisible(true); groupSetting.setVisible(true); + edit.setVisible(true); isUpdate.setChecked(!mBook.getIsCloseUpdate()); } else { isUpdate.setVisible(false); groupSetting.setVisible(false); + edit.setVisible(false); } } return super.onPrepareOptionsMenu(menu); @@ -574,6 +624,11 @@ public class BookDetailedActivity extends BaseActivity { case R.id.action_share: shareBook(); break; + case R.id.action_edit: + Intent editIntent = new Intent(this, BookInfoEditActivity.class); + editIntent.putExtra(APPCONST.BOOK, mBook); + startActivityForResult(editIntent, APPCONST.REQUEST_EDIT_BOOK); + break; case R.id.action_reload: //重新加载 mHandler.sendEmptyMessage(1); break; @@ -647,6 +702,10 @@ public class BookDetailedActivity extends BaseActivity { mBook.setLastReadPosition(chapterAndPage[1]); goReadActivity(); break; + case APPCONST.REQUEST_EDIT_BOOK: + mBook = BookService.getInstance().getBookById(mBook.getId()); + initBookInfo(); + break; } } } @@ -836,4 +895,18 @@ public class BookDetailedActivity extends BaseActivity { } return lines; } + + + private void predictBookCount(){ + if (isCollected) { + BookWordCountPre bwcp = new BookWordCountPre(mBook); + App.getApplication().newThread(() ->{ + if (bwcp.train()){ + int count = bwcp.predict(); + mBook.setWordCount(String.valueOf(count)); + App.runOnUiThread(this::initTagList); + } + }); + } + } } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookInfoEditActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookInfoEditActivity.java new file mode 100644 index 0000000..d23a8de --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookInfoEditActivity.java @@ -0,0 +1,162 @@ +package xyz.fycz.myreader.ui.activity; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +import androidx.appcompat.widget.Toolbar; + +import xyz.fycz.myreader.R; +import xyz.fycz.myreader.application.App; +import xyz.fycz.myreader.base.BaseActivity; +import xyz.fycz.myreader.common.APPCONST; +import xyz.fycz.myreader.databinding.ActivityBookInfoEditBinding; +import xyz.fycz.myreader.greendao.entity.Book; +import xyz.fycz.myreader.greendao.service.BookService; +import xyz.fycz.myreader.model.source.BookSourceManager; +import xyz.fycz.myreader.ui.dialog.DialogCreator; +import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.util.UriFileUtil; +import xyz.fycz.myreader.util.utils.NetworkUtils; +import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; + +/** + * @author fengyue + * @date 2021/4/24 15:05 + */ +public class BookInfoEditActivity extends BaseActivity { + private ActivityBookInfoEditBinding binding; + private Book mBook; + private String imgUrl; + private String bookName; + private String author; + private String desc; + + @Override + protected void bindView() { + binding = ActivityBookInfoEditBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + } + + @Override + protected void setUpToolbar(Toolbar toolbar) { + super.setUpToolbar(toolbar); + setStatusBarColor(R.color.colorPrimary, true); + getSupportActionBar().setTitle("书籍信息编辑"); + } + + @Override + protected boolean initSwipeBackEnable() { + return false; + } + + @Override + protected void initData(Bundle savedInstanceState) { + super.initData(savedInstanceState); + mBook = (Book) getIntent().getSerializableExtra(APPCONST.BOOK); + if (mBook == null) { + ToastUtils.showError("未读取到书籍信息"); + finish(); + } + imgUrl = NetworkUtils.getAbsoluteURL(ReadCrawlerUtil.getReadCrawler(mBook.getSource()).getNameSpace(), mBook.getImgUrl()); + bookName = mBook.getName(); + author = mBook.getAuthor(); + desc = mBook.getDesc(); + } + + @Override + protected void initWidget() { + super.initWidget(); + initImg(imgUrl); + binding.tieBookName.setText(bookName); + binding.tieBookAuthor.setText(author); + binding.tieCoverUrl.setText(imgUrl); + binding.tieBookDesc.setText(desc); + } + + @Override + protected void initClick() { + binding.btSelectLocalPic.setOnClickListener(v -> { + ToastUtils.showInfo("请选择一张图片"); + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("image/*"); + startActivityForResult(intent, APPCONST.REQUEST_SELECT_COVER); + }); + } + + // 添加菜单 + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_book_info_edit, menu); + return true; + } + + //菜单 + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_save) { + saveInfo(); + } + return super.onOptionsItemSelected(item); + } + + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == APPCONST.REQUEST_SELECT_COVER) { + if (resultCode == RESULT_OK && null != data) { + String imgUrl = UriFileUtil.getPath(this, data.getData()); + binding.tieCoverUrl.setText(imgUrl); + initImg(imgUrl); + } + } + } + + @Override + public void finish() { + if (hasChange()) { + DialogCreator.createThreeButtonDialog(this, "退出" + , "当前书籍信息已更改,是否保存?", true, + "直接退出", "取消", "保存并退出", + (dialog, which) -> super.finish(), null, + (dialog, which) -> { + saveInfo(); + super.finish(); + } + ); + } else { + super.finish(); + } + } + + private void initImg(String imgUrl) { + if (!App.isDestroy(this)) { + binding.ivCover.load(imgUrl, mBook.getName(), mBook.getAuthor()); + } + } + + private void saveInfo() { + bookName = binding.tieBookName.getText().toString(); + author = binding.tieBookAuthor.getText().toString(); + imgUrl = binding.tieCoverUrl.getText().toString(); + desc = binding.tieBookDesc.getText().toString(); + mBook.setName(bookName); + mBook.setAuthor(author); + mBook.setImgUrl(imgUrl); + mBook.setDesc(desc); + BookService.getInstance().updateEntity(mBook); + ToastUtils.showSuccess("书籍信息保存成功"); + setResult(Activity.RESULT_OK); + } + + private boolean hasChange() { + return !bookName.equals(binding.tieBookName.getText().toString()) || + !author.equals(binding.tieBookAuthor.getText().toString()) || + !imgUrl.equals(binding.tieCoverUrl.getText().toString()) || + !desc.equals(binding.tieBookDesc.getText().toString()); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/DonateActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/DonateActivity.java new file mode 100644 index 0000000..4ce10a6 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/DonateActivity.java @@ -0,0 +1,134 @@ +package xyz.fycz.myreader.ui.activity; + +import android.content.Intent; +import android.net.Uri; +import android.util.Log; +import android.view.View; + +import androidx.appcompat.widget.Toolbar; + +import com.sigmob.sdk.common.models.sigdsp.pb.Ad; +import com.weaction.ddsdk.ad.DdSdkFlowAd; +import com.weaction.ddsdk.ad.DdSdkRewardAd; + +import java.util.List; + +import io.reactivex.annotations.NonNull; +import xyz.fycz.myreader.R; +import xyz.fycz.myreader.base.BaseActivity; +import xyz.fycz.myreader.base.observer.MySingleObserver; +import xyz.fycz.myreader.common.URLCONST; +import xyz.fycz.myreader.databinding.ActivityDonateBinding; +import xyz.fycz.myreader.util.SharedPreUtils; +import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.util.utils.AdUtils; + +/** + * @author fengyue + * @date 2021/4/23 21:23 + */ +public class DonateActivity extends BaseActivity { + + private ActivityDonateBinding binding; + private static final String TAG = DonateActivity.class.getSimpleName(); + + @Override + protected void bindView() { + binding = ActivityDonateBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + } + + @Override + protected void setUpToolbar(Toolbar toolbar) { + super.setUpToolbar(toolbar); + setStatusBarColor(R.color.colorPrimary, true); + getSupportActionBar().setTitle(getString(R.string.support_author)); + } + + @Override + protected void initWidget() { + AdUtils.checkHasAd().subscribe(new MySingleObserver() { + @Override + public void onSuccess(@NonNull Boolean aBoolean) { + if (aBoolean) { + AdUtils.initAd(); + initAd(); + } + } + }); + } + + private void initAd() { + binding.llAdSupport.setVisibility(View.VISIBLE); + new DdSdkFlowAd().getFlowViews(DonateActivity.this, 6, 1, new DdSdkFlowAd.FlowCallback() { + // 信息流广告拉取完毕后返回的 views + @Override + public void getFlowViews(List views) { + Log.i(TAG, "信息流广告拉取完毕后返回了" + views.size() + "个view"); + binding.llAdSupport.addView(views.get(0), 2); + } + + // 信息流广告展示后调用 + @Override + public void show() { + AdUtils.adRecord("flow","adShow"); + Log.i(TAG, "信息流广告展示成功"); + } + + // 广告拉取失败调用 + @Override + public void error(String msg) { + Log.i(TAG, "广告拉取失败\n" + msg); + } + }); + } + + @Override + protected void initClick() { + binding.llWxZsm.setOnClickListener(v -> goDonate(URLCONST.WX_ZSM)); + binding.llZfbSkm.setOnClickListener(v -> goDonate(URLCONST.ZFB_SKM)); + binding.llQqSkm.setOnClickListener(v -> goDonate(URLCONST.QQ_SKM)); + binding.llRewardedVideo.setOnClickListener(v -> { + DdSdkRewardAd.show(this, new DdSdkRewardAd.DdSdkRewardCallback() { + @Override + public void show() { + Log.i(TAG, "激励视频展示成功"); + AdUtils.adRecord("rewardVideo","adShow"); + } + + @Override + public void click() { + Log.i(TAG, "激励视频被点击"); + AdUtils.adRecord("rewardVideo","adClick"); + } + + @Override + public void error(String msg) { + } + + @Override + public void skip() { + Log.i(TAG, "激励视频被跳过"); + AdUtils.adRecord("rewardVideo","adSkip"); + } + + @Override + public void finishCountdown() { + Log.i(TAG, "激励视频计时完成"); + AdUtils.adRecord("rewardVideo","adFinishCount"); + } + }); + }); + } + + + private void goDonate(String address) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(address)); + startActivity(intent); + } catch (Exception e) { + ToastUtils.showError(e.getLocalizedMessage()); + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java index b18d635..524e324 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java @@ -98,6 +98,11 @@ public class MainActivity extends BaseActivity { setStatusBarColor(R.color.colorPrimary, true); } + @Override + protected boolean initSwipeBackEnable() { + return false; + } + @Override protected void initData(Bundle savedInstanceState) { super.initData(savedInstanceState); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java index 23e6b1f..123b1fd 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java @@ -156,7 +156,7 @@ public class MoreSettingActivity extends BaseActivity { @Override protected void processLogic() { - if(isWebDav){ + if (isWebDav) { binding.llWebdav.performClick(); } } @@ -470,38 +470,35 @@ public class MoreSettingActivity extends BaseActivity { binding.rlCatheGap.setOnClickListener(v -> binding.scCatheGap.performClick()); binding.rlDeleteCathe.setOnClickListener(v -> { - App.runOnUiThread(() -> { - File catheFile = getCacheDir(); - String catheFileSize = FileUtils.getFileSize(FileUtils.getDirSize(catheFile)); - - File eCatheFile = new File(BOOK_CACHE_PATH); - String eCatheFileSize; - if (eCatheFile.exists() && eCatheFile.isDirectory()) { - eCatheFileSize = FileUtils.getFileSize(FileUtils.getDirSize(eCatheFile)); - } else { - eCatheFileSize = "0"; - } - CharSequence[] cathes = {"章节缓存:" + eCatheFileSize, "图片缓存:" + catheFileSize}; - boolean[] catheCheck = {true, true}; - new MultiChoiceDialog().create(this, "清除缓存", cathes, catheCheck, 2, - (dialog, which) -> { - String tip = ""; - if (catheCheck[0]) { - BookService.getInstance().deleteAllBookCathe(); - tip += "章节缓存 "; - } - if (catheCheck[1]) { - FileUtils.deleteFile(catheFile.getAbsolutePath()); - tip += "图片缓存 "; - } - if (tip.length() > 0) { - tip += "清除成功"; - ToastUtils.showSuccess(tip); - } - }, null, null); - }); - }); + File catheFile = getCacheDir(); + String catheFileSize = FileUtils.getFileSize(FileUtils.getDirSize(catheFile)); + File eCatheFile = new File(BOOK_CACHE_PATH); + String eCatheFileSize; + if (eCatheFile.exists() && eCatheFile.isDirectory()) { + eCatheFileSize = FileUtils.getFileSize(FileUtils.getDirSize(eCatheFile)); + } else { + eCatheFileSize = "0"; + } + CharSequence[] cathes = {"章节缓存:" + eCatheFileSize, "图片缓存:" + catheFileSize}; + boolean[] catheCheck = {true, true}; + new MultiChoiceDialog().create(this, "清除缓存", cathes, catheCheck, 2, + (dialog, which) -> { + String tip = ""; + if (catheCheck[0]) { + BookService.getInstance().deleteAllBookCathe(); + tip += "章节缓存 "; + } + if (catheCheck[1]) { + FileUtils.deleteFile(catheFile.getAbsolutePath()); + tip += "图片缓存 "; + } + if (tip.length() > 0) { + tip += "清除成功"; + ToastUtils.showSuccess(tip); + } + }, null, null); + }); } @@ -539,18 +536,21 @@ public class MoreSettingActivity extends BaseActivity { int resetScreenSelection = 0; switch (resetScreenTime) { - case 0: + case -1: resetScreenSelection = 0; break; - case 1: + case 0: resetScreenSelection = 1; break; - case 3: + case 1: resetScreenSelection = 2; break; - case 5: + case 3: resetScreenSelection = 3; break; + case 5: + resetScreenSelection = 4; + break; } binding.scResetScreen.setSelection(resetScreenSelection); binding.scResetScreen.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @@ -558,15 +558,18 @@ public class MoreSettingActivity extends BaseActivity { public void onItemSelected(AdapterView> parent, View view, int position, long id) { switch (position) { case 0: - resetScreenTime = 0; + resetScreenTime = -1; break; case 1: - resetScreenTime = 1; + resetScreenTime = 0; break; case 2: - resetScreenTime = 3; + resetScreenTime = 1; break; case 3: + resetScreenTime = 3; + break; + case 4: resetScreenTime = 5; break; } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java index 4bbe032..0eaefbd 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java @@ -276,6 +276,11 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe getSupportActionBar().setTitle(mBook.getName()); } + @Override + protected boolean initSwipeBackEnable() { + return false; + } + @Override protected void initData(Bundle savedInstanceState) { super.initData(savedInstanceState); @@ -1481,7 +1486,11 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe * 重置黑屏时间 */ private void screenOffTimerStart() { - if (screenTimeOut <= 0) { + if (screenTimeOut < 0) { + keepScreenOn(false); + return; + } + if (screenTimeOut == 0) { keepScreenOn(true); return; } @@ -1678,7 +1687,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe private View vProtectEye; private void initEyeView() { - ViewGroup content = findViewById(android.R.id.content); + ViewGroup content = (ViewGroup) findViewById(android.R.id.content); vProtectEye = new FrameLayout(this); vProtectEye.setBackgroundColor(mSetting.isProtectEye() ? getFilterColor(mSetting.getBlueFilterPercent()) : Color.TRANSPARENT); //设置透明 WindowManager.LayoutParams params = new WindowManager.LayoutParams(); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceDebugActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceDebugActivity.java index 7b20bc2..a622b15 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceDebugActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceDebugActivity.java @@ -29,10 +29,10 @@ import xyz.fycz.myreader.entity.sourcedebug.DebugEntity; import xyz.fycz.myreader.entity.sourcedebug.ListResult; import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.Chapter; +import xyz.fycz.myreader.ui.dialog.LoadingDialog; import xyz.fycz.myreader.util.utils.GsonExtensionsKt; import xyz.fycz.myreader.util.utils.NetworkUtils; import xyz.fycz.myreader.util.utils.OkHttpUtils; -import xyz.fycz.myreader.util.utils.ProgressUtils; import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler; @@ -49,6 +49,7 @@ public class SourceDebugActivity extends BaseActivity { private DebugEntity debugEntity; private ReadCrawler rc; private Disposable disposable; + private LoadingDialog loadingDialog; @Override protected void bindView() { @@ -83,6 +84,11 @@ public class SourceDebugActivity extends BaseActivity { super.initData(savedInstanceState); debugEntity = getIntent().getParcelableExtra("debugEntity"); rc = ReadCrawlerUtil.getReadCrawler(debugEntity.getBookSource(), true); + loadingDialog = new LoadingDialog(this, "正在请求", () -> { + if (disposable != null && !disposable.isDisposed()) { + disposable.dispose(); + } + }); } @Override @@ -118,18 +124,11 @@ public class SourceDebugActivity extends BaseActivity { }); } - @Override - public void onBackPressed() { - if (ProgressUtils.isShowing()) { - ProgressUtils.dismiss(); - } else { - super.onBackPressed(); - } - } - @Override protected void onDestroy() { - ProgressUtils.dismiss(); + if (loadingDialog != null) { + loadingDialog.dismiss(); + } super.onDestroy(); } @@ -149,11 +148,7 @@ public class SourceDebugActivity extends BaseActivity { } private void initDebugEntity() { - ProgressUtils.show(this, "正在请求...", "取消", (dialog, which) -> { - if (disposable != null) { - disposable.dispose(); - } - }); + loadingDialog.show(); Observable.create((ObservableOnSubscribe) emitter -> { try { String url = debugEntity.getUrl(); @@ -209,7 +204,7 @@ public class SourceDebugActivity extends BaseActivity { public void onNext(@NonNull Boolean flag) { binding.rvParseResult.setCode(debugEntity.getParseResult()).apply(); binding.rvSourceCode.setCode(debugEntity.getHtml()).apply(); - ProgressUtils.dismiss(); + loadingDialog.dismiss(); } @Override @@ -217,7 +212,7 @@ public class SourceDebugActivity extends BaseActivity { binding.rvParseResult.setCode(String.format("{\n\b\b\b\b\"result\": \"error\", \n\b\b\b\b\"msg\": \"%s\"\n}" , e.getLocalizedMessage().replace("\"", "\\\""))).apply(); binding.rvSourceCode.setCode(debugEntity.getHtml()).apply(); - ProgressUtils.dismiss(); + loadingDialog.dismiss(); } }); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java index ea60fb0..f43a235 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java @@ -65,6 +65,11 @@ public class SourceEditActivity extends BaseActivity { getSupportActionBar().setTitle("书源编辑"); } + @Override + protected boolean initSwipeBackEnable() { + return false; + } + @Override protected void initData(Bundle savedInstanceState) { super.initData(savedInstanceState); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java index 2acbe2a..fd9f650 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java @@ -6,21 +6,27 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; +import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.signature.ObjectKey; import com.gyf.immersionbar.ImmersionBar; +import com.weaction.ddsdk.ad.DdSdkSplashAd; import java.io.File; import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.base.BaseActivity; +import xyz.fycz.myreader.base.observer.MySingleObserver; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.databinding.ActivitySplashBinding; import xyz.fycz.myreader.greendao.service.BookGroupService; @@ -30,6 +36,7 @@ import xyz.fycz.myreader.util.IOUtils; import xyz.fycz.myreader.util.PermissionsChecker; import xyz.fycz.myreader.util.SharedPreUtils; import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.util.utils.AdUtils; import xyz.fycz.myreader.util.utils.ImageLoader; import xyz.fycz.myreader.util.utils.MD5Utils; import xyz.fycz.myreader.util.utils.OkHttpUtils; @@ -37,7 +44,8 @@ import xyz.fycz.myreader.util.utils.SystemBarUtils; public class SplashActivity extends BaseActivity { /*************Constant**********/ - private static int WAIT_INTERVAL = 1000; + public static final String TAG = SplashActivity.class.getSimpleName(); + public static int WAIT_INTERVAL = 0; private static final int PERMISSIONS_REQUEST_STORAGE = 1; static final String[] PERMISSIONS = { @@ -46,6 +54,9 @@ public class SplashActivity extends BaseActivity { }; private ActivitySplashBinding binding; + private SharedPreUtils spu; + private int todayAdCount; + private int adTimes; private PermissionsChecker mPermissionsChecker; private Thread myThread = new Thread() {//创建子线程 @@ -63,6 +74,23 @@ public class SplashActivity extends BaseActivity { } }; + private Thread countTime = new Thread(){ + @Override + public void run() { + for (int i = 0; i < 8; i++) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (!App.isDestroy(SplashActivity.this)){ + WAIT_INTERVAL = 0; + startNormal(); + } + } + }; + @Override protected void bindView() { @@ -82,18 +110,82 @@ public class SplashActivity extends BaseActivity { .fullScreen(true) .init(); SystemBarUtils.hideStableStatusBar(this); - loadImage(); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M){ + //loadImage(); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { mPermissionsChecker = new PermissionsChecker(this); requestPermission(); - }else { + } else { start(); } } - private void start(){ + @Override + protected boolean initSwipeBackEnable() { + return false; + } + + @Override + protected void initData(Bundle savedInstanceState) { + spu = SharedPreUtils.getInstance(); + String splashAdCount = spu.getString("splashAdCount"); + adTimes = spu.getInt("curAdTimes", 3); + String[] splashAdCounts = splashAdCount.split(":"); + String today = DateHelper.getYearMonthDay1(); + if (today.equals(splashAdCounts[0])){ + todayAdCount = Integer.parseInt(splashAdCounts[1]); + }else { + todayAdCount = 0; + } + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { + return true; + } + return super.onKeyDown(keyCode, event); + } + + private void start() { + if (adTimes >= 0 && todayAdCount >= adTimes){ + startNoAd(); + } else { + AdUtils.checkHasAd() + .subscribe(new MySingleObserver() { + @Override + public void onSuccess(@NonNull Boolean aBoolean) { + if (aBoolean) { + AdUtils.initAd(); + startWithAd(); + binding.ivSplash.setVisibility(View.GONE); + binding.llAd.setVisibility(View.VISIBLE); + } else { + startNoAd(); + } + + } + + @Override + public void onError(Throwable e) { + startNoAd(); + } + }); + } + } + + private void startNoAd(){ + Animation inAni = AnimationUtils.loadAnimation(SplashActivity.this, R.anim.fade_in); + binding.ivSplash.setVisibility(View.VISIBLE); + binding.ivSplash.startAnimation(inAni); + binding.llAd.setVisibility(View.GONE); + WAIT_INTERVAL = 1500; + loadImage(); + startNormal(); + } + + private void startNormal() { if (BookGroupService.getInstance().curGroupIsPrivate()) { - App.runOnUiThread(() ->{ + App.runOnUiThread(() -> { MyAlertDialog.showPrivateVerifyDia(SplashActivity.this, needGoTo -> { myThread.start(); }, () -> { @@ -102,17 +194,64 @@ public class SplashActivity extends BaseActivity { myThread.start(); }); }); - }else { + } else { myThread.start(); } } + private void startWithAd() { + try { + new DdSdkSplashAd().show(binding.flAd, this, new DdSdkSplashAd.CountdownCallback() { + // 展示成功 + @Override + public void show() { + Log.d(TAG, "广告展示成功"); + AdUtils.adRecord("splash","adShow"); + countTodayAd(); + countTime.start(); + } + + // 广告被点击 + @Override + public void click() { + Log.d(TAG, "广告被点击"); + AdUtils.adRecord("splash","adClick"); + } + + // 展示出错时可读取 msg 中的错误信息 + @Override + public void error(String msg) { + WAIT_INTERVAL = 1500; + if (!App.isDestroy(SplashActivity.this)) + startNormal(); + Log.e(TAG, msg); + //ToastUtils.showError(msg); + } + + // 倒计时结束或用户主动点击跳过按钮后调用 + @Override + public void finishCountdown() { + Log.d(TAG, "倒计时结束或用户主动点击跳过按钮"); + AdUtils.adRecord("splash","adFinishCount"); + if (!App.isDestroy(SplashActivity.this)) + startNormal(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + WAIT_INTERVAL = 1500; + if (!App.isDestroy(SplashActivity.this)) + startNormal(); + } + } + + private void loadImage() { File imgFile = getFileStreamPath(APPCONST.FILE_NAME_SPLASH_IMAGE); SharedPreUtils preUtils = SharedPreUtils.getInstance(); String splashImageMD5 = preUtils.getString("splashImageMD5"); if (!imgFile.exists() || preUtils.getBoolean("needUdSI") || - !splashImageMD5.equals(MD5Utils.getFileMD5s(imgFile, 16))){ + !splashImageMD5.equals(MD5Utils.getFileMD5s(imgFile, 16))) { if ("".equals(splashImageMD5)) return; downLoadImage(); return; @@ -129,18 +268,22 @@ public class SplashActivity extends BaseActivity { startTime = DateHelper.strDateToLong(splashLoadDates[0] + " 00:00:00"); endTime = DateHelper.strDateToLong(splashLoadDates[1] + " 00:00:00"); } - if (startTime == 0){ + if (startTime == 0) { startTime = DateHelper.strDateToLong(splashLoadDate + " 00:00:00"); } - if (endTime == 0){ + if (endTime == 0) { endTime = startTime + 24 * 60 * 60 * 1000; } - if (curTime >= startTime && curTime <= endTime){ + if (curTime >= startTime && curTime <= endTime) { WAIT_INTERVAL = 1500; + RequestOptions options = new RequestOptions() + .error(R.drawable.start) + .signature(new ObjectKey(splashLoadDate)); ImageLoader.INSTANCE .load(this, imgFile) - .error(R.drawable.start) - .signature(new ObjectKey(splashLoadDate)) + /*.error(R.drawable.start) + .signature(new ObjectKey(splashLoadDate))*/ + .apply(options) .into(binding.ivSplash); } } @@ -156,27 +299,27 @@ public class SplashActivity extends BaseActivity { fos = openFileOutput(APPCONST.FILE_NAME_SPLASH_IMAGE, MODE_PRIVATE); byte[] bytes = new byte[1024]; int len; - while ((len = is.read(bytes)) != -1){ + while ((len = is.read(bytes)) != -1) { fos.write(bytes, 0, len); } fos.flush(); Log.d("SplashActivity", "downLoadImage success!"); } catch (Exception e) { File data = getFileStreamPath(APPCONST.FILE_NAME_SPLASH_IMAGE); - if (data != null && data.exists()){ + if (data != null && data.exists()) { data.delete(); } e.printStackTrace(); - }finally { + } finally { IOUtils.close(is, fos); } } }); } - private void requestPermission(){ + private void requestPermission() { //获取读取和写入SD卡的权限 - if (mPermissionsChecker.lacksPermissions(PERMISSIONS)){ + if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) { ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSIONS_REQUEST_STORAGE); } else { start(); @@ -202,4 +345,10 @@ public class SplashActivity extends BaseActivity { } } } + + private void countTodayAd() { + String today = DateHelper.getYearMonthDay1(); + todayAdCount++; + spu.putString("splashAdCount", today + ":" + todayAdCount); + } } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/LoadingDialog.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/LoadingDialog.java new file mode 100644 index 0000000..5913428 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/dialog/LoadingDialog.java @@ -0,0 +1,122 @@ +package xyz.fycz.myreader.ui.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.WindowManager; +import android.view.animation.LinearInterpolator; +import android.view.animation.RotateAnimation; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import xyz.fycz.myreader.R; +import xyz.fycz.myreader.common.APPCONST; +import xyz.fycz.myreader.databinding.DialogLoadingBinding; +import xyz.fycz.myreader.util.ToastUtils; + +import static org.jetbrains.anko.AnkoContextKt.setContentView; + +/** + * @author fengyue + * @date 2021/4/22 17:19 + */ +public class LoadingDialog extends Dialog { + + private static final String TAG = "LoadingDialog"; + + private DialogLoadingBinding binding; + + private String mMessage; + //private int mImageId; + private boolean mCancelable; + private RotateAnimation mRotateAnimation; + private long cancelTime; + private OnCancelListener mOnCancelListener; + + public LoadingDialog(@NonNull Context context, String message, OnCancelListener onCancelListener) { + this(context, R.style.LoadingDialog, message, false, onCancelListener); + } + + public LoadingDialog(@NonNull Context context, int themeResId, String message, boolean cancelable, OnCancelListener onCancelListener) { + super(context, themeResId); + mMessage = message; + //mImageId = imageId; + mCancelable = cancelable; + mOnCancelListener = onCancelListener; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = DialogLoadingBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + initView(); + } + + private void initView() { + // 设置窗口大小 + WindowManager windowManager = getWindow().getWindowManager(); + int screenWidth = windowManager.getDefaultDisplay().getWidth(); + WindowManager.LayoutParams attributes = getWindow().getAttributes(); + attributes.alpha = 0.3f; + attributes.width = screenWidth / 3; + attributes.height = attributes.width; + getWindow().setAttributes(attributes); + setCancelable(mCancelable); + + binding.tvLoading.setText(mMessage); + //iv_loading.setImageResource(mImageId); + binding.ivLoading.measure(0, 0); + mRotateAnimation = new RotateAnimation(0, 360, binding.ivLoading.getMeasuredWidth() / 2f, + binding.ivLoading.getMeasuredHeight() / 2f); + mRotateAnimation.setInterpolator(new LinearInterpolator()); + mRotateAnimation.setDuration(1000); + mRotateAnimation.setRepeatCount(-1); + } + + public void setmMessage(String mMessage) { + this.mMessage = mMessage; + binding.tvLoading.setText(mMessage); + } + + @Override + public void show() { + super.show(); + binding.ivLoading.startAnimation(mRotateAnimation); + } + + @Override + public void dismiss() { + if (isShowing()) { + mRotateAnimation.cancel(); + super.dismiss(); + } + } + + @Override + public void onBackPressed() { + if (mCancelable) { + super.onBackPressed(); + dismiss(); + mOnCancelListener.cancel(); + } else { + if (System.currentTimeMillis() - cancelTime > APPCONST.exitConfirmTime) { + ToastUtils.showInfo("再按一次取消"); + cancelTime = System.currentTimeMillis(); + } else { + super.onBackPressed(); + dismiss(); + mOnCancelListener.cancel(); + } + } + } + + + public interface OnCancelListener { + void cancel(); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/UpdateDialog.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/UpdateDialog.java new file mode 100644 index 0000000..22f1aca --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/ui/dialog/UpdateDialog.java @@ -0,0 +1,396 @@ +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; +import android.os.Environment; +import android.os.Handler; +import android.text.Html; +import android.text.TextUtils; +import android.text.method.ScrollingMovementMethod; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +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.liulishuo.filedownloader.BaseDownloadTask; +import com.liulishuo.filedownloader.FileDownloadLargeFileListener; +import com.liulishuo.filedownloader.FileDownloader; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLDecoder; + +import xyz.fycz.myreader.R; +import xyz.fycz.myreader.application.App; +import xyz.fycz.myreader.databinding.FragmentUpdateBinding; +import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.webapi.CommonApi; +import xyz.fycz.myreader.webapi.callback.ResultCallback; + + +public class UpdateDialog extends Fragment { + + private String version, content, appUrl, path; + private boolean isContentHtml = false; + private boolean cancelable; + // private OnConfirmListener listener; + private FragmentActivity mActivity; + private final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0x100; + private BaseDownloadTask baseDownloadTask; + private boolean debug = false; + private FragmentUpdateBinding binding; + + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mActivity = (FragmentActivity) context; + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + binding = FragmentUpdateBinding.inflate(inflater, container, false); + return binding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + FileDownloader.setup(mActivity); + initViews(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + if (baseDownloadTask != null && (!baseDownloadTask.isRunning() || baseDownloadTask.pause())) + baseDownloadTask = null; + } + + private void initViews() { + binding.tvContent.setMovementMethod(ScrollingMovementMethod.getInstance()); + binding.ivClose.setVisibility(cancelable ? View.VISIBLE : View.GONE); + binding.btUpdate.setOnClickListener(v -> { +// if (listener != null) { +// listener.onConfirm(v); +// } else { + if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_WRITE_EXTERNAL_STORAGE); + } else { + downloadApk(appUrl); + } +// } + }); + binding.btBrowser.setOnClickListener(v -> { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setData(Uri.parse(appUrl)); + mActivity.startActivity(intent); + }); + binding.rlProgress.setOnClickListener(v -> { + if (binding.barPercentView.getProgress() == 1) { + install(new File(path), mActivity); + } + }); + binding.ivClose.setOnClickListener(v -> { + if (baseDownloadTask == null) { + dismissUpdateDialog(); + return; + } + if (!baseDownloadTask.isRunning() || baseDownloadTask.pause()) { + dismissUpdateDialog(); + } + }); + binding.tvVersion.setText(TextUtils.isEmpty(version) ? "" : version); + if (isContentHtml) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + binding.tvContent.setText(Html.fromHtml(TextUtils.isEmpty(content) ? "" : content, Html.FROM_HTML_MODE_LEGACY)); + } else { + binding.tvContent.setText(Html.fromHtml(TextUtils.isEmpty(content) ? "" : content)); + } + } else { + binding.tvContent.setText(content); + } + } + + public static class Builder { + + private UpdateDialog updateDialog; + + public Builder() { + this.updateDialog = new UpdateDialog(); + } + + public Builder setVersion(String version) { + updateDialog.version = version; + return this; + } + + public Builder setContent(String content) { + updateDialog.content = content; + return this; + } + + public Builder setContentHtml(boolean isContentHtml) { + updateDialog.isContentHtml = isContentHtml; + return this; + } + + public Builder setCancelable(boolean cancelable) { + updateDialog.cancelable = cancelable; + return this; + } + + public Builder setDebug(boolean debug) { + updateDialog.debug = debug; + return this; + } + + public Builder setDownloadUrl(String url) { + updateDialog.appUrl = url; + return this; + } + +// public Builder setOnConfirmListener(OnConfirmListener listener) { +// updateDialog.listener = listener; +// return this; +// } + + public UpdateDialog build() { + return updateDialog; + } + + } + +// public void setPercent(int progressPercent) { +// barPercentView.setPercentage(progressPercent); +// progressTv.setText(progressPercent + "%"); +// } + +// public interface OnConfirmListener { +// void onConfirm(View view); +// } + + public void showUpdateDialog(FragmentActivity activity) { + if (this.isAdded() && this.isVisible()) return; + FragmentTransaction fragmentTransaction = activity.getSupportFragmentManager().beginTransaction(); + fragmentTransaction.add(android.R.id.content, this); + fragmentTransaction.setCustomAnimations(R.anim.push_bottom_in, R.anim.push_bottom_out); + fragmentTransaction.show(this); + if (activity.isFinishing() || activity.isDestroyed()) { + fragmentTransaction.commitAllowingStateLoss(); + } else { + fragmentTransaction.commit(); + } + } + + public void dismissUpdateDialog() { + FragmentTransaction fragmentTransaction = mActivity.getSupportFragmentManager().beginTransaction(); + if (this.isAdded() && this.isVisible()) { + fragmentTransaction.setCustomAnimations(R.anim.push_bottom_in, R.anim.push_bottom_out); + fragmentTransaction.hide(this); + fragmentTransaction.commit(); + new Handler().postDelayed(this::destroyUpdateDialog, 500); + } + } + + private void destroyUpdateDialog() { + FragmentTransaction fragmentTransaction = mActivity.getSupportFragmentManager().beginTransaction(); + fragmentTransaction.hide(this); + fragmentTransaction.remove(this); + fragmentTransaction.commit(); + } + + private void downloadApk(String apkUrl) { + if (TextUtils.isEmpty(apkUrl)) throw new NullPointerException("url can not be empty"); + binding.rlProgress.setVisibility(View.VISIBLE); + binding.btUpdate.setVisibility(View.GONE); + binding.btBrowser.setVisibility(View.GONE); + if (apkUrl.endsWith(".apk")) { + downloadApkNormal(apkUrl); + } else if (apkUrl.contains("fycz.lanzou")) { + downloadWithLanzous(apkUrl); + } else { + downloadApkWithNoFileName(apkUrl); + } + } + + private void downloadApkNormal(String apkUrl) { + String decodeUrl; + try { + decodeUrl = URLDecoder.decode(apkUrl, "UTF-8"); + } catch (UnsupportedEncodingException e) { + decodeUrl = apkUrl; + } + if (debug) Log.e("downloadApk", "originUrl------->" + apkUrl); + if (debug) Log.e("downloadApk", "decodeUrl------->" + decodeUrl); +// path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/" + decodeUrl.substring(decodeUrl.lastIndexOf("/") + 1); + path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/风月读书" + version + ".apk"; + File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + if (!dir.exists() && dir.mkdir()) { + startDownloadingApk(decodeUrl); + } else { + startDownloadingApk(decodeUrl); + } + } + + private void startDownloadingApk(String decodeUrl) { + baseDownloadTask = FileDownloader.getImpl() + .create(decodeUrl) + .setPath(path, new File(path).isDirectory()) + .setCallbackProgressMinInterval(100) + .setListener(new FileDownloadLargeFileListener() { + @Override + protected void pending(BaseDownloadTask task, long soFarBytes, long totalBytes) { + if (debug) Log.e("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 (percent >= 3) { + binding.barPercentView.setPercentage(percent); + binding.tvProgress.setText((int) percent + "%"); + } + } + + @Override + protected void paused(BaseDownloadTask task, long soFarBytes, long totalBytes) { + if (debug) Log.e("downloadApk", "paused-------"); + } + + @Override + protected void completed(BaseDownloadTask task) { + if (debug) Log.e("downloadApk", "completed-------"); + binding.barPercentView.setPercentage(100); + binding.tvProgress.setText("100%"); + install(new File(path), mActivity); + } + + @Override + protected void error(BaseDownloadTask task, Throwable e) { + if (debug) Log.e("downloadApk", "error-------" + e.toString()); + UpdateDialog.this.error(); + } + + @Override + protected void warn(BaseDownloadTask task) { + if (debug) Log.e("downloadApk", "warn-------"); + } + }); + baseDownloadTask.setAutoRetryTimes(3); + baseDownloadTask.start(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == PERMISSION_WRITE_EXTERNAL_STORAGE) { + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // 权限被用户同意,可以去放肆了。 + if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + return; + } else { + downloadApk(appUrl); + } + } else { + // 权限被用户拒绝了,洗洗睡吧。 + } + } + } + + /** + * 安装 + */ + private void install(File filePath, Context context) { + if (filePath.exists()) { + Intent intent = new Intent(Intent.ACTION_VIEW); + //判断是否是Android N 以及更高的版本 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + Log.d("UpdateDialog", "install: " + filePath); + Uri contentUri = FileProvider.getUriForFile(context, mActivity.getPackageName() + ".fileprovider", filePath); + intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); + } else { + intent.setDataAndType(Uri.fromFile(filePath), "application/vnd.android.package-archive"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + context.startActivity(intent); + } else { + downloadApk(appUrl); + } + } + + private void downloadApkWithNoFileName(String appUrl) { + App.getApplication().newThread(() -> { + try { + URL url = new URL(appUrl); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestMethod("GET"); + urlConnection.setConnectTimeout(5000); + int responseCode = urlConnection.getResponseCode(); + if (responseCode == 200) { + URL url1 = urlConnection.getURL(); + downloadApkNormal(url1.toString()); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + private void downloadWithLanzous(String apkUrl) { + binding.tvProgress.setText("正在获取下载链接..."); + binding.barPercentView.setPercentage(0); + CommonApi.getUrl(apkUrl, new ResultCallback() { + @Override + public void onFinish(Object o, int code) { + String downloadUrl = (String) o; + if (downloadUrl == null) { + App.runOnUiThread(() -> error()); + return; + } + App.runOnUiThread(() -> downloadApkNormal(downloadUrl)); + } + + @Override + public void onError(Exception e) { + App.runOnUiThread(() -> error()); + } + }); + } + + private void error() { + ToastUtils.showError("下载链接获取失败,请前往浏览器下载!"); + binding.btBrowser.performClick(); + binding.rlProgress.setVisibility(View.GONE); + binding.btUpdate.setVisibility(View.VISIBLE); + binding.btBrowser.setVisibility(View.VISIBLE); + } +} 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 3ce8175..8c852d6 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 @@ -42,7 +42,9 @@ import xyz.fycz.myreader.model.storage.BackupRestoreUi; import xyz.fycz.myreader.model.storage.Restore; import xyz.fycz.myreader.model.storage.WebDavHelp; import xyz.fycz.myreader.ui.activity.AboutActivity; +import xyz.fycz.myreader.ui.activity.AdSettingActivity; import xyz.fycz.myreader.ui.activity.BookSourceActivity; +import xyz.fycz.myreader.ui.activity.DonateActivity; import xyz.fycz.myreader.ui.activity.FeedbackActivity; import xyz.fycz.myreader.ui.activity.LoginActivity; import xyz.fycz.myreader.ui.activity.MoreSettingActivity; @@ -282,6 +284,8 @@ public class MineFragment extends BaseFragment { }); + binding.mineRlAdSetting.setOnClickListener(v -> startActivity(new Intent(getActivity(), AdSettingActivity.class))); + binding.mineRlAbout.setOnClickListener(v -> { Intent aboutIntent = new Intent(getActivity(), AboutActivity.class); startActivity(aboutIntent); @@ -291,6 +295,8 @@ public class MineFragment extends BaseFragment { Intent intent = new Intent(getContext(), FeedbackActivity.class); getActivity().startActivity(intent); }); + + binding.mineRlDonate.setOnClickListener(v -> startActivity(new Intent(getActivity(), DonateActivity.class))); } @Override diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java index 6b58f01..b1133c7 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java @@ -175,7 +175,7 @@ public class BookcasePresenter implements BasePresenter { //启动 @Override public void start() { - checkVersionByServer(mMainActivity, false, mBookcaseFragment); + checkVersionByServer(mMainActivity, false); if (mSetting.getBookcaseStyle() == null) { mSetting.setBookcaseStyle(BookcaseStyle.listMode); } diff --git a/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java b/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java index 71fba35..ce4850f 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java +++ b/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java @@ -94,8 +94,8 @@ public class HttpUtil { if (mClient == null) { OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.connectTimeout(5, TimeUnit.SECONDS) - .readTimeout(15, TimeUnit.SECONDS) - .writeTimeout(15, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) .sslSocketFactory(createSSLSocketFactory(), createTrustAllManager()) .hostnameVerifier((hostname, session) -> true) .protocols(Collections.singletonList(Protocol.HTTP_1_1)) diff --git a/app/src/main/java/xyz/fycz/myreader/util/SharedPreUtils.java b/app/src/main/java/xyz/fycz/myreader/util/SharedPreUtils.java index 8140d46..027f427 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/SharedPreUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/SharedPreUtils.java @@ -84,7 +84,7 @@ public class SharedPreUtils { } public boolean getBoolean(String key,boolean def){ - return sharedReadable.getBoolean(key, false); + return sharedReadable.getBoolean(key, def); } public float getFloat(String key){ diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java new file mode 100644 index 0000000..235fb6b --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java @@ -0,0 +1,133 @@ +package xyz.fycz.myreader.util.utils; + +import android.util.Log; + +import com.weaction.ddsdk.base.DdSdkHelper; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import io.reactivex.Single; +import io.reactivex.SingleEmitter; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.annotations.NonNull; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import xyz.fycz.myreader.application.App; +import xyz.fycz.myreader.base.observer.MySingleObserver; +import xyz.fycz.myreader.common.URLCONST; +import xyz.fycz.myreader.model.backup.UserService; +import xyz.fycz.myreader.util.DateHelper; +import xyz.fycz.myreader.util.SharedPreUtils; + +/** + * @author fengyue + * @date 2021/4/22 19:00 + */ +public class AdUtils { + public static final String TAG = AdUtils.class.getSimpleName(); + private static boolean hasInitAd = false; + + public static Single checkHasAd() { + return Single.create((SingleOnSubscribe) emitter -> { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + String body = "type=hasAd" + UserService.makeSignalParam(); + RequestBody requestBody = RequestBody.create(mediaType, body); + String jsonStr = OkHttpUtils.getHtml(URLCONST.AD_URL, requestBody, "UTF-8"); + boolean hasAd = false; + try { + JSONObject jsonObject = new JSONObject(jsonStr); + int code = jsonObject.getInt("code"); + if (code > 200) { + Log.e(TAG, "checkHasAd-->errorCode:" + code); + if (code == 213) { + hasAd = true; + } + } else { + hasAd = jsonObject.getBoolean("result"); + } + Log.i(TAG, "hasAd:" + hasAd); + } catch (JSONException e) { + e.printStackTrace(); + } + emitter.onSuccess(hasAd); + }).compose(RxUtils::toSimpleSingle); + } + + public static void adRecord(String type, String name) { + Single.create((SingleOnSubscribe) emitter -> { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + String body = "adType=" + type + "&type=" + name + UserService.makeSignalParam(); + RequestBody requestBody = RequestBody.create(mediaType, body); + OkHttpUtils.getHtml(URLCONST.AD_URL, requestBody, "UTF-8"); + emitter.onSuccess(true); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() { + @Override + public void onSuccess(@NonNull Boolean aBoolean) { + Log.i(TAG, name + "上报成功"); + } + + @Override + public void onError(Throwable e) { + Log.e(TAG, name + "上报失败\n" + e.getLocalizedMessage()); + } + }); + } + + public static Single adTimes() { + return Single.create((SingleOnSubscribe) emitter -> { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + String body = "type=adTimes" + UserService.makeSignalParam(); + RequestBody requestBody = RequestBody.create(mediaType, body); + String jsonStr = OkHttpUtils.getHtml(URLCONST.AD_URL, requestBody, "UTF-8"); + JSONObject jsonObject = new JSONObject(jsonStr); + int[] adTimes = new int[]{-1, 3, 5}; + try { + int code = jsonObject.getInt("code"); + JSONArray adTimesArr = jsonObject.getJSONArray("result"); + Log.i(TAG, "adTimesArr:" + adTimesArr.toString()); + if (code > 200) { + Log.e(TAG, "adTimes-->errorCode:" + code); + if (code == 213) { + adTimes = new int[]{-1}; + } + } else { + adTimes = new int[adTimesArr.length()]; + for (int i = 0; i < adTimesArr.length(); i++) { + adTimes[i] = adTimesArr.getInt(i); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + emitter.onSuccess(adTimes); + }).compose(RxUtils::toSimpleSingle); + } + + public static boolean checkTodayShowAd() { + SharedPreUtils spu = SharedPreUtils.getInstance(); + String splashAdCount = spu.getString("splashAdCount"); + boolean bookDetailAd = spu.getBoolean("bookDetailAd", true); + int adTimes = spu.getInt("curAdTimes", 3); + String[] splashAdCounts = splashAdCount.split(":"); + String today = DateHelper.getYearMonthDay1(); + int todayAdCount; + if (today.equals(splashAdCounts[0])) { + todayAdCount = Integer.parseInt(splashAdCounts[1]); + } else { + todayAdCount = 0; + } + return adTimes < 0 || todayAdCount < adTimes || bookDetailAd; + } + + public static void initAd(){ + if (!hasInitAd){ + hasInitAd = true; + DdSdkHelper.init("1234", "216", "51716a16fbdf50905704b6575b1b3b60", + "142364", "35ce0efe5f3cc960b116db227498e238", + "8167", "85bd159309c3da1b", + App.getApplication(), true); + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/FileUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/FileUtils.java index 74d9288..3f59893 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/FileUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/FileUtils.java @@ -89,6 +89,22 @@ public class FileUtils { } } + //获取File文件夹 + public static String getFilePath(){ + return getFileDir().getAbsolutePath(); + } + + public static File getFileDir(){ + if (isSdCardExist()){ + return App.getmContext() + .getExternalFilesDir(null); + } + else{ + return App.getmContext() + .getFilesDir(); + } + } + public static long getDirSize(File file){ //判断文件是否存在 if (file.exists()) { diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java index c81bd04..d60d83b 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java @@ -53,6 +53,7 @@ public class NetworkUtils { public static String getAbsoluteURL(String baseURL, String relativePath) { if (StringHelper.isEmpty(relativePath)) return ""; if (StringHelper.isEmpty(baseURL)) return relativePath; + if (relativePath.contains("storage")) return relativePath; String header = null; if (StringUtils.startWithIgnoreCase(relativePath, "@header:")) { header = relativePath.substring(0, relativePath.indexOf("}") + 1); 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 847fd2b..7f98a8e 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 @@ -4,6 +4,7 @@ import android.util.Log; import okhttp3.*; import xyz.fycz.myreader.application.TrustAllCerts; import xyz.fycz.myreader.util.HttpUtil; +import xyz.fycz.myreader.util.StringHelper; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; @@ -27,13 +28,20 @@ public class OkHttpUtils { } public static String getHtml(String url, RequestBody requestBody, String encodeType) throws IOException { + return getHtml(url, requestBody, encodeType, null); + } + + public static String getHtml(String url, RequestBody requestBody, String encodeType, String cookie) throws IOException { Request.Builder builder = new Request.Builder() .addHeader("Accept", "*/*") .addHeader("Connection", "Keep-Alive") //.addHeader("Charsert", "utf-8") .addHeader("Cache-Control", "no-cache") - .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36"); + .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"); + if (cookie != null){ + builder.addHeader("Cookie", cookie); + } if (requestBody != null) { builder.post(requestBody); Log.d("HttpPost URl", url); diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java b/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java index 910a9cb..99d1fe4 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java @@ -3,6 +3,7 @@ package xyz.fycz.myreader.webapi; import java.util.List; import io.reactivex.Observable; +import okhttp3.FormBody; import okhttp3.MediaType; import okhttp3.RequestBody; import xyz.fycz.myreader.common.URLCONST; @@ -168,10 +169,26 @@ public class CommonApi extends BaseApi { String url = rc.getSearchLink(); String[] urlInfo = url.split(","); url = urlInfo[0]; + /*String[] bodies = makeSearchUrl(urlInfo[1], key).split("&"); + FormBody.Builder formBody = new FormBody.Builder(); + for (String body : bodies) { + String[] kv = body.split("="); + formBody.add(kv[0], kv[1]); + } + RequestBody requestBody = formBody.build();*/ String body = makeSearchUrl(urlInfo[1], key); MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); RequestBody requestBody = RequestBody.create(mediaType, body); - emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(url, requestBody, finalCharset))); + if (rc.getNameSpace().contains("soxs.cc") || + rc.getNameSpace().contains("xinshuhaige.org") || + rc.getNameSpace().contains("bxwxorg.com") || + rc.getNameSpace().contains("soshuw.com")) { + String cookie = "Hm_lvt_46329db612a10d9ae3a668a40c152e0e=1612793811,1612795781,1613200980,1613218588; " + + "__cfduid=d0ebd0275436b7b0c3ccf4c9eb7394abd1619231977 "; + emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(url, requestBody, finalCharset, cookie))); + } else { + emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(url, requestBody, finalCharset))); + } } else { emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(makeSearchUrl(rc.getSearchLink(), key), finalCharset))); } diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java b/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java index f4799c7..e3a0082 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java @@ -91,13 +91,15 @@ public class LanZousApi { } private static String getKey(String html) { - SharedPreUtils spu = SharedPreUtils.getInstance(); + //SharedPreUtils spu = SharedPreUtils.getInstance(); String lanzousKeyStart = "var pposturl = '"; - try { - lanzousKeyStart = spu.getString(App.getmContext().getString(R.string.lanzousKeyStart)); - }catch (Exception e){ - e.printStackTrace(); + String keyName = StringHelper.getSubString(html, "'sign':", ","); + if (keyName.endsWith("'")){ + lanzousKeyStart = "'sign':'"; + }else { + lanzousKeyStart = "var " + keyName + " = '"; } + //lanzousKeyStart = spu.getString(App.getmContext().getString(R.string.lanzousKeyStart)); return StringHelper.getSubString(html, lanzousKeyStart, "'"); } diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Ben100ReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Ben100ReadCrawler.java index df11d16..145d20a 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Ben100ReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Ben100ReadCrawler.java @@ -21,7 +21,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; - +@Deprecated public class Ben100ReadCrawler extends FindCrawler implements ReadCrawler, BookInfoCrawler { public static final String NAME_SPACE = "https://www.100ben.net"; public static final String NOVEL_SEARCH = "https://www.100ben.net/plus/search.php?keyword={key}"; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChuanQiReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChuanQiReadCrawler.java index ea5fa4c..8c5e044 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChuanQiReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChuanQiReadCrawler.java @@ -17,7 +17,7 @@ import xyz.fycz.myreader.greendao.entity.Chapter; import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; - +@Deprecated public class ChuanQiReadCrawler implements ReadCrawler { public static final String NAME_SPACE = "https://www.xs86.com"; public static final String NOVEL_SEARCH = "https://www.xs86.com/search.php?key={key}"; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/HongChenReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/HongChenReadCrawler.java index 3ad97f3..c998aba 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/HongChenReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/HongChenReadCrawler.java @@ -122,6 +122,7 @@ public class HongChenReadCrawler implements ReadCrawler { */ + @Deprecated public ConcurrentMultiValueMap getBooksFromSearchHtml(String html) { ConcurrentMultiValueMap books = new ConcurrentMultiValueMap<>(); Document doc = Jsoup.parse(html); diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/JiuTaoReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/JiuTaoReadCrawler.java index 8441e05..6f29e4d 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/JiuTaoReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/JiuTaoReadCrawler.java @@ -19,7 +19,7 @@ import java.util.ArrayList; public class JiuTaoReadCrawler implements ReadCrawler { public static final String NAME_SPACE = "https://www.9txs.com"; - public static final String NOVEL_SEARCH = "https://www.9txs.com/search.html,searchkey={key}"; + public static final String NOVEL_SEARCH = "https://so.9txs.org/www/,searchkey={key}"; public static final String CHARSET = "UTF-8"; public static final String SEARCH_CHARSET = "UTF-8"; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LaoYaoReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LaoYaoReadCrawler.java index 8c797e2..93c5cc9 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LaoYaoReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LaoYaoReadCrawler.java @@ -17,7 +17,7 @@ import xyz.fycz.myreader.greendao.entity.Chapter; import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; - +@Deprecated public class LaoYaoReadCrawler implements ReadCrawler { public static final String NAME_SPACE = "https://www.laoyao.org"; public static final String NOVEL_SEARCH = "https://www.laoyao.org/search.php?key={key}"; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiQuReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiQuReadCrawler.java index 6283c58..2471626 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiQuReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiQuReadCrawler.java @@ -17,7 +17,7 @@ import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; import java.util.ArrayList; - +@Deprecated public class MiQuReadCrawler implements ReadCrawler, BookInfoCrawler { public static final String NAME_SPACE = "https://www.meegoq.com"; public static final String NOVEL_SEARCH = "https://www.meegoq.com/search.htm?keyword={key}"; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QianDianReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QianDianReadCrawler.java new file mode 100644 index 0000000..71ade3d --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QianDianReadCrawler.java @@ -0,0 +1,126 @@ +package xyz.fycz.myreader.webapi.crawler.read; + +import android.text.Html; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.util.ArrayList; + +import xyz.fycz.myreader.entity.SearchBookBean; +import xyz.fycz.myreader.enums.LocalBookSource; +import xyz.fycz.myreader.greendao.entity.Book; +import xyz.fycz.myreader.greendao.entity.Chapter; +import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; +import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; + + +public class QianDianReadCrawler implements ReadCrawler { + public static final String NAME_SPACE = "https://www.qidian.com"; + public static final String NOVEL_SEARCH = "https://www.qidian.com/search?kw={key}"; + public static final String CHARSET = "UTF-8"; + public static final String SEARCH_CHARSET = "UTF-8"; + + @Override + public String getSearchLink() { + return NOVEL_SEARCH; + } + + @Override + public String getCharset() { + return CHARSET; + } + + @Override + public String getNameSpace() { + return NAME_SPACE; + } + + @Override + public Boolean isPost() { + return false; + } + + @Override + public String getSearchCharset() { + return SEARCH_CHARSET; + } + + /** + * 从html中获取章节正文 + * + * @param html + * @return + */ + public String getContentFormHtml(String html) { + Document doc = Jsoup.parse(html); + Element divContent = doc.getElementsByClass("read-content").first(); + String content = Html.fromHtml(divContent.html()).toString(); + char c = 160; + String spaec = "" + c; + content = content.replace(spaec, " "); + return content; + } + + /** + * 从html中获取章节列表 + * + * @param html + * @return + */ + public ArrayList getChaptersFromHtml(String html) { + ArrayList chapters = new ArrayList<>(); + Document doc = Jsoup.parse(html); + Element divList = doc.getElementsByClass("attentions").get(1); + Elements elementsByTag = divList.getElementsByTag("a"); + for (int i = 0; i < elementsByTag.size(); i++) { + Element a = elementsByTag.get(i); + String title = a.text(); + String url = a.attr("href"); + Chapter chapter = new Chapter(); + chapter.setNumber(i); + chapter.setTitle(title); + chapter.setUrl(url); + chapters.add(chapter); + } + return chapters; + } + + /** + * 从搜索html中得到书列表 + * + * + * 大主宰:灵玖 + * 作者:霞露分类:其他连载中 + * 简介:她,身负系统,莫名来到了大主宰的时空,成为了聚灵族的最后族人。在她还对周围的情况一片模糊的时候,她遇到了林静这个小恶魔。拜林静所赐,她还遇到了武祖,成为了武柤的小徒弟。参与了灵路,遇到了牧尘和洛璃,她默默的在心中问着自己那一直在划水的系统:“你的活来了,说吧,我该干什么?” + * 小说详情 + * + */ + public ConcurrentMultiValueMap getBooksFromSearchHtml(String html) { + ConcurrentMultiValueMap books = new ConcurrentMultiValueMap<>(); + Document doc = Jsoup.parse(html); + Element div = doc.getElementById("result-list"); + Elements elementsByTag = div.getElementsByTag("li"); + for (int i = 0; i < elementsByTag.size(); i++) { + Element element = elementsByTag.get(i); + Elements as = element.getElementsByTag("a"); + Elements ps = element.getElementsByTag("p"); + Book book = new Book(); + book.setImgUrl(element.getElementsByTag("img").attr("src")); + book.setName(as.get(1).text()); + Elements spans = ps.get(0).getElementsByTag("span"); + book.setAuthor(spans.get(0).text()); + book.setType(spans.get(1).text().replace("分类:", "")); + book.setChapterUrl(as.get(2).attr("href")); + book.setDesc(ps.get(1).text()); + book.setNewestChapterTitle(""); + book.setSource(LocalBookSource.bijian.toString()); + SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); + books.add(sbb, book); + } + return books; + } + +} diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ShuHaiGeReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ShuHaiGeReadCrawler.java index 00b2f61..12ebfa6 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ShuHaiGeReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ShuHaiGeReadCrawler.java @@ -22,8 +22,8 @@ import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; public class ShuHaiGeReadCrawler implements ReadCrawler, BookInfoCrawler { - public static final String NAME_SPACE = "https://www.xinshuhaige.net"; - public static final String NOVEL_SEARCH = "https://www.xinshuhaige.net/search.html,searchkey={key}&searchtype=all"; + public static final String NAME_SPACE = "https://www.xinshuhaige.org"; + public static final String NOVEL_SEARCH = "https://www.xinshuhaige.org/search.html,searchkey={key}&searchtype=all"; public static final String CHARSET = "utf-8"; public static final String SEARCH_CHARSET = "utf-8"; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/TianLaiReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/TianLaiReadCrawler.java index edc750d..4fc7702 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/TianLaiReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/TianLaiReadCrawler.java @@ -22,9 +22,8 @@ import java.util.ArrayList; */ public class TianLaiReadCrawler implements ReadCrawler { - // public static final String NAME_SPACE = "https://www.23txt.com"; - public static final String NAME_SPACE = "https://www.233txt.com"; - public static final String NOVEL_SEARCH = "https://www.233txt.com/search.php?q={key}"; + public static final String NAME_SPACE = "https://www.23txt.com"; + public static final String NOVEL_SEARCH = "https://www.23txt.com/search.php?q={key}"; public static final String CHARSET = "GBK"; public static final String SEARCH_CHARSET = "utf-8"; diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XiaGuReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XiaGuReadCrawler.java index 853b73a..ffafaec 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XiaGuReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XiaGuReadCrawler.java @@ -130,6 +130,7 @@ public class XiaGuReadCrawler implements ReadCrawler { */ + @Deprecated public ConcurrentMultiValueMap getBooksFromSearchHtml(String html) { ConcurrentMultiValueMap books = new ConcurrentMultiValueMap<>(); Document doc = Jsoup.parse(html); diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XingXingReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XingXingReadCrawler.java index 1e9a1ae..1b758d6 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XingXingReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XingXingReadCrawler.java @@ -122,6 +122,7 @@ public class XingXingReadCrawler implements ReadCrawler { */ + @Deprecated public ConcurrentMultiValueMap getBooksFromSearchHtml(String html) { ConcurrentMultiValueMap books = new ConcurrentMultiValueMap<>(); Document doc = Jsoup.parse(html); diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/YanQingLouReadCrawler.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/YanQingLouReadCrawler.java index 7f679f3..5af3b4f 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/YanQingLouReadCrawler.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/YanQingLouReadCrawler.java @@ -23,7 +23,7 @@ import xyz.fycz.myreader.util.utils.OkHttpUtils; import xyz.fycz.myreader.util.utils.StringUtils; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; - +@Deprecated public class YanQingLouReadCrawler implements ReadCrawler { public static final String NAME_SPACE = "http://www.yanqinglou.com"; public static final String NOVEL_SEARCH = "http://www.yanqinglou.com/Home/Search,action=search&q={key}"; diff --git a/app/src/main/java/xyz/fycz/myreader/widget/BarPercentView.java b/app/src/main/java/xyz/fycz/myreader/widget/BarPercentView.java new file mode 100644 index 0000000..dddcfb0 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/widget/BarPercentView.java @@ -0,0 +1,162 @@ +package xyz.fycz.myreader.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Shader; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.Keep; +import androidx.annotation.Nullable; + +import xyz.fycz.myreader.R; + + +public class BarPercentView extends View { + + public static final float MAX = 100f; + public static final int RADIUS = R.dimen._15dp; // 圆角矩形半径 + private RectF rectFBg; + private RectF rectFProgress; + private Paint mPaint; + private int mWidth; + private float progressPercent; + private int bgColor, progressColor; + private int mHeight; + private int radius; + private int startColor, endColor; + private LinearGradient gradient; + private boolean isGradient; + + public BarPercentView(Context context) { + super(context); + init(); + } + + public BarPercentView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + //获取自定义属性 + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BarPercentView); + bgColor = typedArray.getColor(R.styleable.BarPercentView_barBgColor, getResources().getColor(R.color.gray_cfcfcf)); + progressColor = typedArray.getColor(R.styleable.BarPercentView_barProgressColor, getResources().getColor(R.color.orange_ffc032)); + mHeight = typedArray.getDimensionPixelSize(R.styleable.BarPercentView_barHeight, context.getResources().getDimensionPixelSize(R.dimen._10dp)); + isGradient = typedArray.getBoolean(R.styleable.BarPercentView_barIsGradient, false); + startColor = typedArray.getColor(R.styleable.BarPercentView_barStartColor, getResources().getColor(R.color.black_3A3D4E)); + endColor = typedArray.getColor(R.styleable.BarPercentView_barEndColor, getResources().getColor(R.color.black_475B80)); + radius = typedArray.getDimensionPixelSize(R.styleable.BarPercentView_barRadius, RADIUS); + typedArray.recycle(); + init(); + + } + + public BarPercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + if (widthSpecMode == MeasureSpec.EXACTLY || widthSpecMode == MeasureSpec.AT_MOST) { + mWidth = widthSpecSize; + } else { + mWidth = 0; + } + if (heightSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.UNSPECIFIED) { + mHeight = heightSpecSize; + } else if (heightSpecMode == MeasureSpec.EXACTLY) { + mHeight = heightSpecSize; + } else { + mHeight = getContext().getResources().getDimensionPixelSize(R.dimen._10dp); + } + setMeasuredDimension(mWidth, mHeight); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + gradient = new LinearGradient(0, 0, getWidth(), mHeight, startColor, endColor, Shader.TileMode.CLAMP); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + //1、背景 + mPaint.setShader(null); + mPaint.setColor(bgColor); + rectFBg.right = mWidth; //宽度 + rectFBg.bottom = mHeight; //高度 + canvas.drawRoundRect(rectFBg, radius, radius, mPaint); + //2、进度条 + rectFProgress.right = mWidth * progressPercent; + rectFProgress.bottom = mHeight; + //3、是否绘制渐变色 + if (isGradient) { + mPaint.setShader(gradient);//设置线性渐变 + } else { + mPaint.setColor(progressColor); + } +// if (progressPercent > 0 && rectFProgress.right < radius){ +// //进度值小于半径时,设置大于半径的最小值,防止绘制不出圆弧矩形 +//// rectFProgress.right = radius; +// canvas.drawRoundRect(rectFProgress, radius/2f, radius/2f, mPaint);//进度} +// }else { + canvas.drawRoundRect(rectFProgress, radius, radius, mPaint);//进度} +// } + + + } + + @Keep + public void setPercentage(float percentage) { + if (percentage / MAX >= 1) { + this.progressPercent = 1; + } else { + this.progressPercent = percentage / MAX; + } + invalidate(); + } + + private void init() { + rectFBg = new RectF(0, 0, 0, mHeight); + rectFProgress = new RectF(0, 0, 0, mHeight); + mPaint = new Paint(); + //设置抗锯齿 + mPaint.setAntiAlias(true); + } + + public float getProgress() { + return progressPercent; + } +// public void setHeight(int mHeight) { +// this.mHeight = mHeight; +// invalidate(); +// } +// +// public void setBgColor(int bgColor) { +// this.bgColor = bgColor; +// } +// +// public void setProgressColor(int progressColor) { +// this.progressColor = progressColor; +// } +// +// public void setStartColor(int startColor) { +// this.startColor = startColor; +// } +// +// public void setEndColor(int endColor) { +// this.endColor = endColor; +// } +// +// public void setGradient(boolean gradient) { +// isGradient = gradient; +// } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/fycz/myreader/widget/CoverImageView.kt b/app/src/main/java/xyz/fycz/myreader/widget/CoverImageView.kt index 702e37e..8ad2c07 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/CoverImageView.kt +++ b/app/src/main/java/xyz/fycz/myreader/widget/CoverImageView.kt @@ -9,6 +9,7 @@ import android.util.AttributeSet import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.Target import xyz.fycz.myreader.R import xyz.fycz.myreader.util.utils.ImageLoader @@ -126,9 +127,15 @@ class CoverImageView : androidx.appcompat.widget.AppCompatImageView { fun load(path: String?, name: String?, author: String?) { setText(name, author) - ImageLoader.load(context, path)//Glide自动识别http://和file:// + val options = RequestOptions () .placeholder(R.mipmap.default_cover) .error(R.mipmap.default_cover) + .centerCrop() + ImageLoader.load(context, path)//Glide自动识别http://和file:// + /*.placeholder(R.mipmap.default_cover) + .error(R.mipmap.default_cover) + .centerCrop()*/ + .apply(options) .listener(object : RequestListener { override fun onLoadFailed( e: GlideException?, @@ -152,7 +159,6 @@ class CoverImageView : androidx.appcompat.widget.AppCompatImageView { } }) - .centerCrop() .into(this) } } diff --git a/app/src/main/res/anim/push_bottom_in.xml b/app/src/main/res/anim/push_bottom_in.xml new file mode 100644 index 0000000..fb148bd --- /dev/null +++ b/app/src/main/res/anim/push_bottom_in.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/anim/push_bottom_out.xml b/app/src/main/res/anim/push_bottom_out.xml new file mode 100644 index 0000000..2ffd267 --- /dev/null +++ b/app/src/main/res/anim/push_bottom_out.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/drawable/bg_loading_dialog.xml b/app/src/main/res/drawable/bg_loading_dialog.xml new file mode 100644 index 0000000..379da13 --- /dev/null +++ b/app/src/main/res/drawable/bg_loading_dialog.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic___.xml b/app/src/main/res/drawable/ic___.xml new file mode 100644 index 0000000..5800208 --- /dev/null +++ b/app/src/main/res/drawable/ic___.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_ad.xml b/app/src/main/res/drawable/ic_ad.xml new file mode 100644 index 0000000..1280a8e --- /dev/null +++ b/app/src/main/res/drawable/ic_ad.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 0000000..94df902 --- /dev/null +++ b/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_feedback.xml b/app/src/main/res/drawable/ic_feedback.xml index 0a55ce5..9bdf667 100644 --- a/app/src/main/res/drawable/ic_feedback.xml +++ b/app/src/main/res/drawable/ic_feedback.xml @@ -4,9 +4,9 @@ android:viewportWidth="1024" android:viewportHeight="1024"> diff --git a/app/src/main/res/drawable/ic_support.xml b/app/src/main/res/drawable/ic_support.xml new file mode 100644 index 0000000..21199ad --- /dev/null +++ b/app/src/main/res/drawable/ic_support.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/shape_confirm.xml b/app/src/main/res/drawable/shape_confirm.xml new file mode 100644 index 0000000..0233b16 --- /dev/null +++ b/app/src/main/res/drawable/shape_confirm.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_dialog_buttom.xml b/app/src/main/res/drawable/shape_dialog_buttom.xml new file mode 100644 index 0000000..05616d2 --- /dev/null +++ b/app/src/main/res/drawable/shape_dialog_buttom.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_ad_setting.xml b/app/src/main/res/layout/activity_ad_setting.xml new file mode 100644 index 0000000..44fe663 --- /dev/null +++ b/app/src/main/res/layout/activity_ad_setting.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_book_info_edit.xml b/app/src/main/res/layout/activity_book_info_edit.xml new file mode 100644 index 0000000..d764d5d --- /dev/null +++ b/app/src/main/res/layout/activity_book_info_edit.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_donate.xml b/app/src/main/res/layout/activity_donate.xml new file mode 100644 index 0000000..535b613 --- /dev/null +++ b/app/src/main/res/layout/activity_donate.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_more_setting.xml b/app/src/main/res/layout/activity_more_setting.xml index 05239ac..992dc7b 100644 --- a/app/src/main/res/layout/activity_more_setting.xml +++ b/app/src/main/res/layout/activity_more_setting.xml @@ -111,12 +111,12 @@ diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml index 5bbb23e..67ca3a9 100644 --- a/app/src/main/res/layout/activity_splash.xml +++ b/app/src/main/res/layout/activity_splash.xml @@ -1,9 +1,50 @@ - \ No newline at end of file + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical"> + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml new file mode 100644 index 0000000..d71c57f --- /dev/null +++ b/app/src/main/res/layout/dialog_loading.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_mine.xml b/app/src/main/res/layout/fragment_mine.xml index b7790b4..f88df7a 100644 --- a/app/src/main/res/layout/fragment_mine.xml +++ b/app/src/main/res/layout/fragment_mine.xml @@ -295,6 +295,36 @@ android:textColor="@color/textSecondary" android:textSize="@dimen/text_normal_size" /> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_update.xml b/app/src/main/res/layout/fragment_update.xml new file mode 100644 index 0000000..8d0615f --- /dev/null +++ b/app/src/main/res/layout/fragment_update.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 08f155a..a627d75 100644 --- a/app/src/main/res/layout/layout_book_detail_content.xml +++ b/app/src/main/res/layout/layout_book_detail_content.xml @@ -28,7 +28,12 @@ + + diff --git a/app/src/main/res/menu/menu_book_detail.xml b/app/src/main/res/menu/menu_book_detail.xml index 748fb1d..8fc595a 100644 --- a/app/src/main/res/menu/menu_book_detail.xml +++ b/app/src/main/res/menu/menu_book_detail.xml @@ -1,42 +1,47 @@ - + + android:id="@+id/action_change_source" + android:icon="@drawable/ic_change" + android:title="@string/menu_change_source" + app:showAsAction="ifRoom" /> - + app:showAsAction="ifRoom" /> + + android:id="@+id/action_reload" + android:icon="@drawable/ic_refresh" + android:title="@string/menu_reload" + app:showAsAction="never" /> + android:id="@+id/action_open_link" + android:icon="@drawable/ic_link" + android:title="@string/menu_open_link" + app:showAsAction="never" /> + android:id="@+id/action_is_update" + android:checkable="true" + android:checked="true" + android:icon="@drawable/ic_enable" + android:title="@string/menu_is_update" + app:showAsAction="never" /> + android:id="@+id/action_group_setting" + android:icon="@drawable/ic_group" + android:title="@string/menu_group_setting" + app:showAsAction="never" /> diff --git a/app/src/main/res/menu/menu_book_detail_local.xml b/app/src/main/res/menu/menu_book_detail_local.xml index 178fa1e..870c202 100644 --- a/app/src/main/res/menu/menu_book_detail_local.xml +++ b/app/src/main/res/menu/menu_book_detail_local.xml @@ -1,17 +1,22 @@ - + - + app:showAsAction="ifRoom" /> + + android:id="@+id/action_group_setting" + android:icon="@drawable/ic_group" + android:title="@string/menu_group_setting" + app:showAsAction="never" /> diff --git a/app/src/main/res/menu/menu_book_info_edit.xml b/app/src/main/res/menu/menu_book_info_edit.xml new file mode 100644 index 0000000..5bb9134 --- /dev/null +++ b/app/src/main/res/menu/menu_book_info_edit.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xhdpi/ic_dialog_loading.png b/app/src/main/res/mipmap-xhdpi/ic_dialog_loading.png new file mode 100644 index 0000000..79a75e4 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_dialog_loading.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/dialog_close_update.png b/app/src/main/res/mipmap-xxxhdpi/dialog_close_update.png new file mode 100644 index 0000000..3aab1b3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/dialog_close_update.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/down_buttom.png b/app/src/main/res/mipmap-xxxhdpi/down_buttom.png new file mode 100644 index 0000000..fc370e5 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/down_buttom.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/down_center.png b/app/src/main/res/mipmap-xxxhdpi/down_center.png new file mode 100644 index 0000000..930bb60 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/down_center.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/down_up.png b/app/src/main/res/mipmap-xxxhdpi/down_up.png new file mode 100644 index 0000000..77db03b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/down_up.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 3885c3d..b22bd86 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -227,5 +227,11 @@ #FFFFE284 #FFE75B76 - + + #cfcfcf + #ffc032 + #3a3d4e + #475b80 + #fffb8435 + #fffe795f diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f8e9a52..27fbaa5 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -46,5 +46,6 @@ 16sp - + 10dp + 15dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8a03aa..ffe9627 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -452,8 +452,34 @@ 解析器 所有书源 内置书源 + 广告设置 + 开屏广告 + 每日广告显示次数 + 当前次数:%s,今日已显示次数:%s + 广告总开关 + 详情页广告 + 详情页广告开关 + 支持作者 + 捐赠作者 + 微信赞赏码 + 点击打开 + 支付宝收款码 + QQ收款码 + 广告支持作者 + 点击下列广告即可支持作者,建议在WIFI环境下点击 + 信息流广告(下载的文件可在广告设置中清除) + 激励视频 + 广告下载/缓存 + 清除广告文件 + 封面地址 + 书籍简介 + 作者 + 书名 + 选择本地图片 + 编辑书籍 + 跟随系统 常亮 1分钟 3分钟 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 1975f8a..26950b7 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -198,9 +198,32 @@ - // + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/test/java/Test.java b/app/src/test/java/Test.java index 445b05a..8265548 100644 --- a/app/src/test/java/Test.java +++ b/app/src/test/java/Test.java @@ -2,8 +2,12 @@ import org.seimicrawler.xpath.JXDocument; import java.io.File; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import xyz.fycz.myreader.ai.MatrixUtil; +import xyz.fycz.myreader.model.source.BaseAnalyzer; import xyz.fycz.myreader.model.source.MatcherAnalyzer; import xyz.fycz.myreader.util.utils.FileUtils; import xyz.fycz.myreader.util.utils.NetworkUtils; @@ -82,8 +86,41 @@ public class Test { " }";*/ String str = " if (StringHelper.isEmpty(book.get" + strings1[i] + "()) && !StringHelper.isEmpty(bean.get" + strings1[i] + "()))\n" + - " book.set" + strings1[i] + "(bean.get" + strings1[i] + "());"; + " book.set" + strings1[i] + "(bean.get" + strings1[i] + "());"; System.out.println(str); } } + + @org.junit.Test + public void testReverse() { + List list = new ArrayList<>(); + list.add("3"); + list.add("2"); + list.add("1"); + list.add("6"); + list.add("5"); + list.add("4"); + list.add("9"); + list.add("8"); + list.add("7"); + list.add("11"); + list.add("10"); + BaseAnalyzer analyzer = new BaseAnalyzer() { + @Override + public List getStringList(String rule, Object obj, boolean first) { + return null; + } + }; + //List newList = analyzer.evalListFunction("%3;", list); + //for (String s : newList) { + // System.out.println(s); + //} + } + + @org.junit.Test + public void testMatrix() { + double[][] m = MatrixUtil.ones(4,5); + + System.out.println(Arrays.deepToString(m)); + } } diff --git a/app/src/test/resources/html.html b/app/src/test/resources/html.html deleted file mode 100644 index b1fcba2..0000000 --- a/app/src/test/resources/html.html +++ /dev/null @@ -1,1617 +0,0 @@ - - - - - - - - ½IVռ½-½IVռȫĶ- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ҳ - С˵ - С˵ - С˵ - ԽС˵ - С˵ - ƻС˵ - С˵ - 걾С˵ - ҵ - - - - - ͶƼƱٱֱײ - > С˵ > ½IVռ½б - - - - ½IVռ - ߣƼ - ״ ̬ - ¸£һǧپʮ - £2021-02-14 17:29 - - - һˡ - ƿڼ֮ؿƿʱһŽ˫ɫƵĵ֮̽ȻæозˣɷȴһӤһģһӤһĺӡ - - λҪǾá½IVռޡĻ벻ҪQQȺƼŶ - - - - - - ƼĶʥͼ ħ ʦ DZ ƽ ֻϦ Ʒʦ ֮ µھ ˫ - - - - - - - һ ʲô - ڶ ⶳ - ѵǣ - ĺ - ֻǸͨĺӣ - ۼ - - ڰ - ھ - ʮ ֻҪ - ʮһ - ʮ ؽָ - ʮ ͬʱֵ껷 - ʮ Ļ - ʮ Ҷͫ - ʮ - ʮ ѧϰ - ʮ Ҷ֮ͫ - ʮ ĩ - ڶʮ ʧЧĻ꼼 - ڶʮһ Ҷ - ڶʮ ˮɱ - ڶʮ ʳ - ڶʮ - ڶʮ ɴ - ڶʮ - ڶʮ ֳ - ڶʮ ֺ - ڶʮ Ⱥ - ʮ ʶĸо - ʮһ ְ - ʮ - ʮ ҿʦ - ʮ Ϸ - ʮ ʧ - ʮ Ϸ - ʮ ͥʦ - ʮ ʦ - ʮ Σ - ʮ - ʮһ ָ - ʮ - ʮ ս - ʮ սҶͫ - ʮ - ʮ ɳ - ʮ - ʮ ֹӵġ - ʮ Ĵ - ʮ Ȧ - ʮһ ҹ - ʮ 裬е㺦 - ʮ - ʮ - ʮ Ӧ̻ - ʮ ҪDzأ - ʮ ȵ - ʮ ʦ - ʮ - ʮ ְֻ - ʮһ У - ʮ ʦ - ʮ Կʼ - ʮ ǧ - ʮ ģ - ʮ ս - ʮ - ʮ ħԳ - ʮ ֮Σ - ʮ Ȼ - ʮһ ͻȻ뿪ֹ - ʮ - ʮ ¸衶ػʱػ㡷 - ʮ - ʮ ݳƱ - ʮ һ - ʮ ݳ - ʮ 壬ϲ - ʮ - ڰʮ ѧ - ڰʮһ - ڰʮ - ڰʮ ٻ֮ - ڰʮ ְҵѡ - ڰʮ һǮ - ڰʮ - ڰʮ ѵ - ڰʮ Ķ - ڰʮ ݵĸٻ - ھʮ ٻľɫŮ - ھʮһ - ھʮ ȵIJ² - ھʮ ȵ - ھʮ ٻ - ھʮ - ھʮ ħڿ - ھʮ ǰ¿ - ھʮ ս - ھʮ ߲ - һ ںϼĿ - һһ - һ ӺŵС - һ Թ - һ - һ Ժ - һ ƽʨӺ - һ - һ ǧϤ - һ ͽ - һһʮ Ǽս - һһʮһ ˡ - һһʮ ĵ - һһʮ - һһʮ ٴβ - һһʮ ͨн - һһʮ ̨ - һһʮ - һһʮ ս - һһʮ ֲħ - һٶʮ ʤ - һٶʮһ - һٶʮ ѹһ - һٶʮ ںϼ - һٶʮ - һٶʮ ĵ - һٶʮ ֹҲҪȥ춷 - һٶʮ ͬ - һٶʮ Ǽʺ - һٶʮ ţ» - һʮ ̫ճս - һʮһ Ǽʲɱ - һʮ ͽս - һʮ ĵ߸ - һʮ - һʮ ˫ - һʮ 㰡 - һʮ ɱ - һʮ - һʮ ǧҪʷѧԺ - һʮ ȵĽ - һʮһ ͻ - һʮ - һʮ Ԫأ - һʮ ˮ֮ - һʮ - һʮ ȵ - һʮ ˷ - һʮ ʦִ - һʮ ̬仯 - һʮ - һʮһ ڵĺ - һʮ ǰԱ - һʮ Ļ - һʮ ѡʼ - һʮ ػ - һʮ ѹĺѡ - һʮ ¼й - һʮ - һʮ ѡڶ - һʮ ᣡ - һʮһ ɱɱ - һʮ ǰ - һʮ ǿ飡 - һʮ Ǯڵѡ - һʮ ǧѰʾ - һʮ ˺һڤ - һʮ ھ - һʮ ҪͻƵ麣 - һʮ 췲Ĺ - һʮ · - һʮһ 齱֮ת - һʮ Ʒ - һʮ ںϻ - һʮ ѡοʼ - һʮ - һʮ Լһ - һʮ ǧ֥ - һʮ - һʮ - һٰʮ ˿ħԳĵ - һٰʮһ ۱ͷ - һٰʮ ô죿 - һٰʮ ѧԺ - һٰʮ - һٰʮ - һٰʮ - һٰʮ ľ - һٰʮ ƻ - һٰʮ - һپʮ Ļ - һپʮһ Ŧը - һپʮ Եʵ - һپʮ ɫĴ - һپʮ - һپʮ ħ - һپʮ İ - һپʮ Կ - һپʮ лĻ - һپʮ - ڶ һ - ڶһ ʮСʱ - ڶ Ԯ - ڶ - ڶ ƶ - ڶ ʷѧԺ - ڶ ʦʵ - ڶ ѹµijɳ - ڶ ̨ - ڶ - ڶһʮ ɵ - ڶһʮһ ָ - ڶһʮ ǿ - ڶһʮ Ŷ - ڶһʮ ¶Ҷ - ڶһʮ е㲻 - ڶһʮ ꣿ - ڶһʮ ս - ڶһʮ ɱ - ڶһʮ - ڶٶʮ ɫС - ڶٶʮһ - ڶٶʮ ĸǣ - ڶٶʮ ĺ - ڶٶʮ - ڶٶʮ һŬ - ڶٶʮ ʷ˳ - ڶٶʮ · - ڶٶʮ - ڶٶʮ ߲֡Ѫ - ڶʮ Ƭ - ڶʮһ õı - ڶʮ ʮ - ڶʮ ʲô - ڶʮ ֮ - ڶʮ ѪҺ - ڶʮ - ڶʮ - ڶʮ ˫ - ڶʮ ̭һ - ڶʮ ǿ - ڶʮһ ټʧ - ڶʮ ʲôҲû - ڶʮ ǿ - ڶʮ 廷Ŀ - ڶʮ - ڶʮ ע - ڶʮ ʷ˵Ļ - ڶʮ - ڶʮ ںϼ - ڶʮ ѡϵ - ڶʮһ Ǽ - ڶʮ Ŀ - ڶʮ εŻ - ڶʮ ǿɱ - ڶʮ ǡû - ڶʮ ԡѪ - ڶʮ - ڶʮ ϲС - ڶʮ ۺϿԿʲô - ڶʮ ̽ɫ - ڶʮһ ٿ - ڶʮ - ڶʮ - ڶʮ ս - ڶʮ ˿ʼ - ڶʮ - ڶʮ - ڶʮ ǿԭԻ - ڶʮ - ڶʮ ս - ڶʮһ - ڶʮ ٵĵڶ - ڶʮ ֧Ԯ - ڶʮ 룬ԭԻ - ڶʮ ڱ - ڶʮ - ڶʮ ľ - ڶʮ - ڶʮ ħ - ڶٰʮ ֲĵ - ڶٰʮһ ˫ͷԳ - ڶٰʮ ӱ - ڶٰʮ ǿĶ - ڶٰʮ ֮ - ڶٰʮ Ǯڱ - ڶٰʮ °ںϼ - ڶٰʮ ԭԻԵ - ڶٰʮ - ڶٰʮ - ڶپʮ һ - ڶپʮһ ع - ڶپʮ Ϊ㽾 - ڶپʮ ¼ȡ - ڶپʮ ӣ - ڶپʮ ס - ڶپʮ - ڶپʮ ϸ˽ - ڶپʮ ʦĺô - ڶپʮ ʦ - лԵ - һ Σ - Դ - ʷ - ճ - - ϵģ - - һ - е - һʮ Ͷ - һʮһ - һʮ - һʮ - һʮ ѧԱ - һʮ һͨ - һʮ ϡŹս - һʮ һһ߲ - һʮ ǽ - һʮ Դ꼼 - ٶʮ ʲ - ٶʮһ - ٶʮ ע - ٶʮ - ٶʮ ʹ - ٶʮ ͭǽ - ٶʮ ʤǰ - ٶʮ ǿ - ٶʮ - ٶʮ Ի - ʮ ° - ʮһ Ƶģ - ʮ - ʮ - ʮ Ǯ - ʮ ʮ˼ - ʮ - ʮ - ʮ ʷר - ʮ ɫˮ - ʮ Ķǧ - ʮһ - ʮ ִ - ʮ ңҪ䣡ǿ - ʮ - ʮ ŶǮеĻ - ʮ ǧͻ - ʮ Ų - ʮ ʥԨ - ʮ - ʮ - ʮһ ֮ - ʮ ٵĸ - ʮ Dz - ʮ - ʮ - ʮ ʧЧ - ʮ ħ - ʮ ϵıԴ - ʮ ں - ʮ Ǯڵĵ꼼 - ʮһ ǧѯ - ʮ - ʮ Զ죿 - ʮ ֹȵ״ - ʮ Ѫͫʽ - ʮ ǧサ - ʮ ģ - ʮ ζ - ʮ Ŭȵ㣡 - ʮ ӵķ - ʮһ и͵ɵģ - ʮ ̨ϵֹ - ʮ ˣ - ʮ ǣ - ʮ - ʮ - ʮ ʵ - ʮ Ҳó - ʮ - ٰʮ - ٰʮһ - ٰʮ ûл£粽 - ٰʮ ûǮ - ٰʮ ս꼶 - ٰʮ 꼶 - ٰʮ ѹе - ٰʮ - ٰʮ ƽ - ٰʮ - پʮ 㲻֪ - پʮһ ʼ - پʮ - پʮ ֹӵķ - پʮ ϵ˾ - پʮ - پʮ - پʮ ʥԨǬ - پʮ Ӯˣ - پʮ - İ 쳯ʳ - İһ ޣ - İ ¶ - İ - İ ʵ - İ ǹ - İ ԽԽ - İ һǧ - İ - İ - İһʮ ǧͽļֵ - İһʮһ 弶Ӣ - İһʮ ǰ - İһʮ ˵ - İһʮ 굼 - İһʮ ָ - İһʮ ִ - İһʮ ĺԴ - İһʮ ŴӼ - İһʮ - İٶʮ ů - İٶʮһ - İٶʮ - İٶʮ ػ - İٶʮ ٳ - İٶʮ - İٶʮ - İٶʮ ͻ - İٶʮ - İٶʮ 籩 - İʮ - İʮһ ͬ - İʮ ˣ - İʮ ˫ - İʮ - İʮ Զİ - İʮ ع - İʮ ˭ˣ - İʮ ְ֡ʦ - İʮ ʷѧԺ - İʮ - İʮһ ȵĽ - İʮ а취 - İʮ ȥ - İʮ Я - İʮ ٣ǹ - İʮ ħս - İʮ - İʮ ʧ - İʮ ɱӺ - İʮ Ӻ - İʮһ սһ֧ӵһ - İʮ ʷ֮ - İʮ - İʮ - İʮ - İʮ ƴ - İʮ ҪôͬȥҪôȥ - İʮ Ҹ - İʮ ǧһƷ - İʮ һҪӮ - İʮһ ս - İʮ ս - İʮ Ҫһ꼶 - İʮ - İʮ - İʮ 췢ʵ - İʮ ս - İʮ ǿ˾ - İʮ ʥԨꪣ٣ - İʮ Ժһһ - İʮһ Ӧ - İʮ һ - İʮ ȫǺö - İʮ ʮ֥ - İʮ - İʮ һҲҪӮ - İʮ ҽ - İʮ - İʮ ʵƯѧ - İٰʮ 鵶 - İٰʮһ - İٰʮ Ѫ - İٰʮ ° - İٰʮ ѡ뿪 - İٰʮ ƿ - İٰʮ ս - İٰʮ һ꼶 - İٰʮ ս - İٰʮ - İپʮ ֶ - İپʮһ о - İپʮ ȻӮ - İپʮ һ֮Լ - İپʮ սţ - İپʮ ʦͽ - İپʮ ѧܣѽ - İپʮ ҽ֣ - İپʮ ֶɼε - İپʮ - ƽɫ - һ ٷ֮ٵƿ - ʤ - ǮڵIJƱ - ʥԨ - ײٿ - Ľϣ - Ľ£ - ִᆱ - Ů - һʮ - һʮһ - һʮ ӹ - һʮ - һʮ - һʮ - һʮ ԵĿʼ - һʮ ѡ - һʮ 꾣 - һʮ - ٶʮ Ͽ - ٶʮһ ľϲ - ٶʮ ȫѡ - ٶʮ - ٶʮ - ٶʮ - ٶʮ ֮ - ٶʮ ʲô - ٶʮ ʺٵDZ̼ - ٶʮ Ľ - ʮ Ϸ - ʮһ - ʮ ְ֣Ұ - ʮ ڷ - ʮ ļ - ʮ ӹ - ʮ ȼգ - ʮ һ - ʮ Ƭ - ʮ ջ - ʮ ɽʽ - ʮһ ۺ - ʮ ҽа - ʮ - ʮ İݷ - ʮ δ - ʮ ں - ʮ ں - ʮ ħں - ʮ ֮ - ʮ սʵ - ʮһ Լս - ʮ ܹܴ̼ - ʮ սս - ʮ սս - ʮ 㽻 - ʮ ļƻ - ʮ - ʮ ǧ - ʮ ѧ - ʮ ǣ̼ħ - ʮһ ϵ - ʮ - ʮ ĥ - ʮ ٻ - ʮ ǿ - ʮ ʦ٣ - ʮ ͬĩ - ʮ ս - ʮ - ʮ ʻս - ʮһ Ŀĵ - ʮ - ʮ ִ - ʮ ӽԨľ - ʮ - ʮ - ʮ ڽdz - ʮ 뵱Ұְ - ʮ ǰ - ٰʮ ʱ - ٰʮһ ֲװ - ٰʮ ö - ٰʮ 굼 - ٰʮ µ - ٰʮ õĴ - ٰʮ - ٰʮ - ٰʮ - ٰʮ ²ų - پʮ ˲ķ - پʮһ - پʮ ʵ - پʮ ˯С - پʮ װս - پʮ ɢ· - پʮ ߵĵ˲ - پʮ ԺҲס! - پʮ »ʼһ굼ʦѧԺ - پʮ Լսʮ - Թȥ - һ ȵ - ݼ - ݽ - ̳ - ͻͻ - ˫ - Ԩ - ļͥ - д迪ʼ - һʮ ݶ - һʮһ ϼħͫƾ - һʮ ѩ - һʮ Ӧ - һʮ Ϲֵ - һʮ - һʮ - һʮ д͵ - һʮ ع - һʮ ˾ - ٶʮ - ٶʮһ ˼· - ٶʮ ѧɼ̳ - ٶʮ ϵ - ٶʮ λ - ٶʮ ʮ - ٶʮ ʮż - ٶʮ ĵļ - ٶʮ - ٶʮ Ĵ - ʮ ֹҪʷ˿ݳ - ʮһ һŹ - ʮ ʦݳ - ʮ Ԩħ - ʮ Ļͻ - ʮ ֥ - ʮ ȷ - ʮ ǿ - ʮ - ʮ ѵ - ʮ ѧĽ - ʮһ ȫ - ʮ - ʮ - ʮ 㳡 - ʮ 㳡ֹ - ʮ ֹӵѣ - ʮ - ʮ ǵ - ʮ - ʮ ڶ - ʮһ Ͷ촸 - ʮ ߹ - ʮ Դ - ʮ ִԴ - ʮ - ʮ - ʮ ʬ - ʮ ǰԴ - ʮ ʷ˵Ԯ - ʮ Ԫذ - ʮһ ̽ - ʮ - ʮ Դϫ - ʮ ָ - ʮ ɱ - ʮ ֮ - ʮ սеĥ - ʮ ͻ - ʮ ͻƣ - ʮ δ - ʮһ նж - ʮ ң - ʮ ʮ - ʮ - ʮ - ʮ ˼Դ - ʮ Ա - ʮ ȭͷ - ʮ - ٰʮ 㣬 - ٰʮһ - ٰʮ ָ - ٰʮ Ѷ - ٰʮ İʮ - ٰʮ ʵŴ - ٰʮ ʮ - ٰʮ ս - ٰʮ ܲ - ٰʮ ţ - پʮ ȷ - پʮһ - پʮ ˵ - پʮ ŵ - پʮ ûµ - پʮ ʮս - پʮ ڳԺڵļƻ - پʮ - پʮ - پʮ - ߰ ʵ - ߰һ ϻͶ - ߰ ² - ߰ ս - ߰ ִĿĵ - ߰ - ߰ ڰ - ߰ - ߰ ū - ߰ ֮ - ߰һʮ ջ - ߰һʮһ ѧԺ - ߰һʮ ʷ˷Ժ - ߰һʮ Ժд - ߰һʮ Ļ - ߰һʮ ֱ - ߰һʮ ֮ - ߰һʮ ű - ߰һʮ ս꼶 - ߰һʮ չ - ߰ٶʮ - ߰ٶʮһ - ߰ٶʮ - ߰ٶʮ - ߰ٶʮ 䣿 - ߰ٶʮ ˲ - ߰ٶʮ ˾ - ߰ٶʮ Զ - ߰ٶʮ ϲ˫ - ߰ٶʮ ϼϲ - ߰ʮ - ߰ʮ ѹʤ - ߰ʮ ij - ߰ʮ ˻궷 - ߰ʮ ս - ߰ʮ һԼ - ߰ʮ ħת - ߰ʮ Ӱħ - ߰ʮһ ѩŮ - ߰ʮ ֮ - ߰ʮ Ϊϲ - ߰ʮ ʤ - ߰ʮ ʼ - ߰ʮ ѪǮ - ߰ʮ ֮ - ߰ʮ - ߰ʮ - ߰ʮ Ƿӡ棡 - ߰ʮһ ƽɵĿغ - ߰ʮ ޣ - ߰ʮ ԻԳս - ߰ʮ ħ֮ - ߰ʮ ̼Ӱ - ߰ʮ ֮ - ߰ʮ Ƽ¼ - ߰ʮ ɻ - ߰ʮ - ٰ߰ʮ ȹ - ٰ߰ʮһ ͷ - ٰ߰ʮ ħɹ - ٰ߰ʮ ƽʥ֮ - ٰ߰ʮ һƭ - ٰ߰ʮ Ѱ - ٰ߰ʮ ϵļ - ٰ߰ʮ ūԼ - ٰ߰ʮ ߹ֿ - ٰ߰ʮ ߹ - ߰پʮ Ϊ - ߰پʮһ ֮ľ - ߰پʮ Ѱ˷ - ߰پʮ ٳɽ߹ - ߰پʮ - ߰پʮ ֮ˮ - ߰پʮ Ƭ - ߰پʮ ү - ߰پʮ ̩̹ȭ - ߰پʮ ع - ڰ˰ Ȱ˵ - ڰ˰һ ԭԻԵķ - ڰ˰ е - ڰ˰ ԭԻ - ڰ˰ ·С - ڰ˰ ͬѧǵ - ڰ˰ ѧܵ - ڰ˰ Դǵɻ - ڰ˰ - ڰ˰ ǰ - ڰ˰һʮ ɲݵڴ - ڰ˰һʮһ - ڰ˰һʮ ѧɵİأϣ - ڰ˰һʮ ѧɵİأ£ - ڰ˰һʮ ̩ۣ̩ϣ - ڰ˰һʮ ͻƵĴ - ڰ˰һʮ ͶӰ - ڰ˰һʮ ʮ껷 - ڰ˰һʮ Ϊ - ڰ˰һʮ ͻ - ڰ˰ٶʮ ض - ڰ˰ٶʮһ ̬ս - ڰ˰ٶʮ ħս - ڰ˰ٶʮ ̬ս - ڰ˰ٶʮ ɽս - ڰ˰ٶʮ - ڰ˰ٶʮ ǮڣҲܾ - ڰ˰ٶʮ ̬սԴ - ڰ˰ٶʮ ȺֹҪͬʱ - ڰ˰ٶʮ ˽ӣ - ڰ˰ʮ Ķֶ - ڰ˰ʮһ ħ - ڰ˰ʮ ͻȻ˵ - ڰ˰ʮ - ڰ˰ʮ ң - ڰ˰ʮ Լ - ڰ˰ʮ κİ취 - ڰ˰ʮ - ڰ˰ʮ Ҳ - ڰ˰ʮ ֹӺȵij - ڰ˰ʮ һڳ - ڰ˰ʮһ ڵֹӣ - ڰ˰ʮ ͳ - ڰ˰ʮ ֹ - ڰ˰ʮ 壬ʦô - ڰ˰ʮ ǰʧ - ڰ˰ʮ ദֹӺ - ڰ˰ʮ д - ڰ˰ʮ Ԫ - ڰ˰ʮ ҵԵ - ڰ˰ʮ ҪʥԨ - ڰ˰ʮһ - ڰ˰ʮ ǿĶ - ڰ˰ʮ н - ڰ˰ʮ Ҳ - ڰ˰ʮ ȥʥԨ - ڰ˰ʮ - ڰ˰ʮ ֻ - ڰ˰ʮ ֻ - ڰ˰ʮ ̨ - ڰ˰ʮ ܲ - ڰ˰ʮһ ̨ - ڰ˰ʮ ̨IJ² - ڰ˰ʮ βЫ - ڰ˰ʮ ŹĶʦ - ڰ˰ʮ Ԫط籩 - ڰ˰ʮ ٺٺ٣ - ڰ˰ʮ ʮħ - ڰ˰ʮ ˵ȣ - ڰ˰ʮ ֲը - ڰ˰ʮ ҽ - ڰ˰ʮһ ɹ - ڰ˰ʮ ҵ - ڰ˰ʮ 壡壡壡 - ڰ˰ʮ Ҫ - ڰ˰ʮ ʼ - ڰ˰ʮ ̬ս - ڰ˰ʮ ڲ - ڰ˰ʮ - ڰ˰ʮ յ롢 - ڰ˰ٰʮ ҲУ - ڰ˰ٰʮһ һ - ڰ˰ٰʮ ʮ - ڰ˰ٰʮ ս - ڰ˰ٰʮ ʮ - ڰ˰ٰʮ ʯ - ڰ˰ٰʮ ʮ - ڰ˰ٰʮ ǿ - ڰ˰ٰʮ - ڰ˰ٰʮ ʥԨ - ڰ˰پʮ һ - ڰ˰پʮһ ӣ - ڰ˰پʮ Ķ - ڰ˰پʮ ˿ʼ - ڰ˰پʮ ˼ - ڰ˰پʮ ȫ - ڰ˰پʮ Ҳ - ڰ˰پʮ Ϊˮ - ڰ˰پʮ - ڰ˰پʮ ɲɵ - ھŰ Һ - ھŰһ ȷ - ھŰ - ھŰ - ھŰ ټ - ھŰ ̽Ŀ - ھŰ ɨʼ - ھŰ ջ - ھŰ - ھŰ - ھŰһʮ ض - ھŰһʮһ ı - ھŰһʮ ֲ - ھŰһʮ ʱػ - ھŰһʮ һȭ - ھŰһʮ - ھŰһʮ п - ھŰһʮ Ϊ˶ - ھŰһʮ ʦ - ھŰһʮ ѧԺ㱨 - ھŰٶʮ 壬ʦ - ھŰٶʮһ Ľӻ - ھŰٶʮ ƾ㣿 - ھŰٶʮ ̽ - ھŰٶʮ ŵĽ - ھŰٶʮ 뷨 - ھŰٶʮ - ھŰٶʮ - ھŰٶʮ ˵ - ھŰٶʮ Ů - ھŰʮ һ - ھŰʮһ ִ - ھŰʮ ߽ - ھŰʮ - ھŰʮ ֵ˭ - ھŰʮ 鶨 - ھŰʮ ȸ - ھŰʮ - ھŰʮ ʱ - ھŰʮ - ھŰʮ ͻƣ - ھŰʮһ ǽ - ھŰʮ Ļţ - ھŰʮ ͻƣʮ߲ӡ - ھŰʮ - ھŰʮ Ҹ - ھŰʮ - ھŰʮ - ھŰʮ ʷ - ھŰʮ ȫԺ - ھŰʮ - ھŰʮһ - ھŰʮ ֲŲֿ - ھŰʮ - ھŰʮ ֮ŭ - ھŰʮ ҳȥ - ھŰʮ ֮ػ - ھŰʮ Ѫɫ֮ţ - ھŰʮ Ѫ - ھŰʮ ħ - ھŰʮ ƭ - ھŰʮһ 廷 - ھŰʮ Ӧ - ھŰʮ - ھŰʮ ѪѪ - ھŰʮ ֮ս - ھŰʮ սŶ - ھŰʮ ֮ŭ - ھŰʮ ƽһս - ھŰʮ ǹ - ھŰʮ 磬С - ھŰʮһ 㾹ҳ죿 - ھŰʮ - ھŰʮ - ھŰʮ ִ߽ - ھŰʮ - ھŰʮ - ھŰʮ ۰ - ھŰʮ е - ھŰʮ Dzȥ - ھŰٰʮ ס - ھŰٰʮһ ս - ھŰٰʮ - ھŰٰʮ սǰ - ھŰٰʮ ֺս - ھŰٰʮ ǧо - ھŰٰʮ սʼ - ھŰٰʮ Դƻ - ھŰٰʮ - ھŰٰʮ ½ս - ھŰپʮ δ - ھŰپʮһ ս - ھŰپʮ ս - ھŰپʮ Ƶ - ھŰپʮ ʮ - ھŰپʮ ͬھս - ھŰپʮ ֱ - ھŰپʮ DZʮ - ھŰپʮ - ھŰپʮ ͷ - һǧ ռԴ - һǧһ ֮ - һǧ - һǧ · - һǧ ᡭ - һǧ ɫĽ - һǧ - һǧ - һǧ ֮ - һǧ ʿ˵ - һǧһʮ - һǧһʮһ - һǧһʮ ļƻ - һǧһʮ С - һǧһʮ Ԩħ - һǧһʮ λ - һǧһʮ - һǧһʮ Ԫ - һǧһʮ У - һǧһʮ ߲ʽ - һǧʮ һŰֽ - һǧʮһ - һǧʮ - һǧʮ ȡ߹ֵĻ - һǧʮ ݸ㶨 - һǧʮ ħս - һǧʮ εĺ - һǧʮ ˮ - һǧʮ ؽ - һǧʮ 㱨 - һǧʮ - һǧʮһ - һǧʮ عĸ - һǧʮ ճ - һǧʮ ƽ - һǧʮ - һǧʮ һĨɫ - һǧʮ һ - һǧʮ Դ - һǧʮ ǵ - һǧʮ ͻƣ - һǧʮһ ֮ - һǧʮ ռƿ - һǧʮ Ѫ¯ - һǧʮ ֮ - һǧʮ ˲ѡߣ - һǧʮ ֲϵǵĻ - һǧʮ λ - һǧʮ - һǧʮ ꣬үү - һǧʮ ɫ - һǧʮһ - һǧʮ Ǵ죬Ҳǻ - һǧʮ ؽ - һǧʮ ˼ - һǧʮ Ҫ - һǧʮ - һǧʮ ѫ - һǧʮ Ǿ - һǧʮ ֲϵǵĴ - һǧʮ ٷ¡ - һǧʮһ ǼʮĿԴ - һǧʮ - һǧʮ ʵ - һǧʮ - һǧʮ - һǧʮ Ů - һǧʮ - һǧʮ 뷨 - һǧʮ һҪȥǸռ - һǧʮ ˻ - һǧʮһ - һǧʮ 𱨣 - һǧʮ Ůװ - һǧʮ Ů - һǧʮ - һǧʮ С - һǧʮ - һǧʮ īʿж - һǧʮ - һǧʮ ֻ - һǧʮһ īʿ - һǧʮ һϯ֮ - һǧʮ - һǧʮ Ѫȼ - һǧʮ - һǧʮ ֮Ĺ㷺Ӧã - һǧʮ ʧ - һǧʮ ֮ͨ - һǧʮ - һǧʮ ɱ - һǧʮһ ɹ - һǧʮ İݷ - һǧʮ - һǧʮ ɱ - һǧʮ ְˡ - һǧʮ ΣҲǻ - һǧʮ - һǧʮ ö - һǧʮ ףǹ - һǧһ ʼ - һǧһһ ߹ - һǧһ ҹ - һǧһ - һǧһ - һǧһ - һǧһ - һǧһ ʢ - һǧһ - һǧһ ʼ - һǧһһʮ ǧ - һǧһһʮһ ij - һǧһһʮ ֮ - һǧһһʮ - һǧһһʮ 仯 - һǧһһʮ - һǧһһʮ - һǧһһʮ ҿŶ - һǧһһʮ Ľ - һǧһһʮԼ - һǧһٶʮ ݼ - һǧһٶʮһ ƿ֮ʤ - һǧһٶʮ Ľ - һǧһٶʮ - һǧһٶʮ һȭ - һǧһٶʮ - һǧһٶʮ ʤ - һǧһٶʮ 븴 - һǧһٶʮ ǧԼ - һǧһٶʮ 衢 - һǧһʮ 㡢 - һǧһʮһ ɡ - һǧһʮ - һǧһʮ - һǧһʮ ʼҵİ - һǧһʮ ѭʼ - һǧһʮ - һǧһʮ ʥʿ - һǧһʮ Ӱ - һǧһʮ ֲ - һǧһʮ ף - һǧһʮһ - һǧһʮ Һ - һǧһʮ ֮ - һǧһʮ - һǧһʮ ڶ - һǧһʮ ʴ - һǧһʮ Ľ - һǧһʮ - һǧһʮ ʿ - һǧһʮ С - һǧһʮһ Ů - һǧһʮ ս - һǧһʮ սʤ - һǧһʮ - һǧһʮ - һǧһʮ ʹ - һǧһʮ м - һǧһʮ ܾˣ - һǧһʮ ŭİ - һǧһʮ - һǧһʮһ ĵڰ˻꼼 - һǧһʮ - һǧһʮ ķӦ - һǧһʮ Ĵʿ - һǧһʮ η - һǧһʮ ǿĪ - һǧһʮ - һǧһʮ - һǧһʮ ɱ - һǧһʮ ̨ķ - һǧһʮһ Ĵʿ - һǧһʮ ߲ - һǧһʮ - һǧһʮ - һǧһʮ - һǧһʮ - һǧһʮ ߴʿ - һǧһʮ ̭ʼ - һǧһʮ ͩ - һǧһٰʮ - һǧһٰʮһ ˮǣˮ - һǧһٰʮ 罻 - һǧһٰʮ ںϼ - һǧһٰʮ - һǧһٰʮ ħ - һǧһٰʮ ǰ˽ - һǧһٰʮ ڴԵ - һǧһٰʮ ս - һǧһٰʮ ͻȻת - һǧһپʮ ǿԪذ - һǧһپʮһ ǿ - һǧһپʮ ߽ӹ - һǧһپʮ Ľ - һǧһپʮ ӮңȢ - һǧһپʮ ʮʿ - һǧһپʮ ʼ - һǧһپʮ ǿ - һǧһپʮ תǬߵ - һǧһپʮ Ѫ - һǧ Ӯ - һǧһ - һǧ â - һǧ ؼ - һǧ ʿǵijɹ - һǧ 뿪 - һǧ - һǧ ʵְ - һǧ - һǧ ػճ - һǧһʮ ٿ - һǧһʮһ - һǧһʮ ȥˣ - һǧһʮ ְ֡ - һǧһʮ ռԴռϵͳ - һǧһʮ ʷ߹Ϊ - һǧһʮ - һǧһʮ ķ - һǧһʮ - һǧһʮ ֣ - һǧٶʮ Ҫ - һǧٶʮһ ѹ - һǧٶʮ ϺԵ - һǧٶʮ ʼ - һǧٶʮ һ - һǧٶʮ ɱ¶ - һǧٶʮ Իϲ˭ - һǧٶʮ Խ - һǧٶʮ Ϊˮ - һǧٶʮ Խ - һǧʮ ˵ - һǧʮһ ʾ - һǧʮ 㻹Ҫ - һǧʮ ʲô˵ˣң - һǧʮ Ѿ - һǧʮ ؾ - һǧʮ ҡ - һǧʮ ҡ - һǧʮ Ǻˣ - һǧʮ - һǧʮ ĺԵ - һǧʮ 棬ϲ - һǧʮһ ߶ - һǧʮ ԭԻϲǡ - һǧʮ Dzɵ - һǧʮ Ϊʲô߹ - һǧʮ ʷ߹ - һǧʮ ű - һǧʮ Ժѧī - һǧʮ ٻ - һǧʮ - һǧʮ 꼼ɾٻ - һǧʮһ ѧ - һǧʮ Ǯս - һǧʮ ھŻ꼼Ͼٻ - һǧʮ Ͼ - һǧʮ Ժʦ - һǧʮ - һǧʮ ս - һǧʮ ħս˫ϣ - һǧʮ ħս˫£ - һǧʮ ƽ - һǧʮһ ս - һǧʮ - һǧʮ ·VS - һǧʮ ս - һǧʮ - һǧʮ ħŮ - һǧʮ ֲ - һǧʮ ϲ - һǧʮ ǻȲˣ - һǧʮ ս - һǧʮһ նVSŭ - һǧʮ лл - һǧʮ - һǧʮ Ժʦ - һǧʮ ǿҶİ - һǧʮ ȫȽ껷 - һǧʮ Ӳʦ - һǧʮ ս - һǧʮ ŵĻԻ - һǧٰʮ ҵʱ - һǧٰʮһ - һǧٰʮ ôȥ - һǧٰʮ - һǧٰʮ ôܣ - һǧٰʮ ϴ - һǧٰʮ ͷˣҸ - һǧٰʮ - һǧٰʮ - һǧٰʮ ԣ - һǧپʮ ʼ - һǧپʮһ ⰵ - һǧپʮ ⰵ - һǧپʮ ط - һǧپʮ ִ - һǧپʮ ݵı仯 - һǧپʮ Ľ - һǧپʮ ԭ - һǧپʮ Сú - һǧپʮ ӭС - һǧ Ϥ - һǧһ ءҡ - һǧ 峤 - һǧ - һǧ ٵ - һǧ ̨ - һǧ ʿ - һǧ Ѫ - һǧ ֻܡ - һǧ Ҫͻ - һǧһʮ - һǧһʮһ Ա - һǧһʮ - һǧһʮ ʯ - һǧһʮ ʽ - һǧһʮ ߲ - һǧһʮ ͨ - һǧһʮ ͨ - һǧһʮ ɹ - һǧһʮ ˼ - һǧһʮ Ķ - һǧٶʮ С¶ - һǧٶʮһ ͻƣеĹⰵ - һǧٶʮ ըе - һǧʮ ˭˭ - һǧʮ ٵdz - һǧٰʮ սͫ - һǧٰʮһ һһ룿 - һǧٰʮ ¢ϱ - һǧٰʮ ӹܱ - һǧٰʮ - һǧٰʮ - һǧٰʮ ݺ - һǧٰʮ - һǧٰʮ ս - һǧٰʮ ģ - һǧپʮ Բ - һǧپʮ - һǧپʮ - һǧپʮ ս - һǧپʮ ʮλʿ - һǧپʮ ת - һǧپʮ Ůӵĵ - һǧپʮ ֮ĸ - һǧپʮ - һǧİ - һǧİһ άռ䣨ϣ - һǧİ άռ䣨£ - һǧİ - һǧİ ά - һǧİ Э - һǧİ - һǧİ Ϣ - һǧİ ձչ - һǧİ ٽ٣ - һǧİһʮ ȵע - һǧİһʮһ ɽ - һǧİһʮ ķ - һǧİһʮ 壡 - һǧİһʮ Ųʽ - һǧİһʮ Ųػ - һǧİһʮ ɽٹ - һǧİһʮ ҲҪŮ˰ - һǧİһʮ - һǧİһʮ - һǧİٶʮ - һǧİٶʮһ ʮ֮Լ - һǧİٶʮ ֮ - һǧİٶʮ ֮ı仯 - һǧİٶʮ - һǧİٶʮ ɽ - һǧİٶʮ ɽأ - һǧİٶʮ - һǧİٶʮ Ҫ㡭 - һǧİٶʮ ĸ - һǧİʮ ʧܹ - һǧİʮһ Ǹɽٵ - һǧİʮ Ⱥ - һǧİʮ Ľ - һǧİʮ - һǧİʮ ֪ͨ - һǧİʮ - һǧİʮ - һǧİʮ ǰ - һǧİʮ ı - һǧİʮ һ - һǧİʮһ - һǧİʮ - һǧİʮ - һǧİʮ ijͷ - һǧİʮ - һǧİʮ ټ - һǧİʮ - һǧİʮ Ϸȫݼ - һǧİʮ ǧ»Ǯ - һǧİʮ Ľ - һǧİʮһ ܲı - һǧİʮ ̻ - һǧİʮ - һǧİʮ - һǧİʮ - һǧİʮ ҼҴų - һǧİʮ Ů - һǧİʮ ߲׳ - һǧİʮ - һǧİʮ շ - һǧİʮһ - һǧİʮ - һǧİʮ һŮ - һǧİʮ ϣ - һǧİʮ ǵĿ - һǧİʮ ֮צָഺ - һǧİʮ ų - һǧİʮ ʼ - һǧİʮ ĸӶԻ - һǧİʮ - һǧİʮһ Ů - һǧİʮ ԴѪ - һǧİʮ һƷ - һǧİʮ Դ - һǧİʮ - һǧİʮ öڶ - һǧİʮ ̧ - һǧİʮ һ - һǧİʮ տ - һǧİٰʮ 뾺 - һǧİٰʮһ - һǧİٰʮ ػʯ - һǧİٰʮ - һǧİٰʮ - һǧİٰʮ - һǧİٰʮ ֮ - һǧİٰʮ ʺ - һǧİٰʮ - һǧİٰʮ Ҿ - һǧİپʮ ƣ - һǧİپʮһ £ - һǧİپʮ ǹ - һǧİپʮ ֮ - һǧİپʮ ߵǬ - һǧİپʮ ȷѪľ - һǧİپʮ һǧ - һǧİپʮ ᣬ - һǧİپʮ ٹ鴦 - һǧİپʮ - һǧ ʮ - һǧһ dz - һǧ ʼ - һǧ ʥ - һǧ - һǧ - һǧ - һǧ ϵ - һǧ ˳ - һǧ - һǧһʮ һǧ - һǧһʮһ õٹ - һǧһʮ ͩľ - һǧһʮ մ - һǧһʮ - һǧһʮ ļۣһڣ - һǧһʮ һڳɽ - һǧһʮ ø - һǧһʮ ˮ - һǧһʮ ع飬ջһ - һǧٶʮ ع飬ջ - һǧٶʮһ ع飬ջ - һǧٶʮ ع飬ջģ - һǧٶʮ ع飬ջ壩 - һǧٶʮ ع飬ջ - һǧٶʮ ع飬ջߣ - һǧٶʮ ع飬ջˣ - һǧٶʮ Ԫ˧ - һǧٶʮ ʮûչ - һǧٶʮ ͭ - һǧʮ - һǧʮһ ȥ - һǧʮ Ϊҫ - һǧʮ - һǧʮ » - һǧʮ - һǧʮ ʱָ - һǧʮ ʷǿDZ - һǧʮ Ի - һǧʮ ־ - һǧʮ ͻ - һǧʮһ - һǧʮ ұ - һǧʮ ֮ˣ - һǧʮ սĻƿ - һǧʮ ǿ֮ - һǧʮ - һǧʮ ٽ - һǧʮ Ѫɫ - һǧʮ ʿ - һǧʮ ڻʽ - һǧʮһ ʿ֮ - һǧʮ οƲ - һǧʮ - һǧʮ ʦ - һǧʮ ͵ - һǧʮ ػ - һǧʮ Ӧ - һǧʮ ָ - һǧʮ Ϣ - һǧʮ - һǧʮһ ȴʱ - һǧʮ ε֮ - һǧʮ Ԯִ - һǧʮ ս - һǧʮ ƽǹЧ - һǧʮ ͽ - һǧʮ ս - һǧʮ ðյĻ - һǧʮ Ȼ - һǧʮ Լ裿 - һǧʮһ - һǧʮ תĿ - һǧʮ - һǧʮ һž - һǧʮ ĸ֧ - һǧʮ ڻɽ - һǧʮ - һǧʮ ʧ - һǧʮ ͻϮ - һǧٰʮ Ȼ - һǧٰʮһ ͻƣ - һǧٰʮ ȵĵ - һǧٰʮ - һǧٰʮ ˡ - һǧٰʮ ս - һǧٰʮ ɾ - һǧٰʮ Ǯڳ - һǧٰʮ - һǧٰʮ ֮Ҳò - һǧپʮ - һǧپʮһ ɱ - һǧپʮ ѡ - һǧپʮ һ߹ǵʹ - һǧپʮ Ѹٱǿ - һǧپʮ ֮Ŀʲô - һǧپʮ ټ - һǧپʮ ȴ - һǧپʮ - - - - - - - \ No newline at end of file diff --git a/app/version_code.properties b/app/version_code.properties index 53ba11d..794e21a 100644 --- a/app/version_code.properties +++ b/app/version_code.properties @@ -1,2 +1,2 @@ -#Sat Mar 13 21:51:35 CST 2021 -VERSION_CODE=193 +#Sun Apr 25 18:50:43 CST 2021 +VERSION_CODE=194 diff --git a/build.gradle b/build.gradle index a084155..71b15fb 100644 --- a/build.gradle +++ b/build.gradle @@ -5,9 +5,9 @@ ext { buildscript { ext.kotlin_version = '1.4.20' repositories { + google() jcenter() mavenCentral() - google() maven { url 'https://s3.amazonaws.com/fabric-artifacts/public' } maven { url 'https://plugins.gradle.org/m2/' } maven { url "https://maven.java.net/content/groups/public/" } @@ -24,9 +24,9 @@ buildscript { allprojects { repositories { + google() jcenter() maven { url "https://jitpack.io" } - google() } /*gradle.projectsEvaluated { tasks.withType(JavaCompile) { diff --git a/debug/output-metadata.json b/debug/output-metadata.json new file mode 100644 index 0000000..84b2138 --- /dev/null +++ b/debug/output-metadata.json @@ -0,0 +1,18 @@ +{ + "version": 2, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "xyz.fycz.myreader", + "variantName": "processDebugResources", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "versionCode": 193, + "versionName": "1.9.3", + "outputFile": "风月读书v1.9.3.apk" + } + ] +} \ No newline at end of file diff --git a/debug/风月读书v1.9.3.apk b/debug/风月读书v1.9.3.apk new file mode 100644 index 0000000..97add58 Binary files /dev/null and b/debug/风月读书v1.9.3.apk differ diff --git a/gradle.properties b/gradle.properties index af6dcbe..6d8cf36 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,4 +16,5 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true + diff --git a/source/DIYSource.md b/source/DIYSource.md index 510dd18..6929096 100644 --- a/source/DIYSource.md +++ b/source/DIYSource.md @@ -42,7 +42,7 @@ #### 1、概要 -* 书源规则支持四个普通函数和一个列表函数,分别为@r/@replace,@a/@append,@c/contains,@nc/notContains,! +* 书源规则支持四个普通函数和一个列表函数,分别为@r/@replace,@a/@append,@c/contains,@nc/notContains,!,% * 如何使用:在书源规则后添加 ***##+函数*** ,多个函数以半角分号 **;** 分隔,若函数内部出现 **;** 请使用 **\;** 转义 #### 2、@r/@replace替换函数 @@ -123,9 +123,34 @@ #### 6、!跳过列表前几个函数 -* 语法:!+数字 +* 语法:!n +* n为跳过数量 * 此函数仅支持书籍列表(Xpath、JsonPath),章节列表(Matcher、Xpath、JsonPath) +#### 7、!列表分组反转函数 + +* 语法:%n +* 以n为分组长度进行分组,并对各组进行反转 +* 此函数仅支持书籍列表(Xpath、JsonPath),章节列表(Matcher、Xpath、JsonPath) +* 例如: + * ```html + 第三章 + 第二章 + 第一章 + + 第六章 + 第五章 + 第四章 + + 第九章 + 第八章 + 第七章 + + 第十章 + ``` + + * 以上列表每三个为一组顺序倒置了,故可用此函数:%3 即可的到正确排序的列表 + ### 三、书源编辑
作者:霞露分类:其他连载中
简介:她,身负系统,莫名来到了大主宰的时空,成为了聚灵族的最后族人。在她还对周围的情况一片模糊的时候,她遇到了林静这个小恶魔。拜林静所赐,她还遇到了武祖,成为了武柤的小徒弟。参与了灵路,遇到了牧尘和洛璃,她默默的在心中问着自己那一直在划水的系统:“你的活来了,说吧,我该干什么?”
ߣƼ
״ ̬
¸£һǧپʮ
£2021-02-14 17:29
һˡ - ƿڼ֮ؿƿʱһŽ˫ɫƵĵ֮̽ȻæозˣɷȴһӤһģһӤһĺӡ -
λҪǾá½IVռޡĻ벻ҪQQȺƼŶ