From c480adbf8f47abf9d45a4808c83811c2ce0811ef Mon Sep 17 00:00:00 2001
From: fengyuecanzhu <1021300691@qq.com>
Date: Sat, 4 Dec 2021 13:44:52 +0800
Subject: [PATCH] fix bug
---
.idea/assetWizardSettings.xml | 4 +-
.idea/inspectionProfiles/Project_Default.xml | 36 +++++
.idea/misc.xml | 1 +
app/release.md | 5 +-
app/src/main/assets/updatelog.fy | 6 +
.../myreader/experiment/BookWCEstimate.kt | 90 ++++++++++++
.../fycz/myreader/experiment/DoubleUtils.java | 91 +++++++++++++
.../myreader/experiment/LinearRegression.java | 128 ++++++++++++++++++
.../xyz/fycz/myreader/experiment/README.md | 2 +
.../ui/activity/BookDetailedActivity.java | 61 ++++++++-
.../ui/fragment/DIYSourceFragment.java | 9 +-
.../fycz/myreader/util/help/JsExtensions.java | 2 +
.../fycz/myreader/util/utils/FileUtils.java | 2 +-
app/src/main/res/drawable/ic_wc.xml | 12 ++
app/src/main/res/menu/menu_book_detail.xml | 6 +
.../main/res/menu/menu_book_detail_local.xml | 5 +
app/src/main/res/values/strings.xml | 1 +
app/version_code.properties | 4 +-
18 files changed, 453 insertions(+), 12 deletions(-)
create mode 100644 .idea/inspectionProfiles/Project_Default.xml
create mode 100644 app/src/main/java/xyz/fycz/myreader/experiment/BookWCEstimate.kt
create mode 100644 app/src/main/java/xyz/fycz/myreader/experiment/DoubleUtils.java
create mode 100644 app/src/main/java/xyz/fycz/myreader/experiment/LinearRegression.java
create mode 100644 app/src/main/java/xyz/fycz/myreader/experiment/README.md
create mode 100644 app/src/main/res/drawable/ic_wc.xml
diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml
index 3676718..c830d3a 100644
--- a/.idea/assetWizardSettings.xml
+++ b/.idea/assetWizardSettings.xml
@@ -14,8 +14,8 @@
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..6560a98
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 80b0858..3b7db2b 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -25,6 +25,7 @@
+
diff --git a/app/release.md b/app/release.md
index 90dc9f6..523d408 100644
--- a/app/release.md
+++ b/app/release.md
@@ -1,3 +1,2 @@
-* 1、修复切换书源对话框内存泄漏的问题
-* 2、优化导入书源选择
-* 3、修复已知bug
\ No newline at end of file
+* 1、修复从[文档或最近]导入书源时文件读取失败的问题
+* 2、新增实验性功能:书籍字数计算(书籍详情页菜单)
\ No newline at end of file
diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy
index d9ce488..f08ddd9 100644
--- a/app/src/main/assets/updatelog.fy
+++ b/app/src/main/assets/updatelog.fy
@@ -1,3 +1,9 @@
+2021.12.04
+风月读书v2.2.3
+更新内容:
+1、修复从[文档或最近]导入书源时文件读取失败的问题
+2、新增实验性功能:书籍字数计算(书籍详情页菜单)
+
2021.11.12
风月读书v2.2.2
更新内容:
diff --git a/app/src/main/java/xyz/fycz/myreader/experiment/BookWCEstimate.kt b/app/src/main/java/xyz/fycz/myreader/experiment/BookWCEstimate.kt
new file mode 100644
index 0000000..f26f96b
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/experiment/BookWCEstimate.kt
@@ -0,0 +1,90 @@
+package xyz.fycz.myreader.experiment
+
+import xyz.fycz.myreader.common.APPCONST
+import xyz.fycz.myreader.greendao.entity.Book
+import xyz.fycz.myreader.greendao.service.ChapterService
+import xyz.fycz.myreader.util.IOUtils
+import xyz.fycz.myreader.util.utils.FileUtils
+import java.io.BufferedReader
+import java.io.File
+import java.io.FileReader
+import java.io.IOException
+
+/**
+ * @author fengyue
+ * @date 2021/12/4 10:33
+ */
+class BookWCEstimate {
+ /**
+ * -1:本地文件不存在
+ * -2:书籍章节缓存数量少于20
+ */
+ fun getWordCount(book: Book): Int {
+ if (book.type == "本地书籍") {
+ return getLocalWordCount(book)
+ } else if (book.type.isNotEmpty()) {
+ return getNetWordCount(book)
+ }
+ return 0
+ }
+
+ private fun getLocalWordCount(book: Book): Int {
+ val file = File(book.chapterUrl)
+ if (!file.exists()) return -1
+ return -countChar(file)
+ }
+
+
+ /**
+ * 计算方式:使用最小二乘法进行线性回归预测
+ */
+ private fun getNetWordCount(book: Book): Int {
+ val chapterService = ChapterService.getInstance()
+ val chapters = chapterService.findBookAllChapterByBookId(book.id)
+ val map = mutableMapOf()
+ var sum = 0.0
+ var i = 0
+ var cachedChapterSize = 0
+ for (chapter in chapters) {
+ if (cachedChapterSize >= 20) break
+ if (ChapterService.isChapterCached(chapter.bookId, chapter.title)) {
+ cachedChapterSize++
+ }
+ }
+ if (cachedChapterSize < 20 && chapters.size > 50) return -2
+ chapters.forEach { chapter ->
+ if (ChapterService.isChapterCached(chapter.bookId, chapter.title)) {
+ sum += countChar(
+ File(
+ APPCONST.BOOK_CACHE_PATH + chapter.bookId
+ + File.separator + chapter.title + FileUtils.SUFFIX_FY
+ )
+ )
+ map[i++.toDouble()] = sum
+ }
+ }
+ if (map.size == chapters.size) return sum.toInt()
+ val lr = LinearRegression(map)
+ return lr.getY(chapters.size.toDouble()).toInt()
+ }
+
+ private fun countChar(file: File): Int { //统计字符数
+ var charnum = 0 //字符数
+ var x: Int
+ var fReader: FileReader? = null
+ try {
+ fReader = FileReader(file)
+ while ((fReader.read().also { x = it }) != -1) { //按字符读文件,判断,符合则字符加一
+ val a = x.toChar()
+ if (a != '\n' && a != '\r') {
+ charnum++
+ }
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ } finally {
+ IOUtils.close(fReader)
+ }
+ return charnum //返回结果
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/fycz/myreader/experiment/DoubleUtils.java b/app/src/main/java/xyz/fycz/myreader/experiment/DoubleUtils.java
new file mode 100644
index 0000000..119e4e6
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/experiment/DoubleUtils.java
@@ -0,0 +1,91 @@
+package xyz.fycz.myreader.experiment;
+
+import java.math.BigDecimal;
+
+/**
+ * @author fengyue
+ * @date 2021/12/4 12:16
+ */
+public class DoubleUtils {
+ private static final int DEF_DIV_SCALE = 10;
+
+ /**
+ * * 两个Double数相加 *
+ *
+ * @param v1 *
+ * @param v2 *
+ * @return Double
+ */
+ public static Double add(Double v1, Double v2) {
+ BigDecimal b1 = new BigDecimal(v1.toString());
+ BigDecimal b2 = new BigDecimal(v2.toString());
+ return b1.add(b2).doubleValue();
+ }
+
+ /**
+ * * 两个Double数相减 *
+ *
+ * @param v1 *
+ * @param v2 *
+ * @return Double
+ */
+ public static Double sub(Double v1, Double v2) {
+ BigDecimal b1 = new BigDecimal(v1.toString());
+ BigDecimal b2 = new BigDecimal(v2.toString());
+ return b1.subtract(b2).doubleValue();
+ }
+
+ /**
+ * * 两个Double数相乘 *
+ *
+ * @param v1 *
+ * @param v2 *
+ * @return Double
+ */
+ public static Double mul(Double v1, Double v2) {
+ BigDecimal b1 = new BigDecimal(v1.toString());
+ BigDecimal b2 = new BigDecimal(v2.toString());
+ return b1.multiply(b2).doubleValue();
+ }
+
+ /**
+ * * 两个Double数相除 *
+ *
+ * @param v1 *
+ * @param v2 *
+ * @return Double
+ */
+ public static Double div(Double v1, Double v2) {
+ BigDecimal b1 = new BigDecimal(v1.toString());
+ BigDecimal b2 = new BigDecimal(v2.toString());
+ return b1.divide(b2, DEF_DIV_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
+ }
+
+ /**
+ * * 两个Double数相除,并保留scale位小数 *
+ *
+ * @param v1 *
+ * @param v2 *
+ * @param scale *
+ * @return Double
+ */
+ public static Double div(Double v1, Double v2, int scale) {
+ if (scale < 0) {
+ throw new IllegalArgumentException(
+ "The scale must be a positive integer or zero");
+ }
+ BigDecimal b1 = new BigDecimal(v1.toString());
+ BigDecimal b2 = new BigDecimal(v2.toString());
+ return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
+ }
+
+
+ public static int max(int a, int b) {
+ return Math.max(a, b);
+ }
+
+
+ public static int min(int a, int b) {
+ return Math.min(a, b);
+ }
+}
diff --git a/app/src/main/java/xyz/fycz/myreader/experiment/LinearRegression.java b/app/src/main/java/xyz/fycz/myreader/experiment/LinearRegression.java
new file mode 100644
index 0000000..cf7d09e
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/experiment/LinearRegression.java
@@ -0,0 +1,128 @@
+package xyz.fycz.myreader.experiment;
+
+import static xyz.fycz.myreader.experiment.DoubleUtils.add;
+import static xyz.fycz.myreader.experiment.DoubleUtils.div;
+import static xyz.fycz.myreader.experiment.DoubleUtils.mul;
+import static xyz.fycz.myreader.experiment.DoubleUtils.sub;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 使用最小二乘法实现线性回归预测
+ *
+ * @author fengyue
+ * @date 2021/12/4 12:15
+ */
+public class LinearRegression {
+
+ /**
+ * 训练集数据
+ */
+ private Map initData = new HashMap<>();
+
+ /**
+ * 截距
+ */
+ private double intercept = 0.0;
+ //斜率
+ private double slope = 0.0;
+
+ /**
+ * x、y平均值
+ */
+ private double averageX, averageY;
+ /**
+ * 求斜率的上下两个分式的值
+ */
+ private double slopeUp, slopeDown;
+
+
+ public LinearRegression(Map initData) {
+ this.initData = initData;
+ initData();
+ }
+
+ public LinearRegression() {
+ }
+
+ /**
+ * 根据训练集数据进行训练预测
+ * 并计算斜率和截距
+ */
+ public void initData() {
+ if (initData.size() > 0) {
+ //数据个数
+ int number = 0;
+ //x值、y值总和
+ double sumX = 0;
+ double sumY = 0;
+ averageX = 0;
+ averageY = 0;
+ slopeUp = 0;
+ slopeDown = 0;
+ for (Double x : initData.keySet()) {
+ if (x == null || initData.get(x) == null) {
+ continue;
+ }
+ number++;
+ sumX = add(sumX, x);
+ sumY = add(sumY, initData.get(x));
+ }
+ //求x,y平均值
+ averageX = div(sumX, (double) number);
+ averageY = div(sumY, (double) number);
+
+ for (Double x : initData.keySet()) {
+ if (x == null || initData.get(x) == null) {
+ continue;
+ }
+
+ slopeUp = add(slopeUp, mul(sub(x, averageX), sub(initData.get(x), averageY)));
+ slopeDown = add(slopeDown, mul(sub(x, averageX), sub(x, averageX)));
+
+ }
+ initSlopeIntercept();
+ }
+ }
+
+ /**
+ * 计算斜率和截距
+ */
+ private void initSlopeIntercept() {
+ if (slopeUp != 0 && slopeDown != 0) {
+ slope = slopeUp / slopeDown;
+ }
+ intercept = averageY - averageX * slope;
+ }
+
+ /**
+ * 根据x值预测y值
+ *
+ * @param x x值
+ * @return y值
+ */
+ public Double getY(Double x) {
+ return add(intercept, mul(slope, x));
+ }
+
+ /**
+ * 根据y值预测x值
+ *
+ * @param y y值
+ * @return x值
+ */
+ public Double getX(Double y) {
+ return div(sub(y, intercept), slope);
+ }
+
+
+ public Map getInitData() {
+ return initData;
+ }
+
+ public void setInitData(Map initData) {
+ this.initData = initData;
+ }
+
+}
diff --git a/app/src/main/java/xyz/fycz/myreader/experiment/README.md b/app/src/main/java/xyz/fycz/myreader/experiment/README.md
new file mode 100644
index 0000000..0b53387
--- /dev/null
+++ b/app/src/main/java/xyz/fycz/myreader/experiment/README.md
@@ -0,0 +1,2 @@
+* 实验性功能
+ * 回归模型预测小说字数
\ No newline at end of file
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 392f3ea..5b43bb3 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
@@ -13,7 +13,6 @@ import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -28,6 +27,8 @@ import java.util.List;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import xyz.fycz.myreader.R;
@@ -36,8 +37,10 @@ import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.base.BitIntentDataManager;
import xyz.fycz.myreader.base.observer.MyObserver;
+import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.databinding.ActivityBookDetailBinding;
+import xyz.fycz.myreader.experiment.BookWCEstimate;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
@@ -48,6 +51,7 @@ import xyz.fycz.myreader.ui.adapter.BookTagAdapter;
import xyz.fycz.myreader.ui.adapter.DetailCatalogAdapter;
import xyz.fycz.myreader.ui.dialog.BookGroupDialog;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
+import xyz.fycz.myreader.ui.dialog.LoadingDialog;
import xyz.fycz.myreader.ui.dialog.SourceExchangeDialog;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.help.StringHelper;
@@ -83,6 +87,7 @@ public class BookDetailedActivity extends BaseActivity {
private BookGroupDialog mBookGroupDia;
private List tagList = new ArrayList<>();
private Disposable chaptersDis;
+ private Disposable wcDis;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@@ -588,6 +593,7 @@ public class BookDetailedActivity extends BaseActivity {
* @param item
* @return
*/
+ @SuppressLint("NonConstantResourceId")
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@@ -631,12 +637,65 @@ public class BookDetailedActivity extends BaseActivity {
startActivity(sourceIntent);
}
break;
+ case R.id.action_word_count:
+ getWordCount();
+ break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
+ private void getWordCount() {
+
+ LoadingDialog dialog = new LoadingDialog(this, "正在计算", () -> {
+ if (wcDis != null) {
+ wcDis.dispose();
+ }
+ });
+ dialog.show();
+ Single.create((SingleOnSubscribe) emitter -> {
+ BookWCEstimate bookWCEstimate = new BookWCEstimate();
+ emitter.onSuccess(bookWCEstimate.getWordCount(mBook));
+ }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() {
+ @Override
+ public void onSubscribe(Disposable d) {
+ addDisposable(d);
+ wcDis = d;
+ }
+
+ @Override
+ public void onSuccess(@NonNull Integer count) {
+ dialog.dismiss();
+ if (count == -1) {
+ ToastUtils.showWarring("书籍源文件不存在,无法计算字数");
+ } else if (count == -2) {
+ ToastUtils.showWarring("已缓存章节数量不足20,无法计算字数");
+ } else {
+ String tip = "\n";
+ if (count > 0) {
+ tip += "注:当前为网络书籍,无法获取准确字数,软件采用简单线性回归模型进行字数预测,仅供参考";
+ } else {
+ tip += "注:当前为本地书籍,字数统计自书籍源文件";
+ }
+ count = count < 0 ? -count : count;
+ String countTag = count > 100000 ? count / 10000 + "万" : count + "";
+ mBook.setWordCount(countTag);
+ initTagList();
+ mBookService.updateEntity(mBook);
+ DialogCreator.createTipDialog(BookDetailedActivity.this, "书籍字数(实验性功能)",
+ "书籍字数计算成功,本书字数:" + count + tip);
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ ToastUtils.showError("书籍字数计算失败!\n" + e.getLocalizedMessage());
+ dialog.dismiss();
+ }
+ });
+ }
+
/**
* 阅读/章节界面反馈结果处理
diff --git a/app/src/main/java/xyz/fycz/myreader/ui/fragment/DIYSourceFragment.java b/app/src/main/java/xyz/fycz/myreader/ui/fragment/DIYSourceFragment.java
index 52895bc..613a3d7 100644
--- a/app/src/main/java/xyz/fycz/myreader/ui/fragment/DIYSourceFragment.java
+++ b/app/src/main/java/xyz/fycz/myreader/ui/fragment/DIYSourceFragment.java
@@ -25,6 +25,8 @@ import com.hjq.permissions.XXPermissions;
import com.kongzue.dialogx.dialogs.BottomMenu;
import java.io.File;
+import java.io.FileInputStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -57,6 +59,7 @@ import xyz.fycz.myreader.util.ShareUtils;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.UriFileUtil;
import xyz.fycz.myreader.util.utils.ClipBoardUtil;
+import xyz.fycz.myreader.util.utils.DocumentUtil;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.GsonExtensionsKt;
import xyz.fycz.myreader.util.utils.RxUtils;
@@ -320,9 +323,9 @@ public class DIYSourceFragment extends BaseFragment {
});
dialog.show();
Single.create((SingleOnSubscribe) emitter -> {
- String path = UriFileUtil.getChooseFileResultPath(getContext(), data.getData());
- Log.d(TAG, "filePath:" + path);
- emitter.onSuccess(path);
+// String json = FileUtils.readInStream(DocumentUtil.getFileInputSteam(getContext(), data.getData()));
+ String json = new String(DocumentUtil.readBytes(getContext(), data.getData()), StandardCharsets.UTF_8);
+ emitter.onSuccess(json);
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() {
@Override
public void onSubscribe(Disposable d) {
diff --git a/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java b/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java
index 7b8a674..0e20b09 100644
--- a/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java
+++ b/app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java
@@ -35,6 +35,8 @@ import xyz.fycz.myreader.util.utils.StringUtils;
@Keep
@SuppressWarnings({"unused"})
+
+
public interface JsExtensions {
String TAG = JsExtensions.class.getSimpleName();
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 2375a7a..f452f77 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
@@ -364,7 +364,7 @@ public class FileUtils {
}
}
- private static String readInStream(FileInputStream inStream) {
+ public static String readInStream(InputStream inStream) {
ByteArrayOutputStream outStream = null;
try {
outStream = new ByteArrayOutputStream();
diff --git a/app/src/main/res/drawable/ic_wc.xml b/app/src/main/res/drawable/ic_wc.xml
new file mode 100644
index 0000000..bfdb0ef
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wc.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/menu/menu_book_detail.xml b/app/src/main/res/menu/menu_book_detail.xml
index 2d14ee5..4d2e90d 100644
--- a/app/src/main/res/menu/menu_book_detail.xml
+++ b/app/src/main/res/menu/menu_book_detail.xml
@@ -49,4 +49,10 @@
android:icon="@drawable/ic_source"
android:title="@string/book_source"
app:showAsAction="ifRoom"/>
+
+
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 870c202..1f7c136 100644
--- a/app/src/main/res/menu/menu_book_detail_local.xml
+++ b/app/src/main/res/menu/menu_book_detail_local.xml
@@ -19,4 +19,9 @@
android:icon="@drawable/ic_group"
android:title="@string/menu_group_setting"
app:showAsAction="never" />
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9dc94e8..15c593b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -510,6 +510,7 @@
管理书籍分组
书籍分组
在书架内显示书籍分组
+ 计算字数(实验)
diff --git a/app/version_code.properties b/app/version_code.properties
index fda2c7d..de8858f 100644
--- a/app/version_code.properties
+++ b/app/version_code.properties
@@ -1,3 +1,3 @@
#Fri Jun 18 21:45:31 CST 2021
-VERSION_CODE=222
-NEED_CREATE_RELEASE=true
+VERSION_CODE=223
+NEED_CREATE_RELEASE=false