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