修复bug、优化体验

pull/5/head
fengyuecanzhu 4 years ago
parent c8c36c1de4
commit 423cfcb897
  1. 8
      .idea/.gitignore
  2. 9
      .idea/assetWizardSettings.xml
  3. BIN
      .idea/caches/build_file_checksums.ser
  4. 116
      .idea/codeStyles/Project.xml
  5. 11
      .idea/dataSources.xml
  6. 6
      .idea/encodings.xml
  7. 1
      .idea/gradle.xml
  8. 36
      .idea/inspectionProfiles/Project_Default.xml
  9. 30
      .idea/jarRepositories.xml
  10. 10
      .idea/markdown-navigator-enh.xml
  11. 62
      .idea/markdown-navigator.xml
  12. 55
      .idea/misc.xml
  13. 2
      .idea/modules.xml
  14. 55
      .idea/navEditor.xml
  15. 12
      .idea/runConfigurations.xml
  16. 7
      .idea/sqldialects.xml
  17. 124
      .idea/uiDesigner.xml
  18. 2
      .idea/vcs.xml
  19. 14
      app/build.gradle
  20. 18
      app/debug/output-metadata.json
  21. BIN
      app/debug/风月读书v1.9.3.apk
  22. BIN
      app/libs/ddsdk_4.1.6_custom_2021-04-09_0948.aar
  23. BIN
      app/libs/mintegral_appwall.aar
  24. BIN
      app/libs/mintegral_appwallext.aar
  25. BIN
      app/libs/mintegral_chinacommon.aar
  26. BIN
      app/libs/mintegral_interactiveads.aar
  27. BIN
      app/libs/mintegral_interstitial.aar
  28. BIN
      app/libs/mintegral_interstitialvideo.aar
  29. BIN
      app/libs/mintegral_mtgbanner.aar
  30. BIN
      app/libs/mintegral_mtgbid.aar
  31. BIN
      app/libs/mintegral_mtgdownloads.aar
  32. BIN
      app/libs/mintegral_mtgjscommon.aar
  33. BIN
      app/libs/mintegral_mtgnative.aar
  34. BIN
      app/libs/mintegral_mtgnativeadvanced.aar
  35. BIN
      app/libs/mintegral_mtgsplash.aar
  36. BIN
      app/libs/mintegral_nativeex.aar
  37. BIN
      app/libs/mintegral_playercommon.aar
  38. BIN
      app/libs/mintegral_reward.aar
  39. BIN
      app/libs/mintegral_videocommon.aar
  40. BIN
      app/libs/mintegral_videojs.aar
  41. BIN
      app/libs/windAd-2.25.2.aar
  42. 18
      app/release/output-metadata.json
  43. 149
      app/src/main/AndroidManifest.xml
  44. 15
      app/src/main/assets/updatelog.fy
  45. 182
      app/src/main/java/xyz/fycz/myreader/ai/BookWordCountPre.java
  46. 295
      app/src/main/java/xyz/fycz/myreader/ai/MatrixUtil.java
  47. 100
      app/src/main/java/xyz/fycz/myreader/application/App.java
  48. 63
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
  49. 4
      app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java
  50. 2
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  51. 11
      app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
  52. 12
      app/src/main/java/xyz/fycz/myreader/entity/sourceedit/EditEntityUtil.kt
  53. 21
      app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
  54. 2
      app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java
  55. 74
      app/src/main/java/xyz/fycz/myreader/model/source/BaseAnalyzer.java
  56. 14
      app/src/main/java/xyz/fycz/myreader/model/source/JsonPathAnalyzer.java
  57. 18
      app/src/main/java/xyz/fycz/myreader/model/source/MatcherAnalyzer.java
  58. 35
      app/src/main/java/xyz/fycz/myreader/model/source/XpathAnalyzer.java
  59. 2
      app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java
  60. 171
      app/src/main/java/xyz/fycz/myreader/ui/activity/AdSettingActivity.java
  61. 73
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  62. 162
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookInfoEditActivity.java
  63. 134
      app/src/main/java/xyz/fycz/myreader/ui/activity/DonateActivity.java
  64. 5
      app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
  65. 81
      app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java
  66. 13
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  67. 31
      app/src/main/java/xyz/fycz/myreader/ui/activity/SourceDebugActivity.java
  68. 5
      app/src/main/java/xyz/fycz/myreader/ui/activity/SourceEditActivity.java
  69. 187
      app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java
  70. 122
      app/src/main/java/xyz/fycz/myreader/ui/dialog/LoadingDialog.java
  71. 396
      app/src/main/java/xyz/fycz/myreader/ui/dialog/UpdateDialog.java
  72. 6
      app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java
  73. 2
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  74. 4
      app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java
  75. 2
      app/src/main/java/xyz/fycz/myreader/util/SharedPreUtils.java
  76. 133
      app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java
  77. 16
      app/src/main/java/xyz/fycz/myreader/util/utils/FileUtils.java
  78. 1
      app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java
  79. 10
      app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
  80. 19
      app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java
  81. 12
      app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java
  82. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Ben100ReadCrawler.java
  83. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChuanQiReadCrawler.java
  84. 1
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/HongChenReadCrawler.java
  85. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/JiuTaoReadCrawler.java
  86. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LaoYaoReadCrawler.java
  87. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiQuReadCrawler.java
  88. 126
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QianDianReadCrawler.java
  89. 4
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ShuHaiGeReadCrawler.java
  90. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/TianLaiReadCrawler.java
  91. 1
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XiaGuReadCrawler.java
  92. 1
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XingXingReadCrawler.java
  93. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/YanQingLouReadCrawler.java
  94. 162
      app/src/main/java/xyz/fycz/myreader/widget/BarPercentView.java
  95. 10
      app/src/main/java/xyz/fycz/myreader/widget/CoverImageView.kt
  96. 12
      app/src/main/res/anim/push_bottom_in.xml
  97. 11
      app/src/main/res/anim/push_bottom_out.xml
  98. 6
      app/src/main/res/drawable/bg_loading_dialog.xml
  99. 9
      app/src/main/res/drawable/ic___.xml
  100. 15
      app/src/main/res/drawable/ic_ad.xml
  101. Some files were not shown because too many files have changed in this diff Show More

8
.idea/.gitignore vendored

@ -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/

@ -3,11 +3,6 @@
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState />
</value>
</entry>
<entry key="vectorWizard">
<value>
<PersistentState>
@ -19,8 +14,8 @@
<option name="values">
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="outputName" value="ic_yun" />
<entry key="sourceFile" value="F:\SVG图标\.svg" />
<entry key="outputName" value="ic_edit" />
<entry key="sourceFile" value="F:\SVG图标\编辑.svg" />
</map>
</option>
</PersistentState>

@ -1,116 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="fyreader" uuid="d7778050-1d7b-46c3-8f3e-6a61ef69ce07">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://47.105.152.62:3306/fyreader</jdbc-url>
</data-source>
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/release/output.json" charset="GBK" />
</component>
</project>

@ -7,6 +7,7 @@
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="1.8" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

@ -1,36 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
<option name="TOP_LEVEL_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="INNER_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="METHOD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
</value>
</option>
<option name="FIELD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="IGNORE_DEPRECATED" value="false" />
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
<option name="myAdditionalJavadocTags" value="date,r(oldStr," />
</inspection_tool>
</profile>
</component>

@ -26,35 +26,5 @@
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="E:\Java\AndroidSdk\extras\google\m2repository" />
<option name="name" value="E:\Java\AndroidSdk\extras\google\m2repository" />
<option name="url" value="file:/$PROJECT_DIR$/../AndroidSdk/extras/google/m2repository" />
</remote-repository>
<remote-repository>
<option name="id" value="E:\Java\AndroidSdk\extras\m2repository" />
<option name="name" value="E:\Java\AndroidSdk\extras\m2repository" />
<option name="url" value="file:/$PROJECT_DIR$/../AndroidSdk/extras/m2repository" />
</remote-repository>
<remote-repository>
<option name="id" value="E:\Java\AndroidSdk\extras\android\m2repository" />
<option name="name" value="E:\Java\AndroidSdk\extras\android\m2repository" />
<option name="url" value="file:/$PROJECT_DIR$/../AndroidSdk/extras/android/m2repository" />
</remote-repository>
<remote-repository>
<option name="id" value="E:\Java\AndroidSdk\extras\google\m2repository" />
<option name="name" value="E:\Java\AndroidSdk\extras\google\m2repository" />
<option name="url" value="file:/$PROJECT_DIR$/../../AndroidSdk/extras/google/m2repository" />
</remote-repository>
<remote-repository>
<option name="id" value="E:\Java\AndroidSdk\extras\m2repository" />
<option name="name" value="E:\Java\AndroidSdk\extras\m2repository" />
<option name="url" value="file:/$PROJECT_DIR$/../../AndroidSdk/extras/m2repository" />
</remote-repository>
<remote-repository>
<option name="id" value="E:\Java\AndroidSdk\extras\android\m2repository" />
<option name="name" value="E:\Java\AndroidSdk\extras\android\m2repository" />
<option name="url" value="file:/$PROJECT_DIR$/../../AndroidSdk/extras/android/m2repository" />
</remote-repository>
</component>
</project>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownEnhProjectSettings">
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" />
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imagePathType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.md.nav.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" correctedInvalidSettings="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ATXHEADERSPACE" value="true" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="INTELLIJ_DUMMY_IDENTIFIER" value="true" />
<option name="RELAXEDHRULES" value="true" />
<option name="STRIKETHROUGH" value="true" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="true" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="PRODUCTION_SPEC_PARSER" value="true" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" addPageHeader="false" addAnchorLinks="false" anchorLinksWrapText="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" defaultUrlTitle="false" migratedPlantUml="true" migratedAnchorLinks="true" plantUmlConversion="0">
<GeneratorProvider>
<provider providerId="com.vladsch.md.nav.editor.text.html.generator" providerName="Unmodified HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
<fencedCodeConversions>
<option name="c4plantuml" value="NONE" />
<option name="ditaa" value="NONE" />
<option name="erd" value="NONE" />
<option name="graphviz" value="NONE" />
<option name="latex" value="KATEX" />
<option name="math" value="KATEX" />
<option name="mermaid" value="NONE" />
<option name="nomnoml" value="NONE" />
<option name="plantuml" value="NONE" />
<option name="puml" value="NONE" />
<option name="svgbob" value="NONE" />
<option name="umlet" value="NONE" />
<option name="vega" value="NONE" />
<option name="vegalite" value="NONE" />
<option name="wavedrom" value="NONE" />
</fencedCodeConversions>
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.md.nav.editor.text.html.css" providerName="No Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
</component>
</project>

@ -1,57 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="io.reactivex.annotations.NonNull" />
<option name="myNullables">
<value>
<list size="15">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
<item index="11" class="java.lang.String" itemvalue="android.annotation.Nullable" />
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
<item index="14" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="14">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="9" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
<item index="10" class="java.lang.String" itemvalue="android.annotation.NonNull" />
<item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="Android API 29 Platform" project-jdk-type="Android SDK" />
</project>

@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/FYReader-master.iml" filepath="$PROJECT_DIR$/.idea/modules/FYReader-master.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/FYReader-master.iml" filepath="$PROJECT_DIR$/.idea/FYReader-master.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/FYReader-master.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/FYReader-master.app.iml" />
</modules>
</component>

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="navEditor-manualLayoutAlgorithm2">
<option name="myPositions">
<map>
<entry key="mobile_navigation.xml">
<value>
<LayoutPositions>
<option name="myPositions">
<map>
<entry key="navigation_dashboard">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="256" />
<option name="y" value="12" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="navigation_home">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="12" />
<option name="y" value="12" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
<entry key="navigation_notifications">
<value>
<LayoutPositions>
<option name="myPosition">
<Point>
<option name="x" value="12" />
<option name="y" value="368" />
</Point>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</LayoutPositions>
</value>
</entry>
</map>
</option>
</component>
</project>

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java" dialect="GenericSQL" />
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>

@ -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 {

@ -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"
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -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"
}
]
}

@ -1,118 +1,145 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="xyz.fycz.myreader">
xmlns:tools="http://schemas.android.com/tools"
package="xyz.fycz.myreader">
<!-- 删除权限 -->
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
tools:node="remove" />
<uses-permission
android:name="android.permission.GET_TASKS"
tools:node="remove" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"
tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
tools:node="remove" />
<!-- ********************************permission************************************ -->
<!-- 读取手机状态和身份 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!-- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>-->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.example.broadcast.permission"/>
<uses-permission android:name="com.example.broadcast.permission" />
<!-- 拥有完全的网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET" />
<!-- 查看网络连接 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 查看WLAN连接 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 修改或删除您的USB存储设备中的内容 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEM"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEM" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 读取您的USB存储设备中的内容 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 开机启动 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.Manifest.permission.INTERNAL_SYSTEM_WINDOW"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.Manifest.permission.INTERNAL_SYSTEM_WINDOW" />
<!-- 停用屏幕锁定 -->
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<!-- 防止手机休眠 -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 检索正在运行的应用 -->
<!-- <uses-permission android:name="android.permission.GET_TASKS" /> -->
<!-- 连接WLAN网络和断开连接 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<!-- 安装应用的权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:name=".application.App"
android:allowBackup="true"
android:alwaysRetainTaskState="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/MAppTheme"
android:requestLegacyExternalStorage="true"
android:networkSecurityConfig="@xml/network_security_config">
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">
<activity android:name=".ui.activity.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="xyz.fycz.myreader.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
android:name="androidx.core.content.FileProvider"
android:authorities="xyz.fycz.myreader.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<activity android:name=".ui.activity.MainActivity"
android:launchMode="singleTask"
android:alwaysRetainTaskState="true"/>
<activity
android:name=".ui.activity.SearchBookActivity"
android:windowSoftInputMode="stateVisible"/>
<activity android:name=".ui.activity.BookDetailedActivity"/>
android:name=".ui.activity.MainActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTask" />
<activity
android:name=".ui.activity.SearchBookActivity"
android:windowSoftInputMode="stateVisible" />
<activity android:name=".ui.activity.BookDetailedActivity" />
<activity
android:name=".ui.activity.ReadActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="stateAlwaysHidden">
android:name=".ui.activity.ReadActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity android:name=".ui.activity.FontsActivity"/>
<activity android:name=".ui.activity.FontsActivity" />
<activity android:name=".ui.activity.LoginActivity" />
<activity android:name=".ui.activity.LoginActivity"/>
<activity android:name=".ui.activity.RegisterActivity" />
<activity android:name=".ui.activity.RegisterActivity"/>
<activity android:name=".ui.activity.CatalogActivity" />
<activity android:name=".ui.activity.AboutActivity" />
<activity android:name=".ui.activity.CatalogActivity"/>
<activity android:name=".ui.activity.AboutActivity"/>
<activity android:name=".ui.activity.FileSystemActivity" />
<activity android:name=".ui.activity.FileSystemActivity"/>
<activity android:name=".ui.activity.FeedbackActivity" />
<activity android:name=".ui.activity.ReplaceRuleActivity" />
<activity android:name=".ui.activity.FeedbackActivity"/>
<activity android:name=".ui.activity.ReplaceRuleActivity"/>
<activity android:name=".ui.activity.MoreSettingActivity" />
<activity android:name=".ui.activity.BookstoreActivity" />
<activity android:name=".ui.activity.QRCodeScanActivity" />
<activity android:name=".ui.activity.BookSourceActivity" />
<activity android:name=".ui.activity.SourceEditActivity" />
<activity android:name=".ui.activity.SourceDebugActivity" />
<activity android:name=".ui.activity.AdSettingActivity" />
<activity android:name=".ui.activity.DonateActivity" />
<activity android:name=".ui.activity.BookInfoEditActivity" />
<activity android:name=".ui.activity.MoreSettingActivity"/>
<activity android:name=".ui.activity.BookstoreActivity"/>
<activity android:name=".ui.activity.QRCodeScanActivity"/>
<activity android:name=".ui.activity.BookSourceActivity"/>
<activity android:name=".ui.activity.SourceEditActivity"/>
<activity android:name=".ui.activity.SourceDebugActivity"/>
<receiver android:name=".util.notification.NotificationClickReceiver" />
<receiver android:name=".ui.presenter.BookcasePresenter$cancelDownloadReceiver" />
<receiver android:name=".util.notification.NotificationClickReceiver"/>
<receiver android:name=".ui.presenter.BookcasePresenter$cancelDownloadReceiver"/>
<service android:name=".model.audio.ReadAloudService"/>
<service android:name=".model.audio.ReadAloudService" />
</application>
</manifest>

@ -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
更新内容:

@ -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<Chapter> 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<Chapter> catheChapters = new ArrayList<>();
List<Chapter> 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 <T> void swap(T[] a, int i, int j) {
T temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static <T> 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);
}
}
}
}

@ -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;
}
}

@ -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("<br>");
}
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;
}
}

@ -19,6 +19,7 @@ import java.util.ArrayList;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import me.imid.swipebacklayout.lib.app.SwipeBackActivity;
import xyz.fycz.myreader.ActivityManage;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.App;
@ -30,7 +31,7 @@ import xyz.fycz.myreader.util.StatusBarUtil;
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class BaseActivity extends AppCompatActivity {
public abstract class BaseActivity extends SwipeBackActivity {
private static final int INVALID_VAL = -1;
protected CompositeDisposable mDisposable;
@ -43,9 +44,10 @@ public abstract class BaseActivity extends AppCompatActivity {
* 绑定视图
*/
protected abstract void bindView();
/************************init area************************************/
protected void addDisposable(Disposable d){
if (mDisposable == null){
protected void addDisposable(Disposable d) {
if (mDisposable == null) {
mDisposable = new CompositeDisposable();
}
mDisposable.add(d);
@ -54,29 +56,44 @@ public abstract class BaseActivity extends AppCompatActivity {
/**
* 配置Toolbar
*
* @param toolbar
*/
protected void setUpToolbar(Toolbar toolbar){
protected void setUpToolbar(Toolbar toolbar) {
}
/**
* 是否开启左滑手势
*
* @return
*/
protected boolean initSwipeBackEnable() {
return true;
}
protected void initData(Bundle savedInstanceState){
protected void initData(Bundle savedInstanceState) {
}
/**
* 初始化零件
*/
protected void initWidget() {
}
/**
* 初始化点击事件
*/
protected void initClick(){
protected void initClick() {
}
/**
* 逻辑使用区
*/
protected void processLogic(){
protected void processLogic() {
}
/**
* @return 是否夜间模式
*/
@ -86,6 +103,7 @@ public abstract class BaseActivity extends AppCompatActivity {
/**
* 设置夜间模式
*
* @param isNightMode
*/
protected void setNightTheme(boolean isNightMode) {
@ -95,8 +113,6 @@ public abstract class BaseActivity extends AppCompatActivity {
}
/*************************lifecycle area*****************************************************/
@Override
@ -105,6 +121,7 @@ public abstract class BaseActivity extends AppCompatActivity {
initTheme();
ActivityManage.addActivity(this);
bindView();
setSwipeBackEnable(initSwipeBackEnable());
initData(savedInstanceState);
initToolbar();
initWidget();
@ -112,10 +129,10 @@ public abstract class BaseActivity extends AppCompatActivity {
processLogic();
}
private void initToolbar(){
private void initToolbar() {
//更严谨是通过反射判断是否存在Toolbar
mToolbar = findViewById(R.id.toolbar);
if (mToolbar != null){
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null) {
supportActionBar(mToolbar);
setUpToolbar(mToolbar);
}
@ -124,7 +141,7 @@ public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onResume() {
super.onResume();
if (isThemeChange()){
if (isThemeChange()) {
recreate();
}
}
@ -133,16 +150,17 @@ public abstract class BaseActivity extends AppCompatActivity {
protected void onDestroy() {
super.onDestroy();
ActivityManage.removeActivity(this);
if (mDisposable != null){
if (mDisposable != null) {
mDisposable.dispose();
}
}
/**
* 初始化主题
*/
public void initTheme() {
//if (isNightTheme()) {
//setTheme(R.style.AppNightTheme);
//setTheme(R.style.AppNightTheme);
curNightMode = AppCompatDelegate.getDefaultNightMode();
/*} else {
//curNightMode = false;
@ -150,20 +168,21 @@ public abstract class BaseActivity extends AppCompatActivity {
}*/
}
protected boolean isThemeChange(){
protected boolean isThemeChange() {
return curNightMode != AppCompatDelegate.getDefaultNightMode();
}
/**************************used method area*******************************************/
protected void startActivity(Class<? extends AppCompatActivity> activity){
protected void startActivity(Class<? extends AppCompatActivity> activity) {
Intent intent = new Intent(this, activity);
startActivity(intent);
}
protected ActionBar supportActionBar(Toolbar toolbar){
protected ActionBar supportActionBar(Toolbar toolbar) {
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}
@ -173,7 +192,7 @@ public abstract class BaseActivity extends AppCompatActivity {
return actionBar;
}
protected void setStatusBarColor(int statusColor, boolean dark){
protected void setStatusBarColor(int statusColor, boolean dark) {
//沉浸式代码配置
//当FitsSystemWindows设置 true 时,会在屏幕最上方预留出状态栏高度的 padding
StatusBarUtil.setRootViewFitsSystemWindows(this, true);
@ -190,7 +209,9 @@ public abstract class BaseActivity extends AppCompatActivity {
StatusBarUtil.setStatusBarColor(this, 0x55000000);
}
}
} /**
}
/**
* 设置MENU图标颜色
*/
@Override

@ -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***************************/

@ -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;

@ -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";
}

@ -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规则,其中章节名称以<title>占位,章节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规则"))

@ -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);

@ -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);
}

@ -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";

@ -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) {

@ -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);
}
/**

@ -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;
}

@ -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")){

@ -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 + "次";
}
}
}

@ -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 信息流类型只能填 1463 (样式请看下表)
* 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);
}
});
}
}
}

@ -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());
}
}

@ -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());
}
}
}

@ -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);

@ -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;
}

@ -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();

@ -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();
}
});

@ -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);

@ -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);
}
}

@ -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();
}
}

@ -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);
}
}

@ -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

@ -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);
}

@ -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))

@ -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){

@ -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);
}
}
}

@ -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()) {

@ -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);

@ -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);

@ -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)));
}

@ -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, "'");
}

@ -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}";

@ -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}";

@ -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);

@ -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";

@ -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}";

@ -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}";

@ -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">简介身负系统莫名来到了大主宰的时空成为了聚灵族的最后族人在她还对周围的情况一片模糊的时候她遇到了林静这个小恶魔拜林静所赐她还遇到了武祖成为了武柤的小徒弟参与了灵路遇到了牧尘和洛璃她默默的在心中问着自己那一直在划水的系统&ldquo;你的活来了说吧我该干什么&rdquo;</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;
}
}

@ -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";

@ -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";

@ -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);

@ -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);

@ -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}";

@ -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;
// }
}

@ -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)
}
}

@ -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>

@ -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>

@ -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>

@ -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>

@ -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>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save