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 @@ \ 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 activity){ + protected void startActivity(Class 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以<link>占位,不支持普通函数,规则后接##!加数字可以跳过列表前几个\n" + - "对于Xpath/JsonPath解析器:此处填写章节名称,支持普通函数")) + "对于Mathcer解析器:此处填写章节名称和URL规则,其中章节名称以<title>占位,章节URL以<link>占位,仅支持列表函数\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<ReadContext> getReadContextList(String rule, ReadContext rc) { List<ReadContext> 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<Chapter> matchChapters(String rule, String html, String baseUrl) { ArrayList<Chapter> 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("<link>") || !rule.contains("<title>")) 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("<link>") > rule.indexOf("<title>")) { @@ -152,10 +150,8 @@ public class MatcherAnalyzer extends BaseAnalyzer{ rule = rule.replace("<title>", "(.*?)"); 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<Chapter>) 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<JXNode> getJXNodeList(String rule, JXDocument JXDoc) { List<JXNode> 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<JXNode> selN = JXDoc.selN(rule); - list = selN.subList(skip, selN.size()); - return list; + list = JXDoc.selN(rule); + return !hasFunction ? list : evalListFunction(funs, list); } public List<JXNode> getJXNodeList(String rule, JXNode jxNode) { List<JXNode> 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<JXNode> 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<Boolean>() { + @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<int[]>() { + @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<Book> 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<Boolean>() { + @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<View> 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<Boolean>() { + @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<View> 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<Boolean>) 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<Boolean>() { + @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<Boolean> checkHasAd() { + return Single.create((SingleOnSubscribe<Boolean>) 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<Boolean>) 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<Boolean>() { + @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<int[]> adTimes() { + return Single.create((SingleOnSubscribe<int[]>) 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 { </dd> </dl> */ + @Deprecated public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { ConcurrentMultiValueMap<SearchBookBean, Book> 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<Chapter> getChaptersFromHtml(String html) { + ArrayList<Chapter> 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中得到书列表 + * <li> + * <a class="pic" href="http://www.bjcan.com/book/74544.html" target="_blank"><img class="lazy" src="http://www.bjcan.com/uploads/novel/20200907/b38cea14e4a3de1e876395e378c1e544.jpeg" alt="大主宰:灵玖"></a> + * <h5 class="tit"><a href="http://www.bjcan.com/book/74544.html" target="_blank">大主宰:灵玖</a></h5> + * <p class="info">作者:<span>霞露</span><span>分类:其他</span><i class="serial">连载中</i></p> + * <p class="intro">简介:她,身负系统,莫名来到了大主宰的时空,成为了聚灵族的最后族人。在她还对周围的情况一片模糊的时候,她遇到了林静这个小恶魔。拜林静所赐,她还遇到了武祖,成为了武柤的小徒弟。参与了灵路,遇到了牧尘和洛璃,她默默的在心中问着自己那一直在划水的系统:“你的活来了,说吧,我该干什么?”</p> + * <a class="view" href="http://www.bjcan.com/book/74544.html" target="_blank">小说详情</a> + * </li> + */ + public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { + ConcurrentMultiValueMap<SearchBookBean, Book> 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 { </div> </li> */ + @Deprecated public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { ConcurrentMultiValueMap<SearchBookBean, Book> 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 { </div> </li> */ + @Deprecated public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { ConcurrentMultiValueMap<SearchBookBean, Book> 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<Drawable> { 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:duration="300" + android:fromYDelta="100%p" + android:toYDelta="0"/> + + <alpha + android:duration="300" + android:fromAlpha="0.0" + android:toAlpha="1.0"/> +</set> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:duration="200" + android:fromYDelta="0" + android:toYDelta="50"/> + <alpha + android:duration="200" + android:fromAlpha="1.0" + android:toAlpha="0.0"/> +</set> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + + <corners android:radius="10dp" /> + <solid android:color="@android:color/black"/> +</shape> \ 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 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + <path + android:fillColor="#FF000000" + android:pathData="M512,960C265.6,960 64,758.4 64,512S265.6,64 512,64s448,201.6 448,448 -201.6,448 -448,448zM512,128c-211.2,0 -384,172.8 -384,384s172.8,384 384,384 384,-172.8 384,-384 -172.8,-384 -384,-384zM512,800c-17.6,0 -32,-14.4 -32,-32L480,480c0,-17.6 14.4,-32 32,-32s32,14.4 32,32v288c0,17.6 -14.4,32 -32,32zM672,672L352,672c-17.6,0 -32,-14.4 -32,-32s14.4,-32 32,-32h320c17.6,0 32,14.4 32,32s-14.4,32 -32,32zM672,512L352,512c-17.6,0 -32,-14.4 -32,-32s14.4,-32 32,-32h320c17.6,0 32,14.4 32,32s-14.4,32 -32,32zM512,512c-8,0 -16,-3.2 -22.4,-9.6L331.2,344c-12.8,-12.8 -12.8,-32 0,-44.8 12.8,-12.8 32,-12.8 44.8,0l158.4,158.4c12.8,12.8 12.8,32 0,44.8 -6.4,6.4 -14.4,9.6 -22.4,9.6zM512,512c-8,0 -16,-3.2 -22.4,-9.6 -12.8,-12.8 -12.8,-32 0,-44.8L648,299.2c12.8,-12.8 32,-12.8 44.8,0 12.8,12.8 12.8,32 0,44.8L534.4,502.4C528,508.8 520,512 512,512z"/> +</vector> 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 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> +<!-- <path + android:pathData="M583.58,642.25V345.19s199.99,-19.61 199.99,143.16 -174.54,153.91 -199.99,153.91zM298.29,542.16l68.66,-147.05 57.8,147.05z" + android:fillColor="#ffa115"/>--> + <path + android:pathData="M809.88,923.9H220.52c-73.42,0 -133.12,-59.7 -133.12,-133.12V218.62c0,-73.42 59.7,-133.12 133.12,-133.12h589.36c73.42,0 133.12,59.7 133.12,133.12v572.16c0,73.37 -59.75,133.12 -133.12,133.12zM220.52,146.94c-39.53,0 -71.68,32.15 -71.68,71.68v572.16c0,39.53 32.15,71.68 71.68,71.68h589.36c39.53,0 71.68,-32.15 71.68,-71.68V218.62c0,-39.53 -32.15,-71.68 -71.68,-71.68H220.52z" + android:fillColor="#474A54"/> + <path + android:pathData="M506.57,625.36L388.92,342.02a30.74,30.74 0,0 0,-28.36 -18.94h-0.2c-12.49,0.05 -23.71,7.68 -28.31,19.25L218.32,625.66c-6.3,15.77 1.33,33.64 17.05,39.94a30.68,30.68 0,0 0,39.94 -17.1l26.88,-67.02L421.89,581.48l27.96,67.33a30.73,30.73 0,0 0,40.14 16.59c15.67,-6.4 23.09,-24.37 16.59,-40.04zM326.91,520.14l34.15,-85.09 35.33,85.09L326.91,520.14zM592.33,673.02c-1.74,0 -3.53,0 -5.32,-0.05l-3.43,-0.05c-16.95,0 -30.72,-13.77 -30.72,-30.72L552.86,345.19c0,-15.82 11.98,-29.03 27.7,-30.57 4.71,-0.46 115.66,-10.55 183.4,50.84 33.43,30.31 50.33,71.63 50.33,122.88 0,53.04 -16.23,95.95 -48.28,127.64 -38.3,37.84 -96.72,57.04 -173.67,57.04zM614.3,375.4v235.57c49,-2.92 85.45,-15.87 108.54,-38.71 20.22,-19.97 30.05,-47.41 30.05,-83.92 0,-33.54 -9.83,-58.83 -30.05,-77.21 -30.72,-28.01 -78.49,-34.61 -108.54,-35.74z" + android:fillColor="#474A54"/> +</vector> 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 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + <path + android:pathData="M856.9,927.3H118.6c-8.5,0 -16.7,-3.4 -22.7,-9.4s-9.4,-14.2 -9.3,-22.7l2,-732.3c0,-17.6 14.4,-31.9 32,-31.9H633c17.7,0 32,14.3 32,32s-14.3,32 -32,32H152.6l-1.8,668.3H825l1.5,-514.8c0.1,-17.6 14.4,-31.9 32,-31.9h0.1c17.7,0.1 32,14.4 31.9,32.1l-1.6,546.7c0,17.6 -14.3,31.9 -32,31.9z" + android:fillColor="#ffffff"/> + <path + android:pathData="M401.1,665c-8.2,0 -16.4,-3.1 -22.6,-9.4 -12.5,-12.5 -12.5,-32.8 0,-45.3L882.8,106c12.5,-12.5 32.8,-12.5 45.3,0s12.5,32.8 0,45.3L423.7,655.7c-6.3,6.2 -14.5,9.3 -22.6,9.3z" + android:fillColor="#ffffff"/> +</vector> 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"> <path - android:fillColor="#FF000000" + android:fillColor="#FFFFFF" android:pathData="M511.61,961.62"/> <path - android:fillColor="#FF000000" + android:fillColor="#FFFFFF" android:pathData="M946.87,147.17l-70.84,-70.85c-16.32,-16.08 -42.42,-16.08 -58.5,0l-74.12,74.11 129.34,129.34 74.11,-73.88C962.95,189.35 962.95,163.25 946.87,147.17L946.87,147.17 946.87,147.17zM721.51,172.57 L320.67,586.23 287.82,735.38l149.15,-32.86 411.56,-403.17L721.51,172.57 721.51,172.57zM809.84,511.65l0,349.57c0,11.89 -9.08,23.54 -21.44,23.54 -12.35,0 -626.9,-0.24 -626.9,-0.24 -13.05,0 -22.84,-12.58 -22.84,-23.31L138.66,236.65c0,-11.65 10.95,-23.31 24.71,-23.31l348.17,0 74.57,-74.57 -467.49,0c-30.3,0 -54.54,24 -54.54,54.3l0,711.5c0,30.06 24.47,54.53 54.54,54.53l711.49,0c30.06,0 54.3,-24.47 54.3,-54.53L884.41,437.08 809.84,511.65 809.84,511.65 809.84,511.65zM809.84,511.65"/> </vector> 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 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="1024" + android:viewportHeight="1024"> + <path + android:fillColor="#FF000000" + android:pathData="M512,480.2c169.7,0 350,-55.7 350,-159.1S681.7,162 512,162c-169.7,0 -350,55.7 -350,159.1S342.3,480.2 512,480.2zM512,225.6c174.8,0 286.4,56.5 286.4,95.5S686.8,416.5 512,416.5 225.6,360 225.6,321.1 337.2,225.6 512,225.6z"/> + <path + android:fillColor="#FF000000" + android:pathData="M225.5,454.8c-14.2,-10.4 -34.1,-7.2 -44.4,7 -13,17.8 -19.4,36.8 -19,56.4 0.8,47.9 41.8,89.6 115.2,117.5 60.7,23.1 139.4,35.5 222.9,35.5 4.8,0 9.7,-0.1 14.6,-0.1 169.7,-3 349,-61.9 347.2,-165.1 -0.2,-15 -4.4,-29.6 -12.6,-43.5 -8.9,-15.2 -28.6,-20.2 -43.5,-11.3 -15.2,8.9 -20.3,28.4 -11.3,43.6 2.5,4.3 3.7,8.2 3.8,12.2 0.7,38.9 -109.9,97.3 -284.7,100.5 -81.4,1 -156.9,-9.7 -213.7,-31.3 -44.8,-17 -73.9,-40.2 -74.2,-59.2 -0.1,-5.4 2.2,-11.4 6.7,-17.7C242.8,485 239.7,465.1 225.5,454.8z"/> + <path + android:fillColor="#FF000000" + android:pathData="M801.4,643.2c-14.5,9.8 -18.5,29.6 -8.6,44.2 3.8,5.6 5.6,10.7 5.6,15.5 0,38.9 -111.6,95.5 -286.4,95.5s-286.4,-56.6 -286.4,-95.5c0,-4.7 1.7,-9.6 5.1,-14.8 9.6,-14.7 5.6,-34.4 -9.1,-44.1 -14.7,-9.6 -34.4,-5.5 -44.1,9.1 -10.3,15.7 -15.5,32.4 -15.5,49.7C162,806.3 342.3,862 512,862c169.7,0 350,-55.7 350,-159.1 0,-17.7 -5.5,-34.9 -16.4,-51.1C835.8,637.2 816,633.4 801.4,643.2z"/> +</vector> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="#fffb8435" /> + <gradient + android:endColor="#fffe795f" + android:startColor="#fffb8435" + android:type="linear" + android:angle="180" + android:useLevel="true" /> + <corners + android:bottomLeftRadius="20dp" + android:bottomRightRadius="20dp" + android:topLeftRadius="20dp" + android:topRightRadius="20dp" /> +</shape> \ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:bottomLeftRadius="10dp" + android:bottomRightRadius="10dp"/> + <solid android:color="@color/white"/> +</shape> \ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/toolbar" /> + + <RelativeLayout + android:id="@+id/rl_ad" + android:layout_width="match_parent" + android:layout_height="50dp" + android:background="@drawable/selector_common_bg" + android:gravity="center" + android:paddingLeft="20dp" + android:paddingRight="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:text="@string/ad" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + <androidx.appcompat.widget.SwitchCompat + android:id="@+id/sc_ad" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + android:checked="false" + android:clickable="false" + android:enabled="false" + android:longClickable="false" /> + </RelativeLayout> + + <LinearLayout + android:id="@+id/ll_ad_setting" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="25dp" + android:paddingLeft="5dp" + android:paddingRight="5dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerInParent="true" + android:text="@string/splash_ad" + android:textColor="@color/textPrimary" /> + </RelativeLayout> + + <LinearLayout + android:id="@+id/ll_splash_ad_times" + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@drawable/selector_common_bg" + android:orientation="vertical" + android:paddingLeft="20dp" + android:paddingTop="8dp" + android:paddingRight="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/splash_ad_times" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + <TextView + android:id="@+id/tv_splash_cur_ad_times" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" + android:text="@string/splash_cur_ad_times" + android:textColor="@color/textAssist" /> + </LinearLayout> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="25dp" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:visibility="gone"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerInParent="true" + android:text="@string/book_detail_ad" + android:textColor="@color/textPrimary" /> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/rl_book_detail_ad" + android:layout_width="match_parent" + android:layout_height="50dp" + android:background="@drawable/selector_common_bg" + android:gravity="center" + android:paddingLeft="20dp" + android:paddingRight="20dp" + android:visibility="gone"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:text="@string/book_detail_ad_sc" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + <androidx.appcompat.widget.SwitchCompat + android:id="@+id/sc_book_detail_ad" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + android:checked="false" + android:clickable="false" + android:longClickable="false" /> + </RelativeLayout> + </LinearLayout> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="25dp" + android:paddingLeft="5dp" + android:paddingRight="5dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerInParent="true" + android:text="@string/ad_file" + android:textColor="@color/textPrimary" /> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/rl_delete_ad_file" + android:layout_width="match_parent" + android:layout_height="50dp" + android:background="@drawable/selector_common_bg" + android:paddingLeft="20dp" + android:paddingRight="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:text="@string/delete_ad_file" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + </RelativeLayout> + +</LinearLayout> \ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + android:orientation="vertical"> + + <include layout="@layout/toolbar" /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="5dp"> + + <xyz.fycz.myreader.widget.CoverImageView + android:id="@+id/iv_cover" + android:layout_width="90dp" + android:layout_height="126dp" + android:scaleType="centerCrop" + android:src="@mipmap/default_cover" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="5dp"> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/til_book_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/book_name" + android:textColorHint="@color/textSecondary"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/tie_book_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:textColor="@color/textPrimary" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/til_book_author" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/author" + android:textColorHint="@color/textSecondary"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/tie_book_author" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:textColor="@color/textPrimary" /> + </com.google.android.material.textfield.TextInputLayout> + + </LinearLayout> + </LinearLayout> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/til_cover_url" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + android:hint="@string/cover_path" + android:textColorHint="@color/textSecondary"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/tie_cover_url" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColor="@color/textPrimary" /> + </com.google.android.material.textfield.TextInputLayout> + + <androidx.appcompat.widget.AppCompatButton + android:id="@+id/bt_select_local_pic" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:layout_alignParentEnd="true" + android:layout_centerVertical="true" + android:text="@string/select_local_pic" /> + </RelativeLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingStart="5dp" + android:paddingEnd="5dp"> + + + </LinearLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/til_book_jj" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:hint="@string/book_desc" + android:textColorHint="@color/textSecondary"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/tie_book_desc" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColorHint="@color/textSecondary" /> + </com.google.android.material.textfield.TextInputLayout> + + </LinearLayout> + </ScrollView> +</LinearLayout> \ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/colorBackground" + android:scrollbars="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <include layout="@layout/toolbar" /> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="25dp" + android:paddingLeft="5dp" + android:paddingRight="5dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerInParent="true" + android:text="@string/donate_author" + android:textColor="@color/textPrimary" /> + </RelativeLayout> + + <LinearLayout + android:id="@+id/ll_wx_zsm" + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@drawable/selector_common_bg" + android:orientation="vertical" + android:paddingLeft="20dp" + android:paddingTop="8dp" + android:paddingRight="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/wx_zsm" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" + android:text="@string/donate_tip" + android:textColor="@color/textAssist" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/ll_zfb_skm" + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@drawable/selector_common_bg" + android:orientation="vertical" + android:paddingLeft="20dp" + android:paddingTop="8dp" + android:paddingRight="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/zfb_skm" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" + android:text="@string/donate_tip" + android:textColor="@color/textAssist" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/ll_qq_skm" + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@drawable/selector_common_bg" + android:orientation="vertical" + android:paddingLeft="20dp" + android:paddingTop="8dp" + android:paddingRight="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/qq_skm" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" + android:text="@string/donate_tip" + android:textColor="@color/textAssist" /> + </LinearLayout> + + <LinearLayout + android:id="@+id/ll_ad_support" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="40dp" + android:orientation="vertical" + android:paddingLeft="5dp" + android:paddingRight="5dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="25dp" + android:gravity="center" + android:text="@string/ad_support" + android:textColor="@color/textPrimary" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="15dp" + android:gravity="center" + android:text="@string/ad_support_tip" + android:textSize="@dimen/text_minimum_size" + android:textColor="@color/textSecondary" /> + </LinearLayout> + + <RelativeLayout + android:background="@color/colorForeground" + android:layout_width="match_parent" + android:layout_height="25dp" + android:paddingLeft="5dp" + android:paddingRight="5dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerInParent="true" + android:text="@string/flow_ad_tip" + android:textColor="@color/textSecondary" /> + </RelativeLayout> + + <LinearLayout + android:id="@+id/ll_rewarded_video" + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@drawable/selector_common_bg" + android:orientation="vertical" + android:paddingLeft="20dp" + android:paddingTop="8dp" + android:paddingRight="20dp"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/rewarded_video" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" + android:text="@string/donate_tip" + android:textColor="@color/textAssist" /> + </LinearLayout> + </LinearLayout> + + + </LinearLayout> +</androidx.core.widget.NestedScrollView> 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 @@ <androidx.appcompat.widget.AppCompatSpinner android:id="@+id/sc_reset_screen" - android:layout_width="115dp" + android:layout_width="130dp" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:clickable="false" - android:dropDownWidth="75dp" + android:dropDownWidth="90dp" android:gravity="center" android:longClickable="false" /> </RelativeLayout> 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 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.appcompat.widget.AppCompatImageView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/iv_splash" +<RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" - android:scaleType="fitXY" - app:srcCompat="@drawable/start"/> \ 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"> + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/iv_splash" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="fitXY" + app:srcCompat="@drawable/start"/> + <LinearLayout + android:id="@+id/ll_ad" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <FrameLayout + android:id="@+id/fl_ad" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1"/> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="8"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerInParent="true" + android:orientation="horizontal"> + <androidx.appcompat.widget.AppCompatImageView + android:layout_width="wrap_content" + android:layout_height="match_parent" + app:srcCompat="@mipmap/ic_launcher"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center" + android:layout_marginStart="5dp" + android:textColor="@color/textPrimary" + android:textSize="@dimen/text_medium_size" + android:text="@string/app_name"/> + </LinearLayout> + </RelativeLayout> + </LinearLayout> + +</RelativeLayout> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:gravity="center" + android:background="@drawable/bg_loading_dialog" + android:layout_height="match_parent"> + + <ImageView + android:id="@+id/iv_loading" + android:layout_width="wrap_content" + android:src="@mipmap/ic_dialog_loading" + android:layout_height="wrap_content"/> + + <TextView + android:id="@+id/tv_loading" + android:layout_width="wrap_content" + android:layout_marginTop="20dp" + android:text="@string/loading" + android:textSize="16sp" + android:textColor="@android:color/white" + android:layout_height="wrap_content"/> + +</LinearLayout> \ 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" /> </RelativeLayout> + <RelativeLayout + android:id="@+id/mine_rl_ad_setting" + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@drawable/selector_common_bg" + android:paddingLeft="20dp" + android:paddingRight="20dp"> + + <ImageView + android:id="@+id/iv_ad_setting" + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:layout_marginStart="3dp" + android:src="@drawable/ic_ad" + app:tint="@color/textPrimary" /> + + <TextView + android:id="@+id/tv_ad_setting" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_marginStart="16dp" + android:layout_toEndOf="@id/iv_ad_setting" + android:text="@string/ad_setting" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + </RelativeLayout> <RelativeLayout android:layout_width="match_parent" @@ -342,7 +372,35 @@ android:textSize="@dimen/text_normal_size" /> </RelativeLayout> + <RelativeLayout + android:id="@+id/mine_rl_donate" + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@drawable/selector_common_bg" + android:paddingLeft="20dp" + android:paddingRight="20dp"> + + <ImageView + android:id="@+id/iv_donate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:src="@drawable/ic_support" + app:tint="@color/textPrimary" /> + <TextView + android:id="@+id/tv_donate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_marginStart="15dp" + android:layout_toEndOf="@id/iv_donate" + android:text="@string/support_author" + android:textColor="@color/textSecondary" + android:textSize="@dimen/text_normal_size" /> + + </RelativeLayout> <RelativeLayout android:id="@+id/mine_rl_about" android:layout_width="match_parent" @@ -373,5 +431,6 @@ </RelativeLayout> + </LinearLayout> </androidx.core.widget.NestedScrollView> \ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#99000000" + android:clickable="true" + android:focusable="true" + android:focusableInTouchMode="true" + android:gravity="center" + android:orientation="vertical" + android:paddingStart="50dp" + android:paddingEnd="50dp"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@mipmap/down_up"> + + <TextView + android:id="@+id/tv_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="15dp" + android:layout_marginTop="30dp" + android:text="发现新版本" + android:textColor="@color/white" + android:textSize="20sp" /> + + <TextView + android:id="@+id/tv_version" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/tv_title" + android:layout_marginStart="15dp" + android:layout_marginTop="5dp" + android:textColor="@color/white" + android:textSize="13sp" /> + </RelativeLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@mipmap/down_center" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="15dp" + android:paddingTop="5dp" + android:paddingEnd="15dp" + android:paddingBottom="5dp" + android:text="更新内容" + android:textColor="@color/black" + android:textSize="15sp" /> + + + <TextView + android:id="@+id/tv_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:lineSpacingExtra="5dp" + android:maxLines="8" + android:overScrollMode="always" + android:paddingStart="15dp" + android:paddingTop="5dp" + android:paddingEnd="15dp" + android:paddingBottom="5dp" + android:scrollbars="vertical" + android:fadeScrollbars="false" + android:textColor="@color/gray" + android:textSize="13sp" /> + + <Button + android:id="@+id/bt_update" + android:layout_width="match_parent" + android:layout_height="40dp" + android:layout_marginStart="30dp" + android:layout_marginTop="20dp" + android:layout_marginEnd="20dp" + android:background="@drawable/shape_confirm" + android:gravity="center" + android:text="立即更新" + android:textColor="@color/white" + android:textSize="14sp" /> + + <Button + android:id="@+id/bt_browser" + android:layout_width="match_parent" + android:layout_height="40dp" + android:layout_marginStart="30dp" + android:layout_marginTop="10dp" + android:layout_marginEnd="20dp" + android:background="@drawable/shape_confirm" + android:gravity="center" + android:text="浏览器下载" + android:textColor="@color/white" + android:textSize="14sp" /> + + + <RelativeLayout + android:id="@+id/rl_progress" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="30dp" + android:layout_marginTop="20dp" + android:layout_marginEnd="20dp" + android:visibility="gone"> + + <xyz.fycz.myreader.widget.BarPercentView + android:id="@+id/bar_percent_view" + android:layout_width="match_parent" + android:layout_height="40dp" + app:barEndColor="@color/end_fffb8435" + app:barIsGradient="true" + app:barRadius="10dp" + app:barStartColor="@color/start_fffe795f" /> + + <TextView + android:id="@+id/tv_progress" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:text="0%" + android:textColor="@color/white" /> + </RelativeLayout> + </LinearLayout> + + <TextView + android:layout_width="match_parent" + android:layout_height="20dp" + android:background="@drawable/shape_dialog_buttom" /> + + <ImageView + android:id="@+id/iv_close" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="25dp" + android:src="@mipmap/dialog_close_update" /> +</LinearLayout> \ 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 @@ <View android:layout_width="match_parent" - android:layout_height="10dp" + android:layout_height="5dp" + android:background="@color/colorDivider"/> + + <View + android:layout_width="match_parent" + android:layout_height="5dp" android:background="@color/colorDivider"/> <!--书籍目录--> 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 @@ <?xml version ="1.0" encoding ="utf-8"?> -<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> <item - android:id="@+id/action_change_source" - android:icon="@drawable/ic_change" - android:title="@string/menu_change_source" - app:showAsAction="ifRoom"/> + android:id="@+id/action_change_source" + android:icon="@drawable/ic_change" + android:title="@string/menu_change_source" + app:showAsAction="ifRoom" /> <item android:id="@+id/action_share" android:icon="@drawable/ic_share" android:title="@string/menu_share" - app:showAsAction="ifRoom"/> - + app:showAsAction="ifRoom" /> + <item + android:id="@+id/action_edit" + android:icon="@drawable/ic_edit" + android:title="@string/menu_edit" + app:showAsAction="never" /> <item - android:id="@+id/action_reload" - android:icon="@drawable/ic_refresh" - android:title="@string/menu_reload" - app:showAsAction="never"/> + android:id="@+id/action_reload" + android:icon="@drawable/ic_refresh" + android:title="@string/menu_reload" + app:showAsAction="never" /> <item - android:id="@+id/action_open_link" - android:icon="@drawable/ic_link" - android:title="@string/menu_open_link" - app:showAsAction="never"/> + android:id="@+id/action_open_link" + android:icon="@drawable/ic_link" + android:title="@string/menu_open_link" + app:showAsAction="never" /> <item - android:id="@+id/action_is_update" - android:title="@string/menu_is_update" - android:icon="@drawable/ic_enable" - android:checkable="true" - android:checked="true" - 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" /> <item - android:id="@+id/action_group_setting" - android:title="@string/menu_group_setting" - android:icon="@drawable/ic_group" - app:showAsAction="never" /> + android:id="@+id/action_group_setting" + android:icon="@drawable/ic_group" + android:title="@string/menu_group_setting" + app:showAsAction="never" /> </menu> 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 @@ <?xml version ="1.0" encoding ="utf-8"?> -<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> <item android:id="@+id/action_share" android:icon="@drawable/ic_share" android:title="@string/menu_share" - app:showAsAction="ifRoom"/> - + app:showAsAction="ifRoom" /> + <item + android:id="@+id/action_edit" + android:icon="@drawable/ic_edit" + android:title="@string/menu_edit" + app:showAsAction="ifRoom" /> <item - android:id="@+id/action_group_setting" - android:title="@string/menu_group_setting" - android:icon="@drawable/ic_group" - app:showAsAction="never" /> + android:id="@+id/action_group_setting" + android:icon="@drawable/ic_group" + android:title="@string/menu_group_setting" + app:showAsAction="never" /> </menu> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/action_save" + app:showAsAction="always" + android:icon="@drawable/ic_save" + android:title="@string/save" /> +</menu> \ 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 @@ <color name="toast_yellow">#FFFFE284</color> <color name="toast_red">#FFE75B76</color> - + <!-- updateDialog --> + <color name="gray_cfcfcf">#cfcfcf</color> + <color name="orange_ffc032">#ffc032</color> + <color name="black_3A3D4E">#3a3d4e</color> + <color name="black_475B80">#475b80</color> + <color name="end_fffb8435">#fffb8435</color> + <color name="start_fffe795f">#fffe795f</color> </resources> 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 @@ <dimen name="tab_home_text_size">16sp</dimen> - + <dimen name="_10dp">10dp</dimen> + <dimen name="_15dp">15dp</dimen> </resources> 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 @@ <string name="parser">解析器</string> <string name="all_source">所有书源</string> <string name="local_source">内置书源</string> + <string name="ad_setting">广告设置</string> + <string name="splash_ad">开屏广告</string> + <string name="splash_ad_times">每日广告显示次数</string> + <string name="splash_cur_ad_times">当前次数:%s,今日已显示次数:%s</string> + <string name="ad">广告总开关</string> + <string name="book_detail_ad">详情页广告</string> + <string name="book_detail_ad_sc">详情页广告开关</string> + <string name="support_author">支持作者</string> + <string name="donate_author">捐赠作者</string> + <string name="wx_zsm">微信赞赏码</string> + <string name="donate_tip">点击打开</string> + <string name="zfb_skm">支付宝收款码</string> + <string name="qq_skm">QQ收款码</string> + <string name="ad_support">广告支持作者</string> + <string name="ad_support_tip">点击下列广告即可支持作者,建议在WIFI环境下点击</string> + <string name="flow_ad_tip">信息流广告(下载的文件可在广告设置中清除)</string> + <string name="rewarded_video">激励视频</string> + <string name="ad_file">广告下载/缓存</string> + <string name="delete_ad_file">清除广告文件</string> + <string name="cover_path">封面地址</string> + <string name="book_desc">书籍简介</string> + <string name="author">作者</string> + <string name="book_name">书名</string> + <string name="select_local_pic">选择本地图片</string> + <string name="menu_edit">编辑书籍</string> <string-array name="reset_screen_time"> + <item>跟随系统</item> <item>常亮</item> <item>1分钟</item> <item>3分钟</item> 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 @@ </style> - // <declare-styleable name="CodeView"> <attr name="cv_font_size" format="integer"/> <attr name="cv_zoom_enable" format="boolean"/> </declare-styleable> + + <!-- updateDialog --> + <declare-styleable name="BarPercentView"> + <!--进度条高度--> + <attr name="barHeight" format="dimension" /> + <!--圆角矩形半径--> + <attr name="barRadius" format="dimension" /> + <!--进度条背景色--> + <attr name="barBgColor" format="reference" /> + <!--进度条颜色--> + <attr name="barProgressColor" format="reference" /> + <!--渐变开始颜色--> + <attr name="barStartColor" format="reference" /> + <!--渐变结束颜色--> + <attr name="barEndColor" format="reference" /> + <!--是否渐变--> + <attr name="barIsGradient" format="boolean" /> + </declare-styleable> + + <!-- loadingDialog --> + <style name="LoadingDialog" parent="@android:style/Theme.Holo.Dialog.NoActionBar"> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:backgroundDimEnabled">false</item> + </style> </resources> 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<String> 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<String> getStringList(String rule, Object obj, boolean first) { + return null; + } + }; + //List<String> 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 @@ - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml"> -<head> - <meta http-equiv="Cache-Control" content="no-siteapp" /> - <meta http-equiv="Cache-Control" content="no-transform" /> - <script type="text/javascript" src="/m.js"></script> - <title>޴½IVռ½-޴½IVռ޵ȫĶ- - - - - - - - - - - - - - - - - - - - - - -
- -
- - -
- -
-
- - > С˵ > ޴½IVռ½б -
-
-
-

޴½IVռ

-

    ߣƼ

-

״    ̬

-

¸£һǧپʮ

-

£2021-02-14 17:29

-
-
-

һ󣬱ˡ - ƿڼ֮ؿƿʱһŽ˫ɫƵĵ֮̽󣬷ȻæозˣɷȴһӤһģһӤһĺӡ -

-

λҪǾá޴½IVռޡĻ벻ҪQQȺ΢ƼŶ

-
-
- - -
-
-
-
-
- -
һ ʲô
-
ڶ ⶳ
-
ѵǣ
-
ĺ
-
ֻǸͨĺӣ
-
޺ۼ
-
-
ڰ
-
ھ
-
ʮ ֻҪ
-
ʮһ
-
ʮ ؽָ
-
ʮ ͬʱֵ껷
-
ʮ Ļ
-
ʮ Ҷͫ
-
ʮ
-
ʮ ѧϰ
-
ʮ Ҷ֮ͫ
-
ʮ ĩ
-
ڶʮ ʧЧĻ꼼
-
ڶʮһ Ҷ
-
ڶʮ ˮɱ
-
ڶʮ ʳ
-
ڶʮ
-
ڶʮ ɴ
-
ڶʮ
-
ڶʮ ֳ
-
ڶʮ ֺ
-
ڶʮ Ⱥ
-
ʮ ʶĸо
-
ʮһ ְ
-
ʮ
-
ʮ ҿʦ
-
ʮ Ϸ
-
ʮ ʧ
-
ʮ Ϸ
-
ʮ ͥʦ
-
ʮ ʦ
-
ʮ Σ
-
ʮ
-
ʮһ ָ
-
ʮ
-
ʮ ս
-
ʮ սҶͫ
-
ʮ
-
ʮ ɳ
-
ʮ
-
ʮ ֹӵġ
-
ʮ Ĵ
-
ʮ Ȧ
-
ʮһ ҹ
-
ʮ 裬е㺦
-
ʮ
-
ʮ
-
ʮ Ӧ̻
-
ʮ Ҫ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 即可的到正确排序的列表 + ### 三、书源编辑