风月读书v1.0

pull/5/head
fengyuecanzhu 4 years ago
commit 5a5d50e861
  1. 9
      .gitignore
  2. 8
      .idea/.gitignore
  3. BIN
      .idea/caches/build_file_checksums.ser
  4. 116
      .idea/codeStyles/Project.xml
  5. 6
      .idea/compiler.xml
  6. 11
      .idea/dataSources.xml
  7. 6
      .idea/encodings.xml
  8. 20
      .idea/gradle.xml
  9. 36
      .idea/inspectionProfiles/Project_Default.xml
  10. 60
      .idea/jarRepositories.xml
  11. 53
      .idea/misc.xml
  12. 9
      .idea/modules.xml
  13. 12
      .idea/runConfigurations.xml
  14. 7
      .idea/sqldialects.xml
  15. 124
      .idea/uiDesigner.xml
  16. 6
      .idea/vcs.xml
  17. 25
      README.md
  18. 1
      app/.gitignore
  19. 125
      app/build.gradle
  20. BIN
      app/libs/ZHConverter.jar
  21. BIN
      app/libs/adapter-rxjava2-2.7.2.jar
  22. BIN
      app/libs/antlr-2.7.4.jar
  23. BIN
      app/libs/chardet-1.0.jar
  24. BIN
      app/libs/classes.jar
  25. BIN
      app/libs/cpdetector_1.0.10.jar
  26. BIN
      app/libs/jargs-1.0.jar
  27. BIN
      app/libs/rxjava-2.2.19.jar
  28. BIN
      app/libs/sun.misc.BASE64Decoder.jar
  29. BIN
      app/myReader_key.jks
  30. 25
      app/proguard-rules.pro
  31. 100
      app/src/main/AndroidManifest.xml
  32. 4
      app/src/main/assets/disclaimer.fy
  33. BIN
      app/src/main/assets/fonts/number.ttf
  34. 39
      app/src/main/java/xyz/fycz/myreader/ActivityManage.java
  35. 13
      app/src/main/java/xyz/fycz/myreader/MainActivity.java
  36. 358
      app/src/main/java/xyz/fycz/myreader/application/MyApplication.java
  37. 77
      app/src/main/java/xyz/fycz/myreader/application/SysManager.java
  38. 16
      app/src/main/java/xyz/fycz/myreader/application/TrustAllCerts.java
  39. 138
      app/src/main/java/xyz/fycz/myreader/backup/BackupAndRestore.java
  40. 324
      app/src/main/java/xyz/fycz/myreader/backup/UserService.java
  41. 134
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
  42. 6
      app/src/main/java/xyz/fycz/myreader/base/BasePresenter.java
  43. 15
      app/src/main/java/xyz/fycz/myreader/callback/HttpCallback.java
  44. 16
      app/src/main/java/xyz/fycz/myreader/callback/JsonCallback.java
  45. 14
      app/src/main/java/xyz/fycz/myreader/callback/ResultCallback.java
  46. 9
      app/src/main/java/xyz/fycz/myreader/callback/URLConnectionCallback.java
  47. 70
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  48. 11
      app/src/main/java/xyz/fycz/myreader/common/Common.java
  49. 34
      app/src/main/java/xyz/fycz/myreader/common/ErrorCode.java
  50. 39
      app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
  51. 63
      app/src/main/java/xyz/fycz/myreader/controller/SyncChaptersController.java
  52. 144
      app/src/main/java/xyz/fycz/myreader/crawler/BiQuGe44ReadCrawler.java
  53. 215
      app/src/main/java/xyz/fycz/myreader/crawler/BiQuGeReadCrawler.java
  54. 12
      app/src/main/java/xyz/fycz/myreader/crawler/BookInfoCrawler.java
  55. 121
      app/src/main/java/xyz/fycz/myreader/crawler/FYReadCrawler.java
  56. 139
      app/src/main/java/xyz/fycz/myreader/crawler/PinShuReadCrawler.java
  57. 145
      app/src/main/java/xyz/fycz/myreader/crawler/PinShuReadCrawler2.java
  58. 77
      app/src/main/java/xyz/fycz/myreader/crawler/QiDianRankList.java
  59. 23
      app/src/main/java/xyz/fycz/myreader/crawler/ReadCrawler.java
  60. 23
      app/src/main/java/xyz/fycz/myreader/crawler/ReadCrawlerUtil.java
  61. 154
      app/src/main/java/xyz/fycz/myreader/crawler/TianLaiReadCrawler.java
  62. 199
      app/src/main/java/xyz/fycz/myreader/creator/APPDownloadTip.java
  63. 143
      app/src/main/java/xyz/fycz/myreader/creator/ChangeSourceDialog.java
  64. 751
      app/src/main/java/xyz/fycz/myreader/creator/DialogCreator.java
  65. 12
      app/src/main/java/xyz/fycz/myreader/creator/ListenerInterface.java
  66. 113
      app/src/main/java/xyz/fycz/myreader/custom/BaseImageView.java
  67. 365
      app/src/main/java/xyz/fycz/myreader/custom/CircleImageView.java
  68. 201
      app/src/main/java/xyz/fycz/myreader/custom/ContainsEmojiEditText.java
  69. 41
      app/src/main/java/xyz/fycz/myreader/custom/DragAdapter.java
  70. 134
      app/src/main/java/xyz/fycz/myreader/custom/DragFloatBtnT.java
  71. 767
      app/src/main/java/xyz/fycz/myreader/custom/DragSortGridView.java
  72. 69
      app/src/main/java/xyz/fycz/myreader/custom/MyTextView.java
  73. 13
      app/src/main/java/xyz/fycz/myreader/custom/ReadTextView.java
  74. 212
      app/src/main/java/xyz/fycz/myreader/entity/ContactsTree.java
  75. 36
      app/src/main/java/xyz/fycz/myreader/entity/Custom.java
  76. 166
      app/src/main/java/xyz/fycz/myreader/entity/Date.java
  77. 105
      app/src/main/java/xyz/fycz/myreader/entity/JsonModel.java
  78. 53
      app/src/main/java/xyz/fycz/myreader/entity/SearchBookBean.java
  79. 173
      app/src/main/java/xyz/fycz/myreader/entity/Setting.java
  80. 201
      app/src/main/java/xyz/fycz/myreader/entity/Time.java
  81. 37
      app/src/main/java/xyz/fycz/myreader/entity/UpdateInfo.java
  82. 8
      app/src/main/java/xyz/fycz/myreader/entity/Void.java
  83. 28
      app/src/main/java/xyz/fycz/myreader/entity/bookstore/BookType.java
  84. 30
      app/src/main/java/xyz/fycz/myreader/enums/BookSource.java
  85. 20
      app/src/main/java/xyz/fycz/myreader/enums/BookcaseStyle.java
  86. 37
      app/src/main/java/xyz/fycz/myreader/enums/Font.java
  87. 24
      app/src/main/java/xyz/fycz/myreader/enums/Language.java
  88. 25
      app/src/main/java/xyz/fycz/myreader/enums/ReadStyle.java
  89. 34
      app/src/main/java/xyz/fycz/myreader/greendao/GreenDaoManager.java
  90. 259
      app/src/main/java/xyz/fycz/myreader/greendao/entity/Book.java
  91. 92
      app/src/main/java/xyz/fycz/myreader/greendao/entity/BookMark.java
  92. 89
      app/src/main/java/xyz/fycz/myreader/greendao/entity/Chapter.java
  93. 66
      app/src/main/java/xyz/fycz/myreader/greendao/entity/SearchHistory.java
  94. 56
      app/src/main/java/xyz/fycz/myreader/greendao/service/BaseService.java
  95. 170
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookMarkService.java
  96. 288
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
  97. 253
      app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
  98. 109
      app/src/main/java/xyz/fycz/myreader/greendao/service/SearchHistoryService.java
  99. 164
      app/src/main/java/xyz/fycz/myreader/greendao/util/GreenDaoUpgrade.java
  100. 29
      app/src/main/java/xyz/fycz/myreader/greendao/util/MySQLiteOpenHelper.java
  101. Some files were not shown because too many files have changed in this diff Show More

9
.gitignore vendored

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

@ -0,0 +1,116 @@
<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>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
</component>
</project>

@ -0,0 +1,11 @@
<?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>

@ -0,0 +1,6 @@
<?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>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

@ -0,0 +1,36 @@
<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" />
</inspection_tool>
</profile>
</component>

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<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>

@ -0,0 +1,53 @@
<?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="org.jetbrains.annotations.NotNull" />
<option name="myNullables">
<value>
<list size="13">
<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" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="12">
<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" />
</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>
</project>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/FYReader-master.iml" filepath="$PROJECT_DIR$/FYReader-master.iml" group="FYReader-master" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" group="FYReader-master/app" />
</modules>
</component>
</project>

@ -0,0 +1,12 @@
<?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>

@ -0,0 +1,7 @@
<?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/backup/UserService.java" dialect="GenericSQL" />
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

@ -0,0 +1,124 @@
<?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>

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

@ -0,0 +1,25 @@
## FYReader-master
风月读书,一款免费、无广告的小说阅读软件。
主要功能:
1、书籍可换源(共四个书源)
笔趣阁44(www.wqge.cc)、笔趣阁(www.52bqg.com)、天籁小说(www.23txt.com)、品书网(www.vodtw.la)
2、多种阅读方式(覆盖、仿真、滑动、滚动、无动画)
3、书架及设置可备份/恢复
4、支持联网下载字体、支持本地字体
5、支持切换书架布局(列表模式、宫格模式)
6、缓存可导出成txt文件
7、支持导入本地书籍,支持直接打开txt书籍文件
8、支持将书架及设置同步至网络

1
app/.gitignore vendored

@ -0,0 +1 @@
/build

@ -0,0 +1,125 @@
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'//greendao插件dependencies
def releaseTime() {
return new Date().format("yy.MMddHH", TimeZone.getTimeZone("GMT+08:00"))
}
def getVersionCode() {
def versionCodeFile = file('version_code.properties')
if (versionCodeFile.canRead()) {
Properties properties = new Properties()
properties.load(new FileInputStream(versionCodeFile))
def versionCode = properties['VERSION_CODE'].toInteger()//version_code.properties文件存放的版本号
def runTasks = gradle.startParameter.taskNames
def task = ':app:assembleRelease'
if (task in runTasks) {
properties['VERSION_CODE'] = (++versionCode).toString()
properties.store(versionCodeFile.newWriter(), null)
}
return versionCode
} else {
throw new GradleException("无法读取version_code.properties文件!")
}
}
def name = "风月读书"
def version = getVersionCode()
android {
compileSdkVersion 29
buildToolsVersion '29.0.3'
defaultConfig {
applicationId "xyz.fycz.myreader"
minSdkVersion 21
targetSdkVersion 29
versionCode version
versionName '1.' + releaseTime()
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath = true
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
ndk {
abiFilters "x86", "armeabi", "armeabi-v7a"
}
}
debug {
ndk {
abiFilters "x86", "armeabi", "armeabi-v7a"
}
}
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${name}v${defaultConfig.versionName}.apk"
}
}
}
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir "libs/"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
productFlavors {
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.jakewharton:butterknife:10.0.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'
//Glide
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
implementation 'com.squareup.okhttp3:okhttp:3.14.7'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.journeyapps:zxing-android-embedded:3.5.0'
implementation 'org.greenrobot:greendao:3.2.2'
implementation 'org.jsoup:jsoup:1.11.3'
implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.2'
implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.2'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.3'
implementation 'me.gujun.android.taggroup:library:1.4@aar'
implementation 'uk.co.chrisjenx:calligraphy:2.3.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'io.alterac.blurkit:blurkit:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:1.0.0'
}
greendao {
schemaVersion 9
daoPackage 'xyz.fycz.myreader.greendao.gen'
// targetGenDir 'src/main/java'
}

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,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,100 @@
<?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">
<!-- ********************************permission************************************ -->
<!-- 控制振动 -->
<uses-permission android:name="android.permission.VIBRATE"/> <!-- 读取手机状态和身份 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="com.example.broadcast.permission"/> <!-- 拍摄照片和视频 -->
<!-- <uses-permission android:name="android.permission.CAMERA" /> -->
<!-- 访问闪光灯 -->
<!-- <uses-permission android:name="android.permission.FLASHLIGHT" /> -->
<!-- 读取短信 -->
<!-- <uses-permission android:name="android.permission.READ_SMS" /> -->
<!-- 修改短信 -->
<!-- <uses-permission android:name="android.permission.WRITE_SMS" /> -->
<!-- 读取联系人 -->
<!-- <uses-permission android:name="android.permission.READ_CONTACTS" /> -->
<!-- 修改您的通讯录 -->
<!-- <uses-permission android:name="android.permission.WRITE_CONTACTS" /> -->
<!-- <permission android:name="android.permission.WRITE_MEDIA_STORAGE" /> -->
<!-- 读取日历活动和详情 -->
<!-- <uses-permission android:name="android.permission.READ_CALENDAR" /> -->
<!-- 添加或修改日历活动,并在所有者不知情的情况下向邀请对象发送电子邮件 -->
<!-- <uses-permission android:name="android.permission.WRITE_CALENDAR" /> -->
<!-- 拥有完全的网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET"/> <!-- 查看网络连接 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <!-- 查看WLAN连接 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!-- 修改或删除您的USB存储设备中的内容 -->
<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.CALL_PHONE" /> -->
<!-- 开机启动 -->
<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.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.REQUEST_INSTALL_PACKAGES" />
<application
android:name=".application.MyApplication"
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/AppDayTheme"
android:networkSecurityConfig="@xml/network_security_config">
<activity android:name=".ui.home.Splash">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<provider
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"/>
</provider>
<activity android:name=".ui.home.MainActivity"/>
<activity
android:name=".ui.search.SearchBookActivity"
android:windowSoftInputMode="stateVisible"/>
<activity android:name=".ui.bookinfo.BookInfoActivity"/>
<activity
android:name=".ui.read.ReadActivity"
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.font.FontsActivity">
</activity>
<activity android:name=".ui.user.LoginActivity">
</activity>
<activity android:name=".ui.user.RegisterActivity">
</activity>
<activity android:name=".ui.read.catalog.CatalogActivity">
</activity>
</application>
</manifest>

@ -0,0 +1,4 @@
1、 风月读书是一款提供网络文学搜索的工具,为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。
2、 当您搜索一本书的时,风月读书会将该书的书名以关键词的形式提交到各个第三方网络文学网站。各第三方网站返回的内容与风月读书无关,风月读书对其概不负责,亦不承担任何法律责任。任何通过使用风月读书而链接到的第三方网页均系他人制作或提供,您可能从第三方网页上获得其他服务,风月读书对其合法性概不负责,亦不承担任何法律责任。第三方搜索引擎结果根据您提交的书名自动搜索获得并提供试读,不代表风月读书赞成或被搜索链接到的第三方网页上的内容或立场。您应该对使用搜索引擎的结果自行承担风险。
3、 风月读书不做任何形式的保证:不保证第三方搜索引擎的搜索结果满足您的要求,不保证搜索服务不中断,不保证搜索结果的安全性、正确性、及时性、合法性。因网络状况、通讯线路、第三方网站等任何原因而导致您不能正常使用阅读,风月读书不承担任何法律责任。风月读书尊重并保护所有使用风月读书用户的个人隐私权,您注册的用户名、电子邮件地址等个人资料,非经您亲自许可或根据相关法律、法规的强制性规定,风月读书不会主动地泄露给第三方。
4、 风月读书致力于最大程度地减少网络文学阅读者在自行搜寻过程中的无意义的时间浪费,通过专业搜索展示不同网站中网络文学的最新章节。风月读书在为广大小说爱好者提供方便、快捷舒适的试读体验的同时,也使优秀网络文学得以迅速、更广泛的传播,从而达到了在一定程度促进网络文学充分繁荣发展之目的。风月读书鼓励广大小说爱好者通过风月读书发现优秀网络小说及其提供商,并建议阅读正版图书。任何单位或个人认为通过风月读书搜索链接到的第三方网页内容可能涉嫌侵犯其信息网络传播权,应该及时向风月读书提出书面权力通知,并提供身份证明、权属证明及详细侵权情况证明。风月读书在收到上述法律文件后,将会依法尽快断开相关链接内容。

@ -0,0 +1,39 @@
package xyz.fycz.myreader;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
/**
* Created by zhao on 2016/4/16.
*/
public class ActivityManage {
private static ArrayList<AppCompatActivity> activities = new ArrayList<AppCompatActivity>();
public static void addActivity(AppCompatActivity activity){
activities.add(activity);
}
public static void removeActivity(AppCompatActivity activity ){
activities.remove(activity);
}
public static void finishAllActivites() {
for (AppCompatActivity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
public static AppCompatActivity getActivityByCurrenlyRun(){
if(activities.size() <= 0){
return null;
}
return activities.get(activities.size() - 1);
}
}

@ -0,0 +1,13 @@
package xyz.fycz.myreader;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

@ -0,0 +1,358 @@
package xyz.fycz.myreader.application;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import androidx.appcompat.app.AppCompatActivity;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.creator.APPDownloadTip;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.entity.UpdateInfo;
import xyz.fycz.myreader.ui.home.MainActivity;
import xyz.fycz.myreader.ui.home.bookcase.BookcaseFragment;
import xyz.fycz.myreader.util.*;
public class MyApplication extends Application {
private static Handler handler = new Handler();
private static MyApplication application;
private ExecutorService mFixedThreadPool;
@Override
public void onCreate() {
super.onCreate();
application = this;
HttpUtil.trustAllHosts();//信任所有证书
// handleSSLHandshake();
mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池
BaseActivity.setCloseAntiHijacking(true);
}
@SuppressLint("TrulyRandom")
public static void handleSSLHandshake() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
} catch (Exception ignored) {
}
}
public static Context getmContext() {
return application;
}
public void newThread(Runnable runnable) {
try {
mFixedThreadPool.execute(runnable);
} catch (Exception e) {
e.printStackTrace();
mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池
mFixedThreadPool.execute(runnable);
}
}
public void shutdownThreadPool() {
mFixedThreadPool.shutdownNow();
}
@TargetApi(26)
private void createNotificationChannel() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(new NotificationChannel("gxdw_push_and_im", "gxdw", NotificationManager.IMPORTANCE_DEFAULT));
}
/**
* 主线程执行
*
* @param runnable
*/
public static void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
public static MyApplication getApplication() {
return application;
}
private boolean isFolderExist(String dir) {
File folder = Environment.getExternalStoragePublicDirectory(dir);
return (folder.exists() && folder.isDirectory()) || folder.mkdirs();
}
/**
* 获取app版本号
*
* @return
*/
public static int getVersionCode() {
try {
PackageManager manager = application.getPackageManager();
PackageInfo info = manager.getPackageInfo(application.getPackageName(), 0);
return info.versionCode;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取app版本号(String)
*
* @return
*/
public static String getStrVersionName() {
try {
PackageManager manager = application.getPackageManager();
PackageInfo info = manager.getPackageInfo(application.getPackageName(), 0);
return info.versionName;
} catch (Exception e) {
e.printStackTrace();
return "1.0.0";
}
}
/**
* 检查更新
*/
public static void checkVersion(AppCompatActivity activity) {
UpdateInfo updateInfo = (UpdateInfo) CacheHelper.readObject(APPCONST.FILE_NAME_UPDATE_INFO);
int versionCode = getVersionCode();
if (updateInfo != null) {
if (updateInfo.getNewestVersionCode() > versionCode) {
//updateApp(activity, updateInfo.getDownLoadUrl(), versionCode);
}
}
}
/**
* 检查更新
*/
/*public static void checkVersionByServer(final Activity activity, final boolean isManualCheck) {
MyApplication.getApplication().newThread(new Runnable() {
@Override
public void run() {
Document doc = null;
try {
doc = Jsoup.connect("https://novel.fycz.xyz/app/update.html").get();
int newestVersion = 0;
String updateContent = "";
String downloadLink = null;
boolean isForceUpdate = false;
StringBuilder s = new StringBuilder();
assert doc != null;
Elements nodes = doc.getElementsByClass("secd-rank-list");
newestVersion = Integer.valueOf(nodes.get(0).getElementsByTag("a").get(1).text());
downloadLink = nodes.get(0).getElementsByTag("a").get(1).attr("href");
updateContent = nodes.get(0).getElementsByTag("a").get(2).text();
isForceUpdate = Boolean.parseBoolean(nodes.get(0).getElementsByTag("a").get(3).text());
String[] updateContents = updateContent.split("/");
for (String string : updateContents) {
s.append(string);
s.append("\n");
}
int versionCode = getVersionCode();
if (newestVersion > versionCode) {
MyApplication m = new MyApplication();
Setting setting = SysManager.getSetting();
if (isManualCheck || setting.getNewestVersionCode() < newestVersion || isForceUpdate) {
setting.setNewestVersionCode(newestVersion);
SysManager.saveSetting(setting);
int i = setting.getNewestVersionCode();
m.updateApp(activity, downloadLink, newestVersion, s.toString(), isForceUpdate);
}
} else if (isManualCheck) {
TextHelper.showText("已经是最新版本!");
}
} catch (IOException e) {
e.printStackTrace();
TextHelper.showText("无网络连接!");
}
}
});
}*/
public static void checkVersionByServer(final MainActivity activity, final boolean isManualCheck,
final BookcaseFragment mBookcaseFragment) {
MyApplication.getApplication().newThread(new Runnable() {
@Override
public void run() {
Document doc = null;
try {
String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read";
if (isApkInDebug(getmContext())){
url = "https://shimo.im/docs/zfzpda7MUGskOC9v/read";
}
doc = Jsoup.connect(url).get();
String content = doc.getElementsByClass("ql-editor").text();
if (StringHelper.isEmpty(content)){
TextHelper.showText("检查更新失败!");
return;
}
String[] contents = content.split(";");
int newestVersion = 0;
String updateContent = "";
String downloadLink = null;
boolean isForceUpdate = false;
StringBuilder s = new StringBuilder();
newestVersion = Integer.parseInt(contents[0].substring(contents[0].indexOf(":") + 1));
isForceUpdate = Boolean.parseBoolean(contents[1].substring(contents[1].indexOf(":") + 1));
downloadLink = contents[2].substring(contents[2].indexOf(":") + 1);
updateContent = contents[3].substring(contents[3].indexOf(":") + 1);
SharedPreUtils spu = SharedPreUtils.getInstance();
spu.putString("lanzousKeyStart", contents[4].substring(contents[4].indexOf(":") + 1));
String[] updateContents = updateContent.split("/");
for (String string : updateContents) {
s.append(string);
s.append("\n");
}
int versionCode = getVersionCode();
if (newestVersion > versionCode) {
MyApplication m = new MyApplication();
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);
}
} else if (isManualCheck) {
TextHelper.showText("已经是最新版本!");
}
} catch (Exception e) {
e.printStackTrace();
TextHelper.showText("无网络连接!");
}
}
});
}
/**
* App自动升级
*
* @param activity
* @param versionCode
*/
public void updateApp(final MainActivity activity, final String url, final int versionCode, String message,
final boolean isForceUpdate, final BookcaseFragment mBookcaseFragment) {
//String version = (versionCode / 100 % 10) + "." + (versionCode / 10 % 10) + "." + (versionCode % 10);
String cancelTitle;
if(isForceUpdate){
cancelTitle = "退出";
}else {
cancelTitle = "忽略此版本";
}
DialogCreator.createThreeButtonDialog(activity, "发现新版本:", message, !isForceUpdate, cancelTitle, "直接下载",
"浏览器下载", (dialog, which) -> {
if (isForceUpdate) {
activity.finish();
}
}, (dialog, which) -> {
if (url == null || "".equals(url)) {
TextHelper.showText("获取链接失败,请前往浏览器下载!");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(URLCONST.APP_DIR_UR));
activity.startActivity(intent);
if (isForceUpdate) {
activity.finish();
}
} else {
APPDownloadTip downloadTip = new APPDownloadTip(url, mBookcaseFragment, activity, isForceUpdate);
downloadTip.downloadApp();
}
}, (dialog, which) -> {
String downloadLink = url;
if (url == null || "".equals(url)) {
downloadLink = URLCONST.APP_DIR_UR;
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(downloadLink));
activity.startActivity(intent);
if (isForceUpdate) {
activity.finish();
}
});
}
/**
* 判断当前应用是否是debug状态
*/
public static boolean isApkInDebug(Context context) {
try {
ApplicationInfo info = context.getApplicationInfo();
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
return false;
}
}
}

@ -0,0 +1,77 @@
package xyz.fycz.myreader.application;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.enums.BookcaseStyle;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.enums.Language;
import xyz.fycz.myreader.enums.ReadStyle;
import xyz.fycz.myreader.util.CacheHelper;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.widget.page.PageMode;
import static xyz.fycz.myreader.application.MyApplication.getVersionCode;
public class SysManager {
public static void logout() {
}
/**
* 获取设置
* @return
*/
public static Setting getSetting() {
Setting setting = (Setting) CacheHelper.readObject(APPCONST.FILE_NAME_SETTING);
if (setting == null){
setting = getDefaultSetting();
saveSetting(setting);
}
return setting;
}
/**
* 保存设置
* @param setting
*/
public static void saveSetting(Setting setting) {
CacheHelper.saveObject(setting, APPCONST.FILE_NAME_SETTING);
}
/**
* 默认设置
* @return
*/
private static Setting getDefaultSetting(){
Setting setting = new Setting();
setting.setDayStyle(true);
setting.setReadBgColor(R.color.sys_protect_eye_bg);
setting.setReadStyle(ReadStyle.protectedEye);
setting.setReadWordSize(25);
setting.setReadWordColor(R.color.sys_protect_eye_word);
setting.setBrightProgress(50);
setting.setBrightFollowSystem(true);
setting.setLanguage(Language.simplified);
setting.setFont(Font.默认字体);
setting.setAutoScrollSpeed(300);
setting.setPageMode(PageMode.SIMULATION);
setting.setVolumeTurnPage(false);
setting.setBookcaseStyle(BookcaseStyle.listMode);
setting.setNewestVersionCode(getVersionCode());
setting.setLocalFontName("");
setting.setSettingVersion(APPCONST.SETTING_VERSION);
return setting;
}
/**
* 重置设置
*/
public static void resetSetting(){
saveSetting(getDefaultSetting());
}
}

@ -0,0 +1,16 @@
package xyz.fycz.myreader.application;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
public class TrustAllCerts implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
@Override
public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
}

@ -0,0 +1,138 @@
package xyz.fycz.myreader.backup;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author fengyue
* @date 2020/4/25 9:07
*/
public class BackupAndRestore {
BookService mBookService = new BookService();
/**
* 备份书架
* @return 是否备份成功
*/
public boolean backup(String backupName){
List<Book> books = mBookService.getAllBooks();
StringBuilder s = new StringBuilder();
for (Book book : books) {
s.append(book);
s.append(",\n");
}
s.deleteCharAt(s.lastIndexOf(","));
File booksFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/books" + FileUtils.SUFFIX_FY);
File settingFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/setting" + FileUtils.SUFFIX_FY);
BufferedWriter bw = null;
ObjectOutputStream oos = null;
try {
bw = new BufferedWriter(new FileWriter(booksFile));
bw.write(s.toString());
bw.flush();
oos = new ObjectOutputStream(new FileOutputStream(settingFile));
oos.writeObject(SysManager.getSetting());
oos.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 恢复书架
* @return 是否恢复成功
*/
public boolean restore(String backupName) {
File booksFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/books" + FileUtils.SUFFIX_FY);
File settingFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/setting" + FileUtils.SUFFIX_FY);
if (!booksFile.exists() || !settingFile.exists()){
return false;
}
BufferedReader br = null;
ObjectInputStream ois = null;
try {
br = new BufferedReader(new FileReader(booksFile));
String tem = "";
StringBuilder s = new StringBuilder();
while ((tem = br.readLine()) != null){
s.append(tem).append("\n");
}
String[] sBooks = s.toString().split("\\},");
List<Book> books = new ArrayList<>();
for (String sBook : sBooks){
sBook.replace("{", "");
sBook.replace("}", "");
String[] sBookFields = sBook.split(",\n");
for (int i = 0; i < sBookFields.length; i++) {
sBookFields[i] = sBookFields[i].substring(sBookFields[i].indexOf("'") + 1, sBookFields[i].lastIndexOf("'"));
}
String source = "null";
if(!sBookFields[2].contains("novel.fycz.xyz")){
source = sBookFields[17];
}
if ("本地书籍".equals(sBookFields[4])){
sBookFields[15] = "0";
}
Book book = new Book(sBookFields[0], sBookFields[1], sBookFields[2], sBookFields[3], sBookFields[4],
sBookFields[5], sBookFields[6], sBookFields[7], sBookFields[8], sBookFields[9], sBookFields[10],
sBookFields[11], Integer.parseInt(sBookFields[12]), Integer.parseInt(sBookFields[13]),
Integer.parseInt(sBookFields[14]), Integer.parseInt(sBookFields[15]), Integer.parseInt(sBookFields[16])
, source);
books.add(book);
}
mBookService.deleteAllBooks();
mBookService.addBooks(books);
ois = new ObjectInputStream(new FileInputStream(settingFile));
Object obj = ois.readObject();
if (obj instanceof Setting){
Setting setting = (Setting) obj;
SysManager.saveSetting(setting);
}
return true;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return false;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

@ -0,0 +1,324 @@
package xyz.fycz.myreader.backup;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.util.*;
import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/**
* @author fengyue
* @date 2020/4/26 11:03
*/
public class UserService {
/**
* 登录
* @param userLoginInfo 用户名输入的用户名和密码等登录信息
* @return 是否成功登录
*/
public static void login(final Map<String, String> userLoginInfo, final ResultCallback resultCallback) {
MyApplication.getApplication().newThread(() -> {
HttpURLConnection conn = null;
try {
URL url = new URL(URLCONST.APP_WEB_URL + "login");
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(60 * 1000);
conn.setReadTimeout(60 * 1000);
conn.setDoInput(true);
conn.setDoOutput(true);
String params = "username=" + userLoginInfo.get("loginName") +
"&password=" + userLoginInfo.get("loginPwd") + makeSignalParam();
// 获取URLConnection对象对应的输出流
PrintWriter out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(params);
// flush输出流的缓冲
out.flush();
InputStream in = conn.getInputStream();
BufferedReader bw = new BufferedReader(new InputStreamReader(in, "utf-8"));
StringBuilder sb = new StringBuilder();
String line = bw.readLine();
while (line != null) {
sb.append(line);
line = bw.readLine();
}
resultCallback.onFinish(sb.toString(), 1);
} catch (IOException e) {
e.printStackTrace();
resultCallback.onError(e);
}finally {
if (conn != null) {
conn.disconnect();
}
}
});
}
public static void register(final Map<String, String> userRegisterInfo, final ResultCallback resultCallback) {
MyApplication.getApplication().newThread(new Runnable() {
@Override
public void run() {
HttpURLConnection conn = null;
try {
URL url = new URL(URLCONST.APP_WEB_URL + "reg");
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
String params = "username=" + userRegisterInfo.get("username") + "&password=" +
CyptoUtils.encode(APPCONST.KEY, userRegisterInfo.get("password")) + "&key=" +
CyptoUtils.encode(APPCONST.KEY, APPCONST.publicKey) + makeSignalParam();
// 获取URLConnection对象对应的输出流
PrintWriter out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(params);
// flush输出流的缓冲
out.flush();
BufferedReader bw = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
StringBuilder sb = new StringBuilder();
String line = bw.readLine();
while (line != null) {
sb.append(line);
line = bw.readLine();
}
resultCallback.onFinish(sb.toString(), 1);
} catch (IOException e) {
e.printStackTrace();
resultCallback.onError(e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
});
}
/**
* 写配置
* @param userLoginInfo
* @return
*/
public static boolean writeConfig(Map<String,String> userLoginInfo){
FileOutputStream fos = null;
try {
fos = MyApplication.getApplication().openFileOutput("userConfig.fy", MyApplication.getApplication().MODE_PRIVATE);
String userInfo = "username='" + userLoginInfo.get("loginName") + "',\npassword='" + userLoginInfo.get("loginPwd") + "'";
byte[] bs = userInfo.getBytes();
fos.write(bs);
//写完后一定要刷新
fos.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 读配置
* @return
*/
public static Map<String,String> readConfig(){
File file = MyApplication.getApplication().getFileStreamPath("userConfig.fy");
if (!file.exists()){
return null;
}
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
String tem;
StringBuilder config = new StringBuilder();
while ((tem = br.readLine()) != null){
config.append(tem);
}
String[] user = config.toString().split(",");
String userName = user[0].substring(user[0].indexOf("'") + 1, user[0].lastIndexOf("'"));
String password = user[1].substring(user[1].indexOf("'") + 1, user[1].lastIndexOf("'"));
Map<String,String> userInfo = new HashMap<>();
userInfo.put("userName", userName);
userInfo.put("password", password);
return userInfo;
} catch (IOException e) {
e.printStackTrace();
}finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
public static void writeUsername(String username){
File file = FileUtils.getFile(APPCONST.QQ_DATA_DIR + "user");
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(file));
bw.write(username);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.close(bw);
}
}
public static String readUsername(){
File file = new File(APPCONST.QQ_DATA_DIR + "user");
if (!file.exists()){
return "";
}
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
} finally {
IOUtils.close(br);
}
}
/**
* 网络备份
* @return
*/
public static boolean webBackup(){
Map<String,String> userInfo = readConfig();
if (userInfo == null){
return false;
}
BackupAndRestore bar = new BackupAndRestore();
bar.backup("webBackup");
File inputFile = FileUtils.getFile(APPCONST.FILE_DIR + "webBackup");
if (!inputFile.exists()) {
return false;
}
File zipFile = FileUtils.getFile(APPCONST.FILE_DIR + "webBackup.zip");
FileInputStream fis = null;
HttpURLConnection conn = null;
try {
//压缩文件
ZipUtils.zipFile(inputFile, zipFile);
fis = new FileInputStream(zipFile);
URL url = new URL(URLCONST.APP_WEB_URL + "bak?username=" + userInfo.get("userName") +
makeSignalParam());
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-type", "multipart/form-data");
conn.setDoInput(true);
conn.setDoOutput(true);
OutputStream out = conn.getOutputStream();
byte[] bytes = new byte[1024];
int len = -1;
while ((len = fis.read(bytes)) != -1){
out.write(bytes, 0, len);
}
out.flush();
zipFile.delete();
BufferedReader bw = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
StringBuilder sb = new StringBuilder();
String line = bw.readLine();
while (line != null) {
sb.append(line);
line = bw.readLine();
}
String[] info = sb.toString().split(":");
int code = Integer.parseInt(info[0].trim());
return code == 104;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
IOUtils.close(fis);
if (conn != null) {
conn.disconnect();
}
}
}
/**
* 网络恢复
* @return
*/
public static boolean webRestore(){
Map<String,String> userInfo = readConfig();
if (userInfo == null){
return false;
}
FileOutputStream fos = null;
File zipFile = FileUtils.getFile(APPCONST.FILE_DIR + "webBackup.zip");
HttpURLConnection conn = null;
try {
URL url = new URL(URLCONST.APP_WEB_URL + "ret?username=" + userInfo.get("userName") +
makeSignalParam());
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
InputStream is = conn.getInputStream();
fos = new FileOutputStream(zipFile);
//一边读,一边写
byte[] bytes = new byte[512];
int readCount = 0;
while ((readCount = is.read(bytes)) != -1) {
fos.write(bytes,0, readCount);
}
//刷新,输出流一定要刷新
fos.flush();
if (zipFile.length() == 0){
zipFile.delete();
return false;
}
ZipUtils.unzipFile(zipFile.getAbsolutePath(), APPCONST.FILE_DIR);
BackupAndRestore bar = new BackupAndRestore();
bar.restore("webBackup");
zipFile.delete();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}finally {
IOUtils.close(fos);
if (conn != null) {
conn.disconnect();
}
}
}
private static String makeSignalParam(){
return "&signal=" + AppInfoUtils.getSingInfo(MyApplication.getmContext(),
MyApplication.getApplication().getPackageName(), AppInfoUtils.SHA1);
}
/**
* 判断是否登录
* @return
*/
public static boolean isLogin(){
File file = MyApplication.getApplication().getFileStreamPath("userConfig.fy");
return file.exists();
}
}

@ -0,0 +1,134 @@
package xyz.fycz.myreader.base;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import androidx.appcompat.app.AppCompatActivity;
import xyz.fycz.myreader.ActivityManage;
import xyz.fycz.myreader.util.Anti_hijackingUtils;
import xyz.fycz.myreader.util.SystemBarTintManager;
public class BaseActivity extends AppCompatActivity {
public static int width = 0;
public static int height = 0;
public static boolean home;
public static boolean back;
private boolean catchHomeKey = false;
private boolean disallowAntiHijacking;//暂停防界面劫持
private static boolean closeAntiHijacking;//关闭防界面劫持
private InputMethodManager mInputMethodManager; //输入管理器
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//将每一个Activity都加入activity管理器
ActivityManage.addActivity(this);
Log.d("ActivityName: ",getLocalClassName());
DisplayMetrics dm = new DisplayMetrics();
//获取屏幕宽高
if(height == 0 || height == 0){
getWindowManager().getDefaultDisplay().getMetrics(dm);
width = dm.widthPixels;
height = dm.heightPixels;
}
mInputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
}
public static void setCloseAntiHijacking(boolean closeAntiHijacking) {
BaseActivity.closeAntiHijacking = closeAntiHijacking;
}
@Override
protected void onDestroy() {
ActivityManage.removeActivity(this);
super.onDestroy();
}
@Override
protected void onPause() {
if (!disallowAntiHijacking && !closeAntiHijacking) {
Anti_hijackingUtils.getinstance().onPause(this);//防界面劫持提示任务
}
super.onPause();
}
@Override
protected void onResume() {
if (!closeAntiHijacking) {
Anti_hijackingUtils.getinstance().onResume(this);//注销防界面劫持提示任务
}
BaseActivity.home = false;
BaseActivity.back = false;
disallowAntiHijacking = false;
super.onResume();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK){
back = true;//以便于判断是否按返回键触发界面劫持提示
}
return super.onKeyDown(keyCode, event);
}
@TargetApi(19)
protected void setTranslucentStatus(boolean on) {
Window win = getWindow();
WindowManager.LayoutParams winParams = win.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (on) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
win.setAttributes(winParams);
}
public void setDisallowAntiHijacking(boolean disallowAntiHijacking) {
this.disallowAntiHijacking = disallowAntiHijacking;
}
/**
* 设置状态栏颜色
* @param colorId
*/
public void setStatusBar(int colorId){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setTranslucentStatus(true);
SystemBarTintManager tintManager = new SystemBarTintManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(colorId);//通知栏所需颜色ID
}
}
public InputMethodManager getmInputMethodManager() {
return mInputMethodManager;
}
}

@ -0,0 +1,6 @@
package xyz.fycz.myreader.base;
public interface BasePresenter {
void start();
}

@ -0,0 +1,15 @@
package xyz.fycz.myreader.callback;
import android.graphics.Bitmap;
import java.io.InputStream;
/**
* Created by zhao on 2016/4/16.
*/
public interface HttpCallback {
void onFinish(String response);
void onFinish(InputStream in);
void onFinish(Bitmap bm);
void onError(Exception e);
}

@ -0,0 +1,16 @@
package xyz.fycz.myreader.callback;
import xyz.fycz.myreader.entity.JsonModel;
/**
* Created by zhao on 2016/10/25.
*/
public interface JsonCallback {
void onFinish(JsonModel jsonModel);
void onError(Exception e);
}

@ -0,0 +1,14 @@
package xyz.fycz.myreader.callback;
/**
* Created by zhao on 2016/10/25.
*/
public interface ResultCallback {
void onFinish(Object o, int code);
void onError(Exception e);
}

@ -0,0 +1,9 @@
package xyz.fycz.myreader.callback;
/**
* Created by zhao on 2016/4/16.
*/
public interface URLConnectionCallback {
void onFinish(boolean checked);
}

@ -0,0 +1,70 @@
package xyz.fycz.myreader.common;
import android.os.Environment;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.File;
public class APPCONST {
public static String publicKey = "fyds1.0";//服务端公钥
public static String privateKey;//app私钥
public final static String s = "11940364935628058505";
public static final String KEY = "readerByFengyue";
public static final String ALARM_SCHEDULE_MSG = "alarm_schedule_msg";
public static final String FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/";
public static final String TEM_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/tem/";
public static final String BACKUP_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/backup/";
public static final String TXT_BOOK_DIR = Environment.getExternalStorageDirectory() + "/FYReader/noveltxt/";
public static final String FONT_BOOK_DIR = Environment.getExternalStorageDirectory() + "/FYReader/font/";
public static final String UPDATE_APK_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/apk/";
public static final String QQ_DATA_DIR = Environment.getExternalStorageDirectory() + "/tencent/MobileQQ/data/";
//BookCachePath (因为getCachePath引用了Context,所以必须是静态变量,不能够是静态常量)
public static String BOOK_CACHE_PATH = FileUtils.getCachePath() + File.separator
+ "book_cache"+ File.separator ;
public static final String LOADING_ERROR = "\t\t \t\t\t\t\n\n  章节内容转码失败!\n\n\t\t \t\t\t\n\n";
public static long exitTime;
public static final int exitConfirmTime = 2000;
public static final String BOOK = "book";
public static final String CHAPTER_PAGE = "chapter_page";
public static final String SETTING = "setting";
public static final String SEARCH_BOOK_BEAN = "SearchBookBean";
public static final String FONT = "font";
public static final int[] READ_STYLE_NIGHT = {R.color.sys_night_word, R.color.sys_night_bg};//黑夜
public static final int[] READ_STYLE_PROTECTED_EYE = {R.color.sys_protect_eye_word, R.color.sys_protect_eye_bg};//护眼
public static final int[] READ_STYLE_COMMON = {R.color.sys_common_word, R.color.sys_common_bg};//普通
public static final int[] READ_STYLE_BLUE_DEEP = {R.color.sys_blue_deep_word, R.color.sys_blue_deep_bg};//深蓝
public static final int[] READ_STYLE_LEATHER = {R.color.sys_leather_word, R.mipmap.theme_leather_bg};//羊皮纸
public static final int[] READ_STYLE_BREEN_EYE = {R.color.sys_breen_word, R.color.sys_breen_bg};//棕绿色
public static final String FILE_NAME_SETTING = "setting";
public static final String FILE_NAME_UPDATE_INFO = "updateInfo";
public static final int REQUEST_FONT = 1001;
public static final int REQUEST_CHAPTER_PAGE = 1002;
public static final String RESULT_IS_COLLECTED = "result_is_collected";
public static final int REQUEST_READ = 1;
public static final CharSequence[] DIALOG_DOWNLOAD = {
"下载后面五十章", "下载前后五十章",
"下载后面全部章", "下载本书所有章"
};
public static final int APP_INSTALL_CODE = 10086;
public static final int SELECT_FILE_CODE = 10000;
//设置版本号
public static final int SETTING_VERSION = 1;
}

@ -0,0 +1,11 @@
package xyz.fycz.myreader.common;
public interface Common {
int SUCCESS = 1;
int INSUCCESS = 0;
int LOGINEXIT = 2;
int MESSAGE_HINT = 3;
}

@ -0,0 +1,34 @@
package xyz.fycz.myreader.common;
public class ErrorCode {
public static final int login_not_user = 1; // 用户不存在
public static final int login_not_password = 2;// 密码错误
public static final int internet_waring = 3;// 网络访问错误
public static final int no_security = 4;// 没有权限(登陆过期)
public static final int no_login_ = 5;// 没有登录
public static final int meeting_user_no = 6;// 报名用户不存在
public static final int old_password = 8;// 原密码错误
public static final int execute_error = 7;//程序运行异常
public static final int order_timeout = 9;// 订餐超时
public static final int email_or_emailPwd_empty = 10;//邮箱或邮箱密码为空
public static final int email_login_error = 11;//邮箱登陆失败
public static final int email_params_error = 12;//邮箱接口参数不完整
public static final int suggestion_params_error = 13;//反馈意见接口参数不完整
public static final int email_addresses_error = 15;//邮箱地址无效
public static final int uploadfile_extend_error = 16;//上传附件后缀不支持
public static final int params_overlength = 17;//接口参数过长
public static final int leave_error = 18;//请假审批人跟请假人不能相同
public static final int login_simple_pwd = 19;//简单登录密码
public static final int more_depart = 1011;//多部门用户
public static final int Certificate_sys_no_user = 1012;//认证系统内无此用户
public static final int Certificate_no_bind_user = 1013;//用户没绑定证书在系统内
public static final int ukey_duplicate_user = 1014;//ukey序列号系统中不存在或存在多笔数据,请联系管理员
}

@ -0,0 +1,39 @@
package xyz.fycz.myreader.common;
/**
* Created by zhao on 2016/10/20.
*/
public class URLCONST {
// 命名空间
public static String nameSpace_FY = "https://novel.fycz.xyz";
public static String nameSpace_biquge = "https://www.52bqg.com/";
public static String nameSpace_system = "https://novel.fycz.xyz";
public static boolean isRSA = false;
// 搜索小说
public static String method_fengyue_search = "https://novel.fycz.xyz/search.html";
public static String method_buxiu_search = "https://www.23txt.com/search.php";
// 获取最新版本号
public static String method_getCurAppVersion = nameSpace_system + "/mReaderController.do?getCurAppVersion";
// app下载
public static String method_downloadApp = nameSpace_system + "/app/FYReader.apk";
public static String APP_DIR_UR = "https://www.lanzous.com/b00ngso7e";
public static String LAN_ZOUS_URL = "https://fycz.lanzous.com";
//字体下载
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/";
}

@ -0,0 +1,63 @@
package xyz.fycz.myreader.controller;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.ChapterService;
import java.util.ArrayList;
public class SyncChaptersController {
private ArrayList<Chapter> mLocalChapters;//本地章节
private ArrayList<Chapter> mNetChapters;//网络章节
private Book mBook;//书
private ChapterService mChapterService;
public SyncChaptersController(ArrayList<Chapter> localChapters,ArrayList<Chapter> netChapters,Book book){
mLocalChapters = localChapters;
mNetChapters = netChapters;
mBook = book;
mChapterService = new ChapterService();
}
/**
* 开始同步
*/
public void sync(){
}
/**
* 更新所有章节
*
* @param newChapters
*/
private void updateAllOldChapterData(ArrayList<Chapter> newChapters) {
int i;
for (i = 0; i < mLocalChapters.size() && i < newChapters.size(); i++) {
Chapter oldChapter = mLocalChapters.get(i);
Chapter newChapter = newChapters.get(i);
if (!oldChapter.getTitle().equals(newChapter.getTitle())) {
oldChapter.setTitle(newChapter.getTitle());
oldChapter.setUrl(newChapter.getUrl());
oldChapter.setContent(null);
mChapterService.updateEntity(oldChapter);
}
}
if (mLocalChapters.size() < newChapters.size()) {
for (int j = mLocalChapters.size(); j < newChapters.size(); j++) {
mLocalChapters.add(newChapters.get(j));
mChapterService.addChapter(newChapters.get(j), null);
}
} else if (mLocalChapters.size() > newChapters.size()) {
for (int j = newChapters.size(); j < mLocalChapters.size(); j++) {
mChapterService.deleteEntity(mLocalChapters.get(j));
}
mLocalChapters.subList(0, newChapters.size());
}
}
}

@ -0,0 +1,144 @@
package xyz.fycz.myreader.crawler;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import static xyz.fycz.myreader.webapi.BaseApi.getCommonReturnHtmlStringApi;
public class BiQuGe44ReadCrawler implements ReadCrawler, BookInfoCrawler{
public static final String NAME_SPACE = "https://www.wqge.cc";
public static final String NOVEL_SEARCH = "https://www.wqge.cc/modules/article/search.php";
public static final String SEARCH_KEY = "searchkey";
public static final String CHARSET = "GBK";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public String getSearchKey() {
return SEARCH_KEY;
}
/**
* 从html中获取章节正文
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("content");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
String readUrl = doc.select("meta[property=og:novel:read_url]").attr("content");
Element divList = doc.getElementById("list");
String lastTile = null;
int i = 0;
Elements elementsByTag = divList.getElementsByTag("dd");
for (int j = 9; j < elementsByTag.size(); j++) {
Element dd = elementsByTag.get(j);
Elements as = dd.getElementsByTag("a");
if (as.size() > 0) {
Element a = as.get(0);
String title = a.text() ;
if (!StringHelper.isEmpty(lastTile) && title.equals(lastTile)) {
continue;
}
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
String url = readUrl + a.attr("href");
chapter.setUrl(url);
chapters.add(chapter);
lastTile = title;
}
}
return chapters;
}
/**
* 从搜索html中得到书列表
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
Elements divs = doc.getElementsByTag("table");
Element div = divs.get(0);
Elements elementsByTag = div.getElementsByTag("tr");
for (int i = 1; i < elementsByTag.size(); i++) {
Element element = elementsByTag.get(i);
Book book = new Book();
Elements info = element.getElementsByTag("td");
book.setName(info.get(0).text());
book.setChapterUrl(NAME_SPACE + info.get(0).getElementsByTag("a").attr("href"));
book.setAuthor(info.get(2).text());
book.setNewestChapterTitle(info.get(1).text());
book.setSource(BookSource.biquge44.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
return books;
}
/**
* 获取书籍详细信息
* @param book
*/
public Book getBookInfo(String html, Book book){
Document doc = Jsoup.parse(html);
Element img = doc.getElementById("fmimg");
book.setImgUrl(img.getElementsByTag("img").get(0).attr("src"));
Element desc = doc.getElementById("intro");
book.setDesc(desc.getElementsByTag("p").get(0).text());
Element type = doc.getElementsByClass("con_top").get(0);
book.setType(type.getElementsByTag("a").get(2).text());
return book;
}
}

@ -0,0 +1,215 @@
package xyz.fycz.myreader.crawler;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.CommonApi;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BiQuGeReadCrawler implements ReadCrawler, BookInfoCrawler {
private static final String NAME_SPACE = "https://www.52bqg.com";
private static final String NOVEL_SEARCH = "https://www.52bqg.com/modules/article/search.php";
private static final String SEARCH_KEY = "searchkey";
private static final String CHARSET = "GBK";
private ReadCrawler rc = new TianLaiReadCrawler();
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public String getSearchKey() {
return SEARCH_KEY;
}
@Override
public String getCharset() {
return CHARSET;
}
/**
* 获取书城小说分类列表
*
* @param html
* @return
*/
public static List<BookType> getBookTypeList(String html) {
List<BookType> bookTypes = new ArrayList<>();
Document doc = Jsoup.parse(html);
Elements divs = doc.getElementsByClass("nav");
if (divs.size() > 0) {
Elements uls = divs.get(0).getElementsByTag("ul");
if (uls.size() > 0) {
for (Element li : uls.get(0).children()) {
Element a = li.child(0);
BookType bookType = new BookType();
bookType.setTypeName(a.attr("title"));
bookType.setUrl(a.attr("href"));
if (!bookType.getTypeName().contains("小说") || bookType.getTypeName().contains("排行")) continue;
if (!StringHelper.isEmpty(bookType.getTypeName())) {
bookTypes.add(bookType);
}
}
}
}
return bookTypes;
}
/**
* 获取某一分类小说排行榜列表
*
* @param html
* @return
*/
public static List<Book> getBookRankList(String html) {
List<Book> books = new ArrayList<>();
Document doc = Jsoup.parse(html);
Elements divs = doc.getElementsByClass("r");
if (divs.size() > 0) {
Elements uls = divs.get(0).getElementsByTag("ul");
if (uls.size() > 0) {
for (Element li : uls.get(0).children()) {
Book book = new Book();
Element scanS1 = li.getElementsByClass("s1").get(0);
Element scanS2 = li.getElementsByClass("s2").get(0);
Element scanS5 = li.getElementsByClass("s5").get(0);
book.setType(scanS1.html().replace("[", "").replace("]", ""));
Element a = scanS2.getElementsByTag("a").get(0);
book.setName(a.attr("title"));
book.setChapterUrl(a.attr("href"));
book.setAuthor(scanS5.html());
book.setSource(BookSource.biquge.toString());
books.add(book);
}
}
}
return books;
}
@Override
public String getContentFormHtml(String html) {
return rc.getContentFormHtml(html);
}
@Override
public ArrayList<Chapter> getChaptersFromHtml(String html) {
return rc.getChaptersFromHtml(html);
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
final ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
String urlType = doc.select("meta[property=og:type]").attr("content");
if ("novel".equals(urlType)) {
String readUrl = doc.select("meta[property=og:novel:read_url]").attr("content");
Book book = new Book();
book.setChapterUrl(readUrl);
getBookInfo(html, book);
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
} else {
Elements divs = doc.getElementsByClass("novelslistss");
Element div = divs.get(0);
Elements elementsByTag = div.getElementsByTag("li");
for (Element element : elementsByTag) {
Book book = new Book();
Elements info = element.getElementsByTag("span");
book.setName(info.get(1).text());
book.setChapterUrl(info.get(1).getElementsByTag("a").attr("href"));
book.setAuthor(info.get(3).text());
book.setNewestChapterTitle(info.get(2).text());
book.setSource(BookSource.biquge.toString());
book.setType(info.get(0).text());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
}
return books;
}
/**
* 获取小说详细信息
*
* @param html
* @return
*/
public Book getBookInfo(String html, Book book) {
//小说源
book.setSource(BookSource.biquge.toString());
Document doc = Jsoup.parse(html);
//图片url
Element divImg = doc.getElementById("fmimg");
Element img = divImg.getElementsByTag("img").get(0);
book.setImgUrl(img.attr("src"));
Element divInfo = doc.getElementById("info");
//书名
Element h1 = divInfo.getElementsByTag("h1").get(0);
book.setName(h1.html());
Elements ps = divInfo.getElementsByTag("p");
//作者
Element p0 = ps.get(0);
Element a = p0.getElementsByTag("a").get(0);
book.setAuthor(a.html());
//更新时间
Element p2 = ps.get(2);
Pattern pattern = Pattern.compile("更新时间:(.*)&nbsp;");
Matcher matcher = pattern.matcher(p2.html());
if (matcher.find()) {
book.setUpdateDate(matcher.group(1));
}
//最新章节
Element p3 = ps.get(3);
a = p3.getElementsByTag("a").get(0);
book.setNewestChapterTitle(a.attr("title"));
book.setNewestChapterUrl(book.getChapterUrl() + a.attr("href"));
//类型
String type = doc.select("meta[property=og:novel:category]").attr("content");
book.setType(type);
//简介
Element divIntro = doc.getElementById("intro");
book.setDesc(Html.fromHtml(divIntro.html()).toString());
return book;
}
}

@ -0,0 +1,12 @@
package xyz.fycz.myreader.crawler;
import xyz.fycz.myreader.greendao.entity.Book;
/**
* @author fengyue
* @date 2020/5/19 19:50
*/
public interface BookInfoCrawler {
String getCharset();
Book getBookInfo(String html, Book book);
}

@ -0,0 +1,121 @@
package xyz.fycz.myreader.crawler;
import android.text.Html;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.entity.Book;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 风月小说网html解析工具
*/
public class FYReadCrawler implements ReadCrawler{
public static final String NAME_SPACE = "https://novel.fycz.xyz";
public static final String NOVEL_SEARCH = "https://novel.fycz.xyz/search.html";
public static final String SEARCH_KEY = "keyword";
public static final String 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 String getSearchKey() {
return SEARCH_KEY;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Pattern pattern = Pattern.compile("<div class=\"read-content j_readContent\">[\\s\\S]*?</div>");
Matcher matcher = pattern.matcher(html);
if (matcher.find()) {
String content = Html.fromHtml(matcher.group(0)).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
Elements nodes = doc.getElementsByClass("cate-list");
Element node = nodes.get(0);
Elements divs = node.getElementsByTag("a");
for (int i = 0; i < divs.size(); i++) {
Chapter chapter = new Chapter();
Element div = divs.get(i);
chapter.setNumber(i);
chapter.setUrl(div.attr("href").replace("https://novel.fycz.xyz",""));
chapter.setTitle(div.getElementsByTag("span").get(0).text());
chapters.add(chapter);
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
Elements nodes = doc.getElementsByClass("secd-rank-list");
for (Element div : nodes) {
Book book = new Book();
book.setName(div.getElementsByTag("a").get(1).text());
book.setAuthor(div.getElementsByTag("a").get(2).text());
book.setType(div.getElementsByTag("a").get(3).text());
book.setNewestChapterTitle(div.getElementsByTag("a").get(4).text());
book.setNewestChapterUrl(div.getElementsByTag("a").get(4).attr("href"));
Element img = div.getElementsByTag("img").get(0);
book.setImgUrl(img.attr("data-original"));
Element chapterUrl = div.getElementsByTag("a").get(1);
book.setChapterUrl(chapterUrl.attr("href"));
book.setDesc(div.getElementsByTag("p").get(1).text());
book.setUpdateDate(div.getElementsByTag("span").get(1).text().replace("|", ""));
book.setSource(BookSource.fynovel.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
return books;
}
}

@ -0,0 +1,139 @@
package xyz.fycz.myreader.crawler;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import java.util.ArrayList;
/**
* @author fengyue
* @date 2020/5/19 19:50
*/
public class PinShuReadCrawler implements ReadCrawler, BookInfoCrawler {
public static final String NAME_SPACE = "https://www.vodtw.com";
public static final String NOVEL_SEARCH = "https://www.vodtw.com/Book/Search.aspx";
public static final String SEARCH_KEY = "SearchKey";
public static final String CHARSET = "gbk";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public String getSearchKey() {
return SEARCH_KEY;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("BookText");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ").replace("品书网", "")
.replace("手机阅读", "");
return StringHelper.IgnoreCaseReplace(content, "www.vodtw.com", "");
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
String readUrl = doc.select("meta[property=og:novel:read_url]")
.attr("content").replace("index.html", "");
Element divList = doc.getElementsByClass("insert_list").get(0);
String lastTile = null;
int i = 0;
Elements elementsByTag = divList.getElementsByTag("a");
for (Element a : elementsByTag) {
String title = a.text();
if (!StringHelper.isEmpty(lastTile) && title.equals(lastTile)) {
continue;
}
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
String url = readUrl + a.attr("href");
chapter.setUrl(url.replace(".la", ".com"));
chapters.add(chapter);
lastTile = title;
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
Element div = doc.getElementById("Content");
Elements elementsSelected = div.select("[id=CListTitle]");
for (Element element : elementsSelected) {
Book book = new Book();
Elements info = element.getElementsByTag("a");
book.setName(info.get(0).text());
book.setChapterUrl(NAME_SPACE + info.get(0).attr("href"));
book.setAuthor(info.get(1).text());
book.setType(info.get(3).text());
book.setNewestChapterTitle(info.get(4).text());
book.setSource(BookSource.pinshu.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
return books;
}
/**
* 获取书籍详细信息
*
* @param book
*/
public Book getBookInfo(String html, Book book) {
Document doc = Jsoup.parse(html);
Element img = doc.getElementsByClass("bookpic").get(0);
book.setImgUrl(img.getElementsByTag("img").get(0).attr("src"));
Element desc = doc.getElementsByClass("bookintro").get(0);
book.setDesc(desc.text());
return book;
}
}

@ -0,0 +1,145 @@
package xyz.fycz.myreader.crawler;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import java.util.ArrayList;
/**
* @author fengyue
* @date 2020/5/19 19:50
*/
public class PinShuReadCrawler2 implements ReadCrawler, BookInfoCrawler {
public static final String NAME_SPACE = "https://www.vodtw.la";
public static final String NOVEL_SEARCH = "https://www.vodtw.la/search.html";
public static final String SEARCH_KEY = "q";
public static final String 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 String getSearchKey() {
return SEARCH_KEY;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("BookText");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ").replace("品书网", "")
.replace("手机阅读", "");
return StringHelper.IgnoreCaseReplace(content, "www.vodtw.com", "");
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
String readUrl = doc.select("meta[property=og:novel:read_url]")
.attr("content").replace("index.html", "");
Element divList = doc.getElementsByClass("insert_list").get(0);
String lastTile = null;
int i = 0;
Elements elementsByTag = divList.getElementsByTag("a");
for (Element a : elementsByTag) {
String title = a.text();
if (!StringHelper.isEmpty(lastTile) && title.equals(lastTile)) {
continue;
}
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
String url = readUrl + a.attr("href");
chapter.setUrl(url);
chapters.add(chapter);
lastTile = title;
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
Element div = doc.getElementsByClass("booklist").first();
Elements divs = div.getElementsByClass("clearfix");
for (Element element : divs) {
Book book = new Book();
Elements info = element.getElementsByTag("a");
book.setName(info.get(1).text());
book.setChapterUrl(NAME_SPACE + info.get(0).attr("href"));
book.setSource(BookSource.pinshu.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
return books;
}
/**
* 获取书籍详细信息
*
* @param book
*/
public Book getBookInfo(String html, Book book) {
Document doc = Jsoup.parse(html);
Element img = doc.getElementsByClass("bookimg").get(0);
book.setImgUrl(NAME_SPACE + img.getElementsByTag("img").get(0).attr("src"));
String author = doc.select("meta[property=og:novel:author]").attr("content");
String type = doc.select("meta[property=og:novel:category]").attr("content");
String desc = doc.select("meta[property=og:description]").attr("content");
String newestChapterTitle = doc.select("meta[property=og:novel:latest_chapter_name]").attr("content");
book.setAuthor(author);
book.setType(type);
book.setDesc(desc);
book.setNewestChapterTitle(newestChapterTitle);
return book;
}
}

@ -0,0 +1,77 @@
package xyz.fycz.myreader.crawler;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.util.StringHelper;
import java.util.ArrayList;
import java.util.List;
/**
* @author fengyue
* @date 2020/5/27 11:17
*/
public class QiDianRankList {
public static final String[] TYPE_NAME = {
"月票榜", "畅销榜", "阅读榜", "推荐榜", "收藏榜"
};
/**
* 获取书城小说分类列表
*
* @param html
* @return
*/
public static List<BookType> getBookTypeList(String html) {
List<BookType> bookTypes = new ArrayList<>();
Document doc = Jsoup.parse(html);
Elements divs = doc.getElementsByClass("more");
for (int i = 0; i < 5; i++) {
BookType bookType = new BookType();
bookType.setTypeName(TYPE_NAME[i]);
bookType.setUrl("https:" + divs.get(i).attr("href"));
bookTypes.add(bookType);
}
return bookTypes;
}
/**
* 获取某一分类小说排行榜列表
*
* @param html
* @return
*/
public static List<Book> getBookRankList(String html) {
List<Book> books = new ArrayList<>();
Document doc = Jsoup.parse(html);
Elements lis = doc.getElementsByTag("li");
if (lis.size() > 0) {
Elements bookLis = lis.select("data-rid");
if (bookLis.size() > 0) {
for (Element li : bookLis) {
Book book = new Book();
String imgSrc = li.getElementsByClass("book-img-box").get(0)
.getElementsByTag("img").attr("src");
book.setImgUrl("https:" + imgSrc);
Element bookInfo = li.getElementsByClass("book-mid-info").get(0);
Elements tagA = bookInfo.getElementsByTag("a");
book.setName(tagA.get(0).text());
book.setAuthor(tagA.get(1).text());
book.setType(tagA.get(2).text());
book.setNewestChapterTitle(tagA.get(3).text());
String desc = bookInfo.getElementsByClass("intro").get(0).text();
book.setDesc(desc);
books.add(book);
}
}
}
return books;
}
}

@ -0,0 +1,23 @@
package xyz.fycz.myreader.crawler;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import java.util.ArrayList;
/**
* @author fengyue
* @date 2020/4/28 16:18
*/
public interface ReadCrawler {
String getSearchLink();
String getCharset();
String getNameSpace();
String getSearchKey();
String getContentFormHtml(String html);
ArrayList<Chapter> getChaptersFromHtml(String html);
ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html);
}

@ -0,0 +1,23 @@
package xyz.fycz.myreader.crawler;
import java.util.ResourceBundle;
/**
* @author fengyue
* @date 2020/5/17 11:45
*/
public class ReadCrawlerUtil {
private ReadCrawlerUtil() {
}
public static ReadCrawler getReadCrawler(String bookSource){
ResourceBundle rb = ResourceBundle.getBundle("bookcrawler");
try{
String readCrawlerPath = rb.getString(bookSource);
Class clz = Class.forName(readCrawlerPath);
return (ReadCrawler) clz.newInstance();
}catch (Exception e){
return new FYReadCrawler();
}
}
}

@ -0,0 +1,154 @@
package xyz.fycz.myreader.crawler;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import java.util.ArrayList;
/**
* 天籁小说网html解析工具
* Created by zhao on 2017/7/24.
*/
public class TianLaiReadCrawler implements ReadCrawler{
public static final String NAME_SPACE = "https://www.23txt.com";
public static final String NOVEL_SEARCH = "https://www.23txt.com/search.php";
public static final String SEARCH_KEY = "q";
public static final String CHARSET = "GBK";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public String getSearchKey() {
return SEARCH_KEY;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("content");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
content = content.replace("一秒记住【笔趣阁 www.52bqg.com】,精彩小说无弹窗免费阅读!", "");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
String readUrl = doc.select("meta[property=og:novel:read_url]").attr("content");
Element divList = doc.getElementById("list");
Element dl = divList.getElementsByTag("dl").get(0);
String lastTile = null;
int i = 0;
for(Element dd : dl.getElementsByTag("dd")){
Elements as = dd.getElementsByTag("a");
if (as.size() > 0) {
Element a = as.get(0);
String title = a.html();
if (!StringHelper.isEmpty(lastTile) && title.equals(lastTile)) {
continue;
}
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
String url = a.attr("href");
if (url.contains("files/article/html")) {
url = NAME_SPACE + url;
}else {
url = readUrl + url;
}
chapter.setUrl(url);
chapters.add(chapter);
lastTile = title;
}
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
// Element node = doc.getElementById("results");
// for (Element div : node.children()) {
Elements divs = doc.getElementsByClass("result-list");
Element div = divs.get(0);
// if (!StringHelper.isEmpty(div.className()) && div.className().equals("result-list")) {
for (Element element : div.children()) {
Book book = new Book();
Element img = element.child(0).child(0).child(0);
book.setImgUrl(img.attr("src"));
Element title = element.getElementsByClass("result-item-title result-game-item-title").get(0);
book.setName(title.child(0).attr("title"));
book.setChapterUrl(NAME_SPACE + title.child(0).attr("href"));
Element desc = element.getElementsByClass("result-game-item-desc").get(0);
book.setDesc(desc.text());
Element info = element.getElementsByClass("result-game-item-info").get(0);
for (Element element1 : info.children()) {
String infoStr = element1.text();
if (infoStr.contains("作者:")) {
book.setAuthor(infoStr.replace("作者:", "").replace(" ", ""));
} else if (infoStr.contains("类型:")) {
book.setType(infoStr.replace("类型:", "").replace(" ", ""));
} else if (infoStr.contains("更新时间:")) {
book.setUpdateDate(infoStr.replace("更新时间:", "").replace(" ", ""));
} else {
Element newChapter = element1.child(1);
book.setNewestChapterUrl(newChapter.attr("href"));
book.setNewestChapterTitle(newChapter.text());
}
}
book.setSource(BookSource.tianlai.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
return books;
}
}

@ -0,0 +1,199 @@
package xyz.fycz.myreader.creator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import androidx.core.content.FileProvider;
import android.view.View;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.home.MainActivity;
import xyz.fycz.myreader.ui.home.bookcase.BookcaseFragment;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.TextHelper;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.webapi.CommonApi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* @author fengyue
* @date 2020/5/20 20:50
*/
public class APPDownloadTip {
private String url;
private BookcaseFragment mBookcaseFragment;
private MainActivity activity;
private boolean isForceUpdate;
public APPDownloadTip(String url, BookcaseFragment mBookcaseFragment, MainActivity activity, boolean isForceUpdate) {
this.url = url;
this.mBookcaseFragment = mBookcaseFragment;
this.activity = activity;
this.isForceUpdate = isForceUpdate;
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
mBookcaseFragment.getTvDownloadTip().setText("获取下载链接失败,请前往浏览器下载!");
mBookcaseFragment.getRlDownloadTip().setVisibility(View.GONE);
break;
case 2:
mBookcaseFragment.getTvDownloadTip().setText("连接中...");
break;
case 3:
updateDownloadPro((double) msg.obj);
break;
case 4:
mBookcaseFragment.getRlDownloadTip().setVisibility(View.GONE);
break;
}
}
};
public void downloadApp(){
mBookcaseFragment.getTvStopDownload().setVisibility(View.GONE);
mBookcaseFragment.getRlDownloadTip().setVisibility(View.VISIBLE);
mBookcaseFragment.getPbDownload().setProgress(0);
mBookcaseFragment.getTvDownloadTip().setText("正在获取下载链接...");
CommonApi.getUrl(url, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
final String downloadUrl = (String) o;
if (downloadUrl == null) {
error();
return;
}
MyApplication.getApplication().newThread(new Runnable() {
@SuppressLint("SetTextI18n")
@Override
public void run() {
HttpURLConnection con = null;
InputStream is = null;
FileOutputStream fos = null;
File appFile = null;
try {
URL webUrl = new URL(downloadUrl);
mHandler.sendMessage(mHandler.obtainMessage(2));
con = (HttpURLConnection) webUrl.openConnection();
is = con.getInputStream();
String filePath = APPCONST.UPDATE_APK_FILE_DIR + "FYReader.apk.temp";
appFile = FileUtils.getFile(filePath);
fos = new FileOutputStream(appFile);
byte[] tem = new byte[1024];
long alreadyLen = 0;
long fileLength = con.getContentLength();
int len;
double progress;
while ((len = is.read(tem)) != -1) {
fos.write(tem, 0, len);
alreadyLen += len;
progress = alreadyLen * 1.0f * 100f / fileLength;
mHandler.sendMessage(mHandler.obtainMessage(3, progress));
}
fos.flush();
if (fileLength == appFile.length()) {
String newPath = filePath.replace(".temp", "");
final File newFile = new File(newPath);
if (appFile.renameTo(newFile)){
mHandler.sendMessage(mHandler.obtainMessage(4));
DialogCreator.createCommonDialog(activity, "提示", "风月读书下载完成,安装包路径:" + newPath,
!isForceUpdate, "取消", "立即安装", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (isForceUpdate){
activity.finish();
}
}
}, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
activity.installProcess(newFile, isForceUpdate);
}
});
activity.installProcess(newFile, isForceUpdate);
}else {
appFile.delete();
error();
}
}else {
appFile.delete();
error();
}
} catch (IOException e) {
if (appFile != null) {
appFile.delete();
}
error();
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
});
}
@Override
public void onError(Exception e) {
error();
}
});
}
private void error(){
mHandler.sendMessage(mHandler.obtainMessage(1));
TextHelper.showText("获取下载链接失败,请前往浏览器下载!");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
if (isForceUpdate) {
activity.finish();
}
}
@SuppressLint({"SetTextI18n"})
private void updateDownloadPro(double progress) {
mBookcaseFragment.getPbDownload().setProgress((int) progress);
//保留两位小数
//mBookcaseFragment.getTvDownloadTip().setText("正在下载风月读书最新版本...[" + String.format("%.2f", progress) + "%]");
mBookcaseFragment.getTvDownloadTip().setText("正在下载风月读书最新版本...[" + (int) progress + "%]");
}
}

@ -0,0 +1,143 @@
package xyz.fycz.myreader.creator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.crawler.*;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.CommonApi;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
/**
* @author fengyue
* @date 2020/5/19 17:35
*/
public class ChangeSourceDialog {
private Context context;
private Book mBook;
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks = new ConcurrentMultiValueMap<>();
private ArrayList<Book> aBooks;
private SearchBookBean sbb;
private boolean isSearchSuccess;
private int threadCount;
private ResultCallback rc;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
if (threadCount == 0) {
createaBooks();
}
break;
case 2:
if (!isSearchSuccess && threadCount == 0) {
rc.onError(new Exception());
}else if (threadCount == 0){
createaBooks();
}
break;
}
}
};
public ChangeSourceDialog(Context context, Book mBook) {
this.context = context;
this.mBook = mBook;
sbb = new SearchBookBean(mBook.getName(), mBook.getAuthor());
}
public void init(ResultCallback rc) {
this.rc = rc;
getData();
}
/**
* 获取搜索数据
*/
private void getData() {
mBooks.clear();
threadCount = 4;
isSearchSuccess = false;
searchBookByCrawler(new BiQuGe44ReadCrawler(), "");
searchBookByCrawler(new TianLaiReadCrawler(), "");
searchBookByCrawler(new BiQuGeReadCrawler(), "gbk");
searchBookByCrawler(new PinShuReadCrawler(), "gbk");
}
private void searchBookByCrawler(final ReadCrawler rc, String charset) {
String searchKey = mBook.getName();
if (charset.toLowerCase().equals("gbk")) {
try {
searchKey = URLEncoder.encode(mBook.getName(), charset);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
CommonApi.search(searchKey, rc, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
final ConcurrentMultiValueMap<SearchBookBean, Book> cmvm =
(ConcurrentMultiValueMap<SearchBookBean, Book>) o;
if (rc instanceof BookInfoCrawler) {
BookInfoCrawler bic = (BookInfoCrawler) rc;
final List<Book> aBooks = cmvm.getValues(sbb);
if (aBooks != null) {
for (int i = 0; i < aBooks.size(); i++) {
Book book = aBooks.get(i);
final int finalI = i;
CommonApi.getBookInfo(book, bic, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
if (finalI == aBooks.size() - 1) {
mBooks.addAll(cmvm);
isSearchSuccess = true;
threadCount--;
mHandler.sendMessage(mHandler.obtainMessage(1));
}
}
@Override
public void onError(Exception e) {
threadCount--;
mHandler.sendMessage(mHandler.obtainMessage(2));
}
});
}
} else {
isSearchSuccess = true;
threadCount--;
mHandler.sendMessage(mHandler.obtainMessage(1));
}
} else {
mBooks.addAll(cmvm);
isSearchSuccess = true;
threadCount--;
mHandler.sendMessage(mHandler.obtainMessage(1));
}
}
@Override
public void onError(Exception e) {
threadCount--;
mHandler.sendMessage(mHandler.obtainMessage(2));
}
});
}
private void createaBooks() {
aBooks = (ArrayList<Book>) mBooks.getValues(sbb);
rc.onFinish(aBooks, 1);
}
}

@ -0,0 +1,751 @@
package xyz.fycz.myreader.creator;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.enums.ReadStyle;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.util.BrightUtil;
import xyz.fycz.myreader.util.StringHelper;
import static xyz.fycz.myreader.enums.ReadStyle.blueDeep;
import static xyz.fycz.myreader.enums.ReadStyle.breen;
import static xyz.fycz.myreader.enums.ReadStyle.common;
import static xyz.fycz.myreader.enums.ReadStyle.leather;
import static xyz.fycz.myreader.enums.ReadStyle.protectedEye;
public class DialogCreator {
private static ImageView ivLastSelectd = null;
/**
* 阅读详细设置对话框
* @param context
* @param setting
* @param onReadStyleChangeListener
* @param reduceSizeListener
* @param increaseSizeListener
* @param languageChangeListener
* @param onFontClickListener
* @return
*/
public static Dialog createReadDetailSetting(final Context context, final Setting setting,
final OnReadStyleChangeListener onReadStyleChangeListener,
final View.OnClickListener reduceSizeListener,
final View.OnClickListener increaseSizeListener,
final View.OnClickListener languageChangeListener,
final View.OnClickListener onFontClickListener,
final OnPageModeChangeListener onModeClickListener,
View.OnClickListener autoScrollListener) {
final Dialog dialog = new Dialog(context, R.style.jmui_default_dialog_style);
final View view = LayoutInflater.from(context).inflate(R.layout.dialog_read_setting_detail, null);
dialog.setContentView(view);
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
//触摸外部关闭
view.findViewById(R.id.ll_bottom_view).setOnClickListener(null);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
dialog.dismiss();
return false;
}
});
//设置全屏
Window window = dialog.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//阅读背景风格
final ImageView ivCommonStyle = (ImageView) view.findViewById(R.id.iv_common_style);
final ImageView ivLeatherStyle = (ImageView) view.findViewById(R.id.iv_leather_style);
final ImageView ivProtectEyeStyle = (ImageView) view.findViewById(R.id.iv_protect_eye_style);
final ImageView ivBreenStyle = (ImageView) view.findViewById(R.id.iv_breen_style);
final ImageView ivBlueDeepStyle = (ImageView) view.findViewById(R.id.iv_blue_deep_style);
switch (setting.getReadStyle()) {
case common:
ivCommonStyle.setSelected(true);
ivLastSelectd = ivCommonStyle;
break;
case leather:
ivLeatherStyle.setSelected(true);
ivLastSelectd = ivLeatherStyle;
break;
case protectedEye:
ivProtectEyeStyle.setSelected(true);
ivLastSelectd = ivProtectEyeStyle;
break;
case breen:
ivBreenStyle.setSelected(true);
ivLastSelectd = ivBreenStyle;
break;
case blueDeep:
ivBlueDeepStyle.setSelected(true);
ivLastSelectd = ivBlueDeepStyle;
break;
}
ivCommonStyle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedStyle(ivCommonStyle, common, onReadStyleChangeListener);
}
});
ivLeatherStyle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedStyle(ivLeatherStyle, leather, onReadStyleChangeListener);
}
});
ivProtectEyeStyle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedStyle(ivProtectEyeStyle, protectedEye, onReadStyleChangeListener);
}
});
ivBlueDeepStyle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedStyle(ivBlueDeepStyle, blueDeep, onReadStyleChangeListener);
}
});
ivBreenStyle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedStyle(ivBreenStyle, breen, onReadStyleChangeListener);
}
});
//字体大小
TextView tvSizeReduce = (TextView) view.findViewById(R.id.tv_reduce_text_size);
TextView tvSizeIncrease = (TextView) view.findViewById(R.id.tv_increase_text_size);
final TextView tvSize = (TextView) view.findViewById(R.id.tv_text_size);
tvSize.setText(String.valueOf((int) setting.getReadWordSize()));
tvSizeReduce.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (setting.getReadWordSize() > 1) {
tvSize.setText(String.valueOf((int) setting.getReadWordSize() - 1));
if (reduceSizeListener != null) {
reduceSizeListener.onClick(v);
}
}
}
});
tvSizeIncrease.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (setting.getReadWordSize() < 41) {
tvSize.setText(String.valueOf((int) setting.getReadWordSize() + 1));
if (increaseSizeListener != null) {
increaseSizeListener.onClick(v);
}
}
}
});
//亮度调节
SeekBar seekBar = (SeekBar) view.findViewById(R.id.sb_brightness_progress);
final TextView tvBrightFollowSystem = (TextView) view.findViewById(R.id.tv_system_brightness);
seekBar.setProgress(setting.getBrightProgress());
tvBrightFollowSystem.setSelected(setting.isBrightFollowSystem());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
BrightUtil.setBrightness((AppCompatActivity) context, BrightUtil.progressToBright(progress));
tvBrightFollowSystem.setSelected(false);
setting.setBrightProgress(progress);
setting.setBrightFollowSystem(false);
SysManager.saveSetting(setting);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//亮度跟随系统
tvBrightFollowSystem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvBrightFollowSystem.setSelected(!tvBrightFollowSystem.isSelected());
if (tvBrightFollowSystem.isSelected()) {
BrightUtil.followSystemBright((AppCompatActivity) context);
setting.setBrightFollowSystem(true);
SysManager.saveSetting(setting);
} else {
BrightUtil.setBrightness((AppCompatActivity) context, BrightUtil.progressToBright(setting.getBrightProgress()));
setting.setBrightFollowSystem(false);
SysManager.saveSetting(setting);
}
}
});
//音量键翻页
final TextView tvIsVolumeTurnPage = (TextView) view.findViewById(R.id.tv_isVolumeTurnPage);
if (setting.isVolumeTurnPage()) {
tvIsVolumeTurnPage.setText("关");
} else {
tvIsVolumeTurnPage.setText("开");
}
tvIsVolumeTurnPage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (tvIsVolumeTurnPage.getText().toString().equals("关")) {
tvIsVolumeTurnPage.setText("开");
} else {
tvIsVolumeTurnPage.setText("关");
}
if (languageChangeListener != null) {
languageChangeListener.onClick(v);
}
}
});
//选择字体
TextView tvFont = (TextView)view.findViewById(R.id.tv_text_font);
tvFont.setOnClickListener(onFontClickListener);
//选择翻页模式
TextView tvMode = view.findViewById(R.id.tv_page_mode);
tvMode.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onModeClickListener != null){
onModeClickListener.onChange((TextView) v);
}
}
});
switch (setting.getPageMode()) {
case COVER:
tvMode.setText("覆盖");
break;
case SIMULATION:
tvMode.setText("仿真");
break;
case SLIDE:
tvMode.setText("滑动");
break;
case SCROLL:
tvMode.setText("滚动");
break;
case NONE:
tvMode.setText("无");
break;
}
//自动滚屏速度
SeekBar sbScrollSpeed = view.findViewById(R.id.sb_auto_scroll_progress);
TextView tvAutoScroll = view.findViewById(R.id.tv_auto_scroll);
TextView tvAutoScrollSpeed = view.findViewById(R.id.tv_auto_scroll_speed);
sbScrollSpeed.setProgress(setting.getAutoScrollSpeed() / 3);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
sbScrollSpeed.setMin(100);
}
tvAutoScrollSpeed.setText("每分钟阅读字数(CPM):" + setting.getAutoScrollSpeed() + "CPM");
sbScrollSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int speed = progress == 0 ? 300 : progress * 3;
setting.setAutoScrollSpeed(speed);
tvAutoScrollSpeed.setText("每分钟阅读字数(CPM):" + speed + "CPM");
SysManager.saveSetting(setting);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
tvAutoScroll.setOnClickListener(autoScrollListener);
return dialog;
}
private static void selectedStyle(ImageView curSelected, ReadStyle readStyle, OnReadStyleChangeListener listener) {
ivLastSelectd.setSelected(false);
ivLastSelectd = curSelected;
curSelected.setSelected(true);
if (listener != null) {
listener.onChange(readStyle);
}
}
/**
* 阅读设置对话框
*
* @param context
* @param isDayStyle
* @param chapterProgress
* @param backListener
* @param lastChapterListener
* @param nextChapterListener
* @param chapterListListener
* @param onClickNightAndDayListener
* @param settingListener
* @return
*/
public static Dialog createReadSetting(final Context context, final boolean isDayStyle, int chapterProgress, int maxProcess, final Book mBook, Chapter mChapter,
View.OnClickListener backListener,
View.OnClickListener refreshListener,
View.OnClickListener bookMarkListener,
final OnSkipChapterListener lastChapterListener,
final OnSkipChapterListener nextChapterListener,
View.OnClickListener chapterListListener,
final OnClickNightAndDayListener onClickNightAndDayListener,
View.OnClickListener settingListener,
SeekBar.OnSeekBarChangeListener onSeekBarChangeListener,
View.OnClickListener voiceOnClickListener,
final OnClickDownloadAllChapterListener onClickDownloadAllChapterListener) {
final Dialog dialog = new Dialog(context, R.style.jmui_default_dialog_style);
final View view = LayoutInflater.from(context).inflate(R.layout.dialog_read_setting, null);
dialog.setContentView(view);
LinearLayout llBack = (LinearLayout) view.findViewById(R.id.ll_title_back);
LinearLayout llBook = view.findViewById(R.id.ll_book_name);
TextView tvBookName = view.findViewById(R.id.tv_book_name_top);
ImageView ivRefresh = view.findViewById(R.id.iv_refresh);
ImageView ivBookMark = view.findViewById(R.id.iv_book_mark);
ImageView ivMore = view.findViewById(R.id.iv_more);
LinearLayout llChapter = view.findViewById(R.id.ll_chapter_view);
final TextView tvChapterTitle = view.findViewById(R.id.tv_chapter_title_top);
final TextView tvChapterUrl = view.findViewById(R.id.tv_chapter_url);
TextView tvLastChapter = (TextView) view.findViewById(R.id.tv_last_chapter);
TextView tvNextChapter = (TextView) view.findViewById(R.id.tv_next_chapter);
final SeekBar sbChapterProgress = (SeekBar) view.findViewById(R.id.sb_read_chapter_progress);
LinearLayout llChapterList = (LinearLayout) view.findViewById(R.id.ll_chapter_list);
LinearLayout llNightAndDay = (LinearLayout) view.findViewById(R.id.ll_night_and_day);
LinearLayout llSetting = (LinearLayout) view.findViewById(R.id.ll_setting);
final ImageView ivNightAndDay = (ImageView) view.findViewById(R.id.iv_night_and_day);
final TextView tvNightAndDay = (TextView) view.findViewById(R.id.tv_night_and_day);
ImageView ivVoice = (ImageView)view.findViewById(R.id.iv_voice_read);
view.findViewById(R.id.rl_title_view).setOnClickListener(null);
view.findViewById(R.id.ll_bottom_view).setOnClickListener(null);
Window window = dialog.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
if (Build.VERSION.SDK_INT >= 21) {
window.setStatusBarColor(dialog.getContext().getResources().getColor(R.color.sys_dialog_setting_bg));
}
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
dialog.dismiss();
return false;
}
});
tvBookName.setText(mBook.getName());
llBook.setOnClickListener(v -> {
/*Intent intent = new Intent(context, BookInfoActivity.class);
intent.putExtra(APPCONST.BOOK, mBook);
context.startActivity(intent);*/
});
//刷新
ivRefresh.setOnClickListener(refreshListener);
String url = mChapter.getUrl();
if ("null".equals(mBook.getSource()) || BookSource.fynovel.equals(mBook.getSource())) {
if (!url.contains("novel.fycz.xyz")) {
url = URLCONST.nameSpace_FY + url;
}
}
//书签
ivBookMark.setOnClickListener(bookMarkListener);
tvChapterTitle.setText(mChapter.getTitle());
tvChapterUrl.setText(StringHelper.isEmpty(url) ? mChapter.getId() : url);
//跳转对应章节
llChapter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String url = tvChapterUrl.getText().toString();
if (!"本地书籍".equals(mBook.getType()) && !StringHelper.isEmpty(url)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse(url);
intent.setData(uri);
context.startActivity(intent);
}
}
});
if (!isDayStyle) {
ivNightAndDay.setImageResource(R.mipmap.z4);
tvNightAndDay.setText(context.getString(R.string.day));
}
llBack.setOnClickListener(backListener);
tvLastChapter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (lastChapterListener != null){
lastChapterListener.onClick(tvChapterTitle, tvChapterUrl, sbChapterProgress);
}
}
});
tvNextChapter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (nextChapterListener != null){
nextChapterListener.onClick(tvChapterTitle, tvChapterUrl, sbChapterProgress);
}
}
});
sbChapterProgress.setProgress(chapterProgress);
sbChapterProgress.setMax(maxProcess);
llChapterList.setOnClickListener(chapterListListener);
llSetting.setOnClickListener(settingListener);
sbChapterProgress.setOnSeekBarChangeListener(onSeekBarChangeListener);
ivVoice.setOnClickListener(voiceOnClickListener);
//日夜切换
llNightAndDay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean isDay;
if (tvNightAndDay.getText().toString().equals(context.getString(R.string.day))) {
isDay = false;
ivNightAndDay.setImageResource(R.mipmap.ao);
tvNightAndDay.setText(context.getString(R.string.night));
} else {
isDay = true;
ivNightAndDay.setImageResource(R.mipmap.z4);
tvNightAndDay.setText(context.getString(R.string.day));
}
if (onClickNightAndDayListener != null) {
onClickNightAndDayListener.onClick(dialog, view, isDay);
}
}
});
//缓存章节
final TextView tvDownloadProgress = (TextView)view.findViewById(R.id.tv_download_progress);
LinearLayout llDonwloadCache = (LinearLayout)view.findViewById(R.id.ll_download_cache);
llDonwloadCache.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onClickDownloadAllChapterListener != null){
onClickDownloadAllChapterListener.onClick(dialog,v,tvDownloadProgress);
}
}
});
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
return dialog;
}
/**
* 创建一个普通对话框包含确定取消按键
*
* @param context
* @param title
* @param mesage
* @param isCancelable 是否允许返回键取消
* @param positiveListener 确定按键动作
* @param negativeListener 取消按键动作
* @return
*/
public static AlertDialog createCommonDialog(Context context, String title, String mesage, boolean isCancelable,
DialogInterface.OnClickListener positiveListener, DialogInterface.OnClickListener negativeListener) {
final AlertDialog.Builder normalDialog = new AlertDialog.Builder(context);
// normalDialog.setIcon(R.drawable.icon_dialog);
normalDialog.setTitle(title);
normalDialog.setCancelable(isCancelable);
normalDialog.setMessage(mesage);
normalDialog.setPositiveButton("确定", positiveListener);
normalDialog.setNegativeButton("取消", negativeListener);
// 显示
final AlertDialog alertDialog = normalDialog.create();
MyApplication.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
alertDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
return alertDialog;
}
/**
* 创建一个普通对话框包含key1key2按键
*
* @param context
* @param title
* @param mesage
* @param key1
* @param key2
* @param positiveListener key1按键动作
* @param negativeListener key2按键动作
*/
public static void createCommonDialog(Context context, String title, String mesage, boolean isCancelable,
String key1, String key2,
DialogInterface.OnClickListener positiveListener,
DialogInterface.OnClickListener negativeListener) {
try {
final AlertDialog.Builder normalDialog = new AlertDialog.Builder(context);
// normalDialog.setIcon(R.drawable.icon_dialog);
normalDialog.setTitle(title);
normalDialog.setCancelable(isCancelable);
if (mesage != null) {
normalDialog.setMessage(mesage);
}
normalDialog.setPositiveButton(key1, positiveListener);
normalDialog.setNegativeButton(key2, negativeListener);
// 显示
// final AlertDialog alertDialog = normalDialog.create();
MyApplication.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
// final AlertDialog alertDialog = normalDialog.create();
normalDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
// return alertDialog;
}
/**
* 单按键对话框
*
* @param context
* @param title
* @param mesage
* @param key
* @param positiveListener
*/
public static void createCommonDialog(Context context, String title, String mesage, boolean isCancelable,
String key, DialogInterface.OnClickListener positiveListener
) {
try {
final AlertDialog.Builder normalDialog = new AlertDialog.Builder(context);
// normalDialog.setIcon(R.drawable.icon_dialog);
normalDialog.setTitle(title);
normalDialog.setCancelable(isCancelable);
if (mesage != null) {
normalDialog.setMessage(mesage);
}
normalDialog.setPositiveButton(key, positiveListener);
// 显示
// final AlertDialog alertDialog = normalDialog.create();
MyApplication.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
// final AlertDialog alertDialog = normalDialog.create();
normalDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
// return alertDialog;
}
/**
* 创建一个进度对话框圆形旋转
*
* @param context
* @param title
* @param message
* @return
*/
public static ProgressDialog createProgressDialog
(Context context, String title, String message/*,
DialogInterface.OnClickListener positiveListener,DialogInterface.OnClickListener negativeListener*/) {
final ProgressDialog progressDialog = new ProgressDialog(context);
// normalDialog.setIcon(R.drawable.icon_dialog);
if (!StringHelper.isEmpty(title)) {
progressDialog.setTitle(title);
}
if (!StringHelper.isEmpty(message)) {
progressDialog.setMessage(message);
}
progressDialog.setCancelable(false);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
/* progressDialog.setPositiveButton("确定",positiveListener);
progressDialog.setNegativeButton("取消",negativeListener);*/
// 显示
MyApplication.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
progressDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
return progressDialog;
}
/**
* 三按键对话框
*
* @param context
* @param title
* @param msg
* @param btnText1
* @param btnText2
* @param btnText3
* @param positiveListener
* @param neutralListener
* @param negativeListener
* @return
*/
public static void createThreeButtonDialog(Context context, String title, String msg, boolean isCancelable,
String btnText1, String btnText2, String btnText3,
DialogInterface.OnClickListener neutralListener,
DialogInterface.OnClickListener negativeListener,
DialogInterface.OnClickListener positiveListener) {
/* final EditText et = new EditText(context);*/
try {
final AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle(title);
if (!StringHelper.isEmpty(msg)) {
dialog.setMessage(msg);
}
// 第一个按钮
dialog.setNeutralButton(btnText1, neutralListener);
// 中间的按钮
dialog.setNegativeButton(btnText2, negativeListener);
// 第三个按钮
dialog.setPositiveButton(btnText3, positiveListener);
MyApplication.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
// final AlertDialog alertDialog = normalDialog.create();
dialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
dialog.setCancelable(isCancelable);
// Diglog的显示
}catch (Exception e){
e.printStackTrace();
}
}
public static void createTipDialog(Context mContext, String message){
DialogCreator.createCommonDialog(mContext, "提示",
message, true, "知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
public static void createTipDialog(Context mContext, String title,String message){
DialogCreator.createCommonDialog(mContext, title,
message, true, "知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
public interface OnClickPositiveListener {
void onClick(Dialog dialog, View view);
}
public interface OnClickNegativeListener {
void onClick(Dialog dialog, View view);
}
/**
* 白天黑夜切换监听
*/
public interface OnClickNightAndDayListener {
void onClick(Dialog dialog, View view, boolean isDayStyle);
}
/**
* 阅读style切换监听器
*/
public interface OnReadStyleChangeListener {
void onChange(ReadStyle readStyle);
}
public interface OnBrightFollowSystemChangeListener {
void onChange(boolean isFollowSystem);
}
public interface OnClickDownloadAllChapterListener {
void onClick(Dialog dialog, View view,TextView tvDownloadProgress);
}
public interface OnPageModeChangeListener {
void onChange(TextView tvPageMode);
}
public interface OnSkipChapterListener{
void onClick(TextView chapterTitle, TextView chapterUrl, SeekBar sbReadChapterProgress);
}
}

@ -0,0 +1,12 @@
package xyz.fycz.myreader.creator;
/**
* @author fengyue
* @date 2020/7/15 8:02
*/
public class ListenerInterface {
public interface OnRefreshFinishListener{
void onRefresh();
void onFinish();
}
}

@ -0,0 +1,113 @@
package xyz.fycz.myreader.custom;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Xfermode;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import androidx.appcompat.widget.AppCompatImageView;
import java.lang.ref.WeakReference;
public abstract class BaseImageView extends AppCompatImageView {
private static final String TAG = BaseImageView.class.getSimpleName();
protected Context mContext;
private static final Xfermode sXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
// private BitmapShader mBitmapShader;
private Bitmap mMaskBitmap;
private Paint mPaint;
private WeakReference<Bitmap> mWeakBitmap;
public BaseImageView(Context context) {
super(context);
sharedConstructor(context);
}
public BaseImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructor(context);
}
public BaseImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
sharedConstructor(context);
}
private void sharedConstructor(Context context) {
mContext = context;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
public void invalidate() {
mWeakBitmap = null;
if (mMaskBitmap != null) { mMaskBitmap.recycle(); }
super.invalidate();
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
if (!isInEditMode()) {
int i = canvas.saveLayer(0.0f, 0.0f, getWidth(), getHeight(),
null, Canvas.ALL_SAVE_FLAG);
try {
Bitmap bitmap = mWeakBitmap != null ? mWeakBitmap.get() : null;
// Bitmap not loaded.
if (bitmap == null || bitmap.isRecycled()) {
Drawable drawable = getDrawable();
if (drawable != null) {
// Allocation onDraw but it's ok because it will not always be called.
bitmap = Bitmap.createBitmap(getWidth(),
getHeight(), Bitmap.Config.ARGB_8888);
Canvas bitmapCanvas = new Canvas(bitmap);
drawable.setBounds(0, 0, getWidth(), getHeight());
drawable.draw(bitmapCanvas);
// If mask is already set, skip and use cached mask.
if (mMaskBitmap == null || mMaskBitmap.isRecycled()) {
mMaskBitmap = getBitmap();
}
// Draw Bitmap.
mPaint.reset();
mPaint.setFilterBitmap(false);
mPaint.setXfermode(sXfermode);
// mBitmapShader = new BitmapShader(mMaskBitmap,
// Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
// mPaint.setShader(mBitmapShader);
bitmapCanvas.drawBitmap(mMaskBitmap, 0.0f, 0.0f, mPaint);
mWeakBitmap = new WeakReference<Bitmap>(bitmap);
}
}
// Bitmap already loaded.
if (bitmap != null) {
mPaint.setXfermode(null);
// mPaint.setShader(null);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
return;
}
} catch (Exception e) {
System.gc();
Log.e(TAG, String.format("Failed to draw, Id :: %s. Error occurred :: %s", getId(), e.toString()));
} finally {
canvas.restoreToCount(i);
}
} else {
super.onDraw(canvas);
}
}
public abstract Bitmap getBitmap();
}

@ -0,0 +1,365 @@
/*
* Copyright 2014 - 2015 Henning Dodenhof
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.fycz.myreader.custom;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.widget.ImageView;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import xyz.fycz.myreader.R;
public class CircleImageView extends androidx.appcompat.widget.AppCompatImageView {
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 2;
private static final int DEFAULT_BORDER_WIDTH = 0;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
private static final boolean DEFAULT_BORDER_OVERLAY = false;
private final RectF mDrawableRect = new RectF();
private final RectF mBorderRect = new RectF();
private final Matrix mShaderMatrix = new Matrix();
private final Paint mBitmapPaint = new Paint();
private final Paint mBorderPaint = new Paint();
private final Paint mFillPaint = new Paint();
private int mBorderColor = DEFAULT_BORDER_COLOR;
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
private int mFillColor = DEFAULT_FILL_COLOR;
private Bitmap mBitmap;
private BitmapShader mBitmapShader;
private int mBitmapWidth;
private int mBitmapHeight;
private float mDrawableRadius;
private float mBorderRadius;
private ColorFilter mColorFilter;
private boolean mReady;
private boolean mSetupPending;
private boolean mBorderOverlay;
public CircleImageView(Context context) {
super(context);
init();
}
public CircleImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);
a.recycle();
init();
}
private void init() {
super.setScaleType(SCALE_TYPE);
mReady = true;
if (mSetupPending) {
setup();
mSetupPending = false;
}
}
@Override
public ScaleType getScaleType() {
return SCALE_TYPE;
}
@Override
public void setScaleType(ScaleType scaleType) {
if (scaleType != SCALE_TYPE) {
throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
}
}
@Override
public void setAdjustViewBounds(boolean adjustViewBounds) {
if (adjustViewBounds) {
throw new IllegalArgumentException("adjustViewBounds not supported.");
}
}
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap == null) {
return;
}
if (mFillColor != Color.TRANSPARENT) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
}
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint);
if (mBorderWidth != 0) {
canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setup();
}
public int getBorderColor() {
return mBorderColor;
}
public void setBorderColor(@ColorInt int borderColor) {
if (borderColor == mBorderColor) {
return;
}
mBorderColor = borderColor;
mBorderPaint.setColor(mBorderColor);
invalidate();
}
public void setBorderColorResource(@ColorRes int borderColorRes) {
setBorderColor(getContext().getResources().getColor(borderColorRes));
}
public int getFillColor() {
return mFillColor;
}
public void setFillColor(@ColorInt int fillColor) {
if (fillColor == mFillColor) {
return;
}
mFillColor = fillColor;
mFillPaint.setColor(fillColor);
invalidate();
}
public void setFillColorResource(@ColorRes int fillColorRes) {
setFillColor(getContext().getResources().getColor(fillColorRes));
}
public int getBorderWidth() {
return mBorderWidth;
}
public void setBorderWidth(int borderWidth) {
if (borderWidth == mBorderWidth) {
return;
}
mBorderWidth = borderWidth;
setup();
}
public boolean isBorderOverlay() {
return mBorderOverlay;
}
public void setBorderOverlay(boolean borderOverlay) {
if (borderOverlay == mBorderOverlay) {
return;
}
mBorderOverlay = borderOverlay;
setup();
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
mBitmap = bm;
setup();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
mBitmap = getBitmapFromDrawable(drawable);
setup();
}
@Override
public void setImageResource(@DrawableRes int resId) {
super.setImageResource(resId);
mBitmap = getBitmapFromDrawable(getDrawable());
setup();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
setup();
}
@Override
public void setColorFilter(ColorFilter cf) {
if (cf == mColorFilter) {
return;
}
mColorFilter = cf;
applyColorFilter();
invalidate();
}
@Override
public ColorFilter getColorFilter() {
return mColorFilter;
}
private void applyColorFilter() {
if (mBitmapPaint != null) {
mBitmapPaint.setColorFilter(mColorFilter);
}
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
try {
Bitmap bitmap;
if (drawable instanceof ColorDrawable) {
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void setup() {
if (!mReady) {
mSetupPending = true;
return;
}
if (getWidth() == 0 && getHeight() == 0) {
return;
}
if (mBitmap == null) {
invalidate();
return;
}
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setShader(mBitmapShader);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
mFillPaint.setStyle(Paint.Style.FILL);
mFillPaint.setAntiAlias(true);
mFillPaint.setColor(mFillColor);
mBitmapHeight = mBitmap.getHeight();
mBitmapWidth = mBitmap.getWidth();
mBorderRect.set(0, 0, getWidth(), getHeight());
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
mDrawableRect.set(mBorderRect);
if (!mBorderOverlay) {
mDrawableRect.inset(mBorderWidth, mBorderWidth);
}
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
applyColorFilter();
updateShaderMatrix();
invalidate();
}
private void updateShaderMatrix() {
float scale;
float dx = 0;
float dy = 0;
mShaderMatrix.set(null);
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / (float) mBitmapHeight;
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
} else {
scale = mDrawableRect.width() / (float) mBitmapWidth;
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
}
mShaderMatrix.setScale(scale, scale);
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
mBitmapShader.setLocalMatrix(mShaderMatrix);
}
}

@ -0,0 +1,201 @@
package xyz.fycz.myreader.custom;
/**
* Created by zhao on 2017/2/10.
*/
import android.content.Context;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.widget.AppCompatEditText;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ContainsEmojiEditText extends AppCompatEditText {
//输入表情前的光标位置
private int cursorPos;
//输入表情前EditText中的文本
private String inputAfterText;
//是否重置了EditText的内容
private boolean resetText;
private ArrayList<TextWatcher> mListeners = null;
@Override
public void addTextChangedListener(TextWatcher watcher)
{
if (mListeners == null)
{
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
super.addTextChangedListener(watcher);
}
@Override
public void removeTextChangedListener(TextWatcher watcher)
{
if (mListeners != null)
{
int i = mListeners.indexOf(watcher);
if (i >= 0)
{
mListeners.remove(i);
}
}
super.removeTextChangedListener(watcher);
}
public void clearTextChangedListeners()
{
if(mListeners != null)
{
for(TextWatcher watcher : mListeners)
{
super.removeTextChangedListener(watcher);
}
mListeners.clear();
mListeners = null;
}
}
private Context mContext;
public ContainsEmojiEditText(Context context) {
super(context);
this.mContext = context;
initEditText();
}
public ContainsEmojiEditText(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initEditText();
}
public ContainsEmojiEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initEditText();
}
// 初始化edittext 控件
private void initEditText() {
addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int before, int count) {
if (!resetText) {
cursorPos = getSelectionEnd();
// 这里用s.toString()而不直接用s是因为如果用s,
// 那么,inputAfterText和s在内存中指向的是同一个地址,s改变了,
// inputAfterText也就改变了,那么表情过滤就失败了
inputAfterText= s.toString();
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try {
if (!resetText) {
if (count >= 2) {//表情符号的字符长度最小为2
CharSequence input = s.subSequence(cursorPos, cursorPos + count);
if (containsEmoji(input.toString())) {
resetText = true;
Toast.makeText(mContext, "不支持输入Emoji表情符号", Toast.LENGTH_SHORT).show();
//是表情符号就将文本还原为输入表情符号之前的内容
setText(inputAfterText);
CharSequence text = getText();
if (/*text instanceof Spannable*/ text != null) {
Spannable spanText = (Spannable) text;
Selection.setSelection(spanText, text.length());
}
}
}
} else {
resetText = false;
}
}catch (Exception e){
e.printStackTrace();
resetText = false;
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
}
public void setAfterTextChanged(EditText editText){
}
/**
* 检测是否有emoji表情
*
* @param source
* @return
*/
public static boolean containsEmoji(String source) {
int len = source.length();
for (int i = 0; i < len; i++) {
char codePoint = source.charAt(i);
if (!isEmojiCharacter(codePoint)) { //如果不能匹配,则该字符是Emoji表情
return true;
}
}
return false;
}
/**
* 判断是否是Emoji
*
* @param codePoint 比较的单个字符
* @return
*/
private static boolean isEmojiCharacter(char codePoint) {
return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) ||
(codePoint == 0xD) || ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000)
&& (codePoint <= 0x10FFFF));
}
public String getInputAfterText() {
return inputAfterText;
}
public boolean isResetText() {
return resetText;
}
public boolean isEmoji(String string) {
Pattern p = Pattern.compile("/[\u1F60-\u1F64]|[\u2702-\u27B0]|[\u1F68-\u1F6C]|[\u1F30-\u1F70]|[\u2600-\u26ff]/g");
Matcher m = p.matcher(string);
return m.matches();
}
public boolean isEmoji2(String string) {
Pattern p = Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]",
Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(string);
return m.find();
}
}

@ -0,0 +1,41 @@
package xyz.fycz.myreader.custom;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
/**
* Created by zhao on 2017/5/15.
*/
public abstract class DragAdapter extends BaseAdapter {
/**
*
* @描述:当从from排序被拖到to排序时的处理方式,请对相应的数据做处理
*
* @param from
* @param to
* @作者 [pWX273343] 2015年6月24日
*/
public abstract void onDataModelMove(int from, int to);
/**
* 复制View使用的方法,默认直接使用getView方法获取
* @param position
* @param convertView
* @param parent
* @return
*/
public View copyView(int position, View convertView, ViewGroup parent) {
return null;
}
/**
* 是否启用copyView方法
* @return true 使用copyView复制 false 使用getView直接获取镜像
*/
public boolean isUseCopyView() {
return false;
}
}

@ -0,0 +1,134 @@
package xyz.fycz.myreader.custom;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.animation.BounceInterpolator;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import xyz.fycz.myreader.util.DipPxUtil;
public class DragFloatBtnT extends FloatingActionButton {
//移动后的xy坐标
private float mLastRawx;
private float mLastRawy;
private boolean isDrug = false;
private int mRootMeasuredWidth;
private int padding;
public DragFloatBtnT(Context context) {
super(context);
init();
}
public DragFloatBtnT(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DragFloatBtnT(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
padding = DipPxUtil.dp2px(getContext(), 15);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent ev) {
//当前手指的坐标
float mRawx = ev.getRawX();
float mRawy = ev.getRawY();
ViewGroup mViewGroup = (ViewGroup) getParent();
switch (ev.getAction()) {
//手指按下
case MotionEvent.ACTION_DOWN:
setPressed(true);
isDrug = false;
//记录按下的位置
mLastRawx = mRawx;
mLastRawy = mRawy;
if (mViewGroup != null) {
int[] location = new int[2];
mViewGroup.getLocationInWindow(location);
//获取父布局的宽度
mRootMeasuredWidth = mViewGroup.getMeasuredWidth();
}
break;
//手指滑动
case MotionEvent.ACTION_MOVE:
if (mViewGroup != null) {
int[] location = new int[2];
mViewGroup.getLocationInWindow(location);
//获取父布局的高度
int mRootMeasuredHeight = mViewGroup.getMeasuredHeight();
mRootMeasuredWidth = mViewGroup.getMeasuredWidth();
//获取父布局顶点的坐标
int rootTopy = location[1];
if (mRawx >= 0 && mRawx <= mRootMeasuredWidth && mRawy >= rootTopy && mRawy <= (mRootMeasuredHeight + rootTopy)) {
//手指X轴滑动距离
float differenceValuex = mRawx - mLastRawx;
//手指Y轴滑动距离
float differenceValuey = mRawy - mLastRawy;
//判断是否为拖动操作
if (!isDrug) {
isDrug = !(Math.sqrt(differenceValuex * differenceValuex + differenceValuey * differenceValuey) < 2);
}
//获取手指按下的距离与控件本身X轴的距离
float ownx = getX();
//获取手指按下的距离与控件本身Y轴的距离
float owny = getY();
//理论中X轴拖动的距离
float endx = ownx + differenceValuex;
//理论中Y轴拖动的距离
float endy = owny + differenceValuey;
//X轴可以拖动的最大距离
float maxx = mRootMeasuredWidth - getWidth();
//Y轴可以拖动的最大距离
float maxy = mRootMeasuredHeight - getHeight();
//X轴边界限制
endx = endx < 0 ? 0 : Math.min(endx, maxx);
//Y轴边界限制
endy = endy < 0 ? 0 : Math.min(endy, maxy);
//开始移动
setX(endx);
setY(endy);
//记录位置
mLastRawx = mRawx;
mLastRawy = mRawy;
}
}
break;
//手指离开
case MotionEvent.ACTION_UP:
//恢复按压效果
setPressed(false);
float center = mRootMeasuredWidth / 2;
//自动贴边
if (mLastRawx <= center) {
//向左贴边
DragFloatBtnT.this.animate()
.setInterpolator(new BounceInterpolator())
.setDuration(500)
.x(padding + 20)
.start();
} else {
//向右贴边
DragFloatBtnT.this.animate()
.setInterpolator(new BounceInterpolator())
.setDuration(500)
.x(mRootMeasuredWidth - getWidth() - padding - 20)
.start();
}
break;
default:
}
//是否拦截事件
return isDrug ? isDrug : super.onTouchEvent(ev);
}
}

@ -0,0 +1,767 @@
package xyz.fycz.myreader.custom;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ScrollView;
import xyz.fycz.myreader.R;
import java.util.ArrayList;
import java.util.List;
/**
* Copyright (C), 2008-2015, Huawei Tech. Co., Ltd.
* <p/>
* Description : 拖动排序布局
*
* @version V100R001
* @since V100R001
*/
@SuppressLint({"NewApi", "Override"})
public class DragSortGridView extends FrameLayout {
protected NoScrollGridView mGridView;
private ScrollView mScrollView;
private int headDragPosition = 0;
private int footDragPosition = 0;
private FrameLayout mDragFrame;
private View mCopyView, hideView;
private GestureDetector detector;
private ViewGroup touchClashparent;
private boolean isLongOnClick;
/**
* 动画时间
*/
private static final long ANIM_DURING = 250;
protected int mNumColumns = 3, mColHeight = 0, mColWidth = 0, mChildCount = 0, mMaxHeight = 0;
private int currentDragPosition = -1;
private DragAdapter adapter;
/**
* 持有子view
*/
private List<View> mChilds = new ArrayList<View>();
private static final int TAG_KEY = R.id.first;
// private static final int TAG_KEY = R.id.tag_key;
private int mCurrentY = 0;
/**
* 触摸区域,0不滚动区域,1可向上滚动的区域,-1可向下滚动的区域
*/
private int mTouchArea = 0;
/**
* gridview能否滚动,是否内容太多
*/
private boolean canScroll = true;
/**
* 是否可以拖动,点击拖动策略下直接开启,长按拖动需要长按以后开启
*/
private boolean isDragable = true;
/**
* 自动滚屏的动画
*/
private ValueAnimator animator;
/**
* view是否加载完成,如果未加载完成,没有宽高,无法接受事件
*/
private boolean isViewInitDone = false;
/**
* 是否有位置发生改变,否则不用重绘
*/
private boolean hasPositionChange = false;
/**
* 适配器的观察者,观察适配器的数据改变
*/
private DataSetObserver observer = new DataSetObserver() {
@Override
public void onChanged() {
mChildCount = adapter.getCount();
// 下列属性状态清除,才会在被调用notifyDataSetChange时,在gridview测量布局完成后重新获取
mChilds.clear();
mColHeight = mColWidth = mMaxHeight = 0;
isViewInitDone = false;
}
@Override
public void onInvalidated() {
mChildCount = adapter.getCount();
}
};
private float[] lastLocation = null;
/**
* 手势监听器,滚动和单击
*/
private GestureDetector.SimpleOnGestureListener simpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (hasSendDragMsg) {
hasSendDragMsg = false;
handler.removeMessages(0x123);
}
if (isDragable && mCopyView != null) {// 可以拖动,实现跟随手指的拖动效果
// /// 2015/11/27补充修正跟随手指移动方法,适用于当本控件在drag时同时滚动的情况
if (lastLocation == null) {
lastLocation = new float[]{e1.getRawX(), e1.getRawY()};
}
distanceX = lastLocation[0] - e2.getRawX();
distanceY = lastLocation[1] - e2.getRawY();
lastLocation[0] = e2.getRawX();
lastLocation[1] = e2.getRawY();
// ////////
mCopyView.setX(mCopyView.getX() - distanceX);
mCopyView.setY(mCopyView.getY() - distanceY);
mCopyView.invalidate();
int to = eventToPosition(e2);
if (to != currentDragPosition && to >= headDragPosition && to < mChildCount - footDragPosition) {
onDragPositionChange(currentDragPosition, to);
}
}
return true;
}
@Override
public void onShowPress(final MotionEvent e) {
/** 响应长按拖拽 */
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isLongOnClick) {
if (itemLongClickListener != null) {
itemLongClickListener.onItemLongClick(mGridView, childAt(currentDragPosition), currentDragPosition, 0);
}
handler.post(new Runnable() {
@Override
public void run() {
if (mDragMode == DRAG_BY_LONG_CLICK) {
// 启动拖拽模式
// isDragable = true;
// 通知父控件不拦截我的事件
getParent().requestDisallowInterceptTouchEvent(true);
// 根据点击的位置生成该位置上的view镜像
int position = eventToPosition(e);
if (position >= headDragPosition && position < mChildCount - footDragPosition) {
// copyView(currentDragPosition = position);
Message msg = handler.obtainMessage(0x123, position, 0);
// showpress本身大概需要170毫秒
// handler.sendMessageDelayed(msg, dragLongPressTime - 170);
handler.sendMessage(msg);
hasSendDragMsg = true;
}
}
}
});
}
}
},dragLongPressTime - 170);
};
};
private boolean hasSendDragMsg = false;
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 0x123:
// 启动拖拽模式
isDragable = true;
// 根据点击的位置生成该位置上的view镜像
copyView(currentDragPosition = msg.arg1);
hasSendDragMsg = false;
break;
default:
break;
}
return false;
}
});
public DragSortGridView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DragSortGridView(Context context) {
super(context);
init();
}
private void init() {
Context context = getContext();
mGridView = new NoScrollGridView(context);
mGridView.setVerticalScrollBarEnabled(false);
mGridView.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
mGridView.setSelector(new ColorDrawable());
// View的宽高之类必须在测量,布局,绘制一系列过程之后才能获取到
mGridView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (mChilds.isEmpty()) {
for (int i = 0; i < mGridView.getChildCount(); i++) {
View view = mGridView.getChildAt(i);
view.setTag(TAG_KEY, new int[]{0, 0});
view.clearAnimation();
mChilds.add(view);
}
}
if (!mChilds.isEmpty()) {
mColHeight = mChilds.get(0).getHeight();
}
mColWidth = mGridView.getColumnWidth();
if (mChildCount % mNumColumns == 0) {
mMaxHeight = mColHeight * mChildCount / mNumColumns;
} else {
mMaxHeight = mColHeight * (mChildCount / mNumColumns + 1);
}
canScroll = mMaxHeight - getHeight() > 0;
// 告知事件处理,完成View加载,许多属性也已经初始化了
isViewInitDone = true;
}
});
mScrollView = new ListenScrollView(context);
mDragFrame = new FrameLayout(context);
addView(mScrollView, -1, -1);
mScrollView.addView(mGridView, -1, -1);
addView(mDragFrame, new LayoutParams(-1, -1));
detector = new GestureDetector(context, simpleOnGestureListener);
detector.setIsLongpressEnabled(false);
mGridView.setNumColumns(mNumColumns);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
try {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
isLongOnClick = true;
break;
case MotionEvent.ACTION_UP:
isLongOnClick = false;
break;
default:
break;
}
if (l != null) {
l.onTouch(this, ev);
}
if (!isViewInitDone) {
return false;
}
if (isDragable) {
handleScrollAndCreMirror(ev);
} else {
// 交给子控件自己处理
if (canScroll) {
mScrollView.dispatchTouchEvent(ev);
} else {
mGridView.dispatchTouchEvent(ev);
}
}
// 处理拖动
detector.onTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP) {
lastLocation = null;
if (hasSendDragMsg) {
hasSendDragMsg = false;
handler.removeMessages(0x123);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* Author :[pWX273343] 2015年7月22日
* <p>
* Description :拦截所有事件
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
/**
* 处理自动滚屏,和单击生成镜像
*/
private void handleScrollAndCreMirror(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 通知父控件不拦截我的事件
getParent().requestDisallowInterceptTouchEvent(true);
if (touchClashparent != null) {
touchClashparent.requestDisallowInterceptTouchEvent(true);
}
// 根据点击的位置生成该位置上的view镜像
int position = eventToPosition(ev);
if (position >= headDragPosition && position < mChildCount - footDragPosition) {
copyView(currentDragPosition = position);
}
break;
case MotionEvent.ACTION_MOVE:
// 通知父控件不拦截我的事件
getParent().requestDisallowInterceptTouchEvent(true);
if (touchClashparent != null) {
touchClashparent.requestDisallowInterceptTouchEvent(true);
}
// 内容太多时,移动到边缘会自动滚动
if (canScroll) {
int touchArea = decodeTouchArea(ev);
if (touchArea != mTouchArea) {
onTouchAreaChange(touchArea);
mTouchArea = touchArea;
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (hideView != null) {
hideView.setVisibility(View.VISIBLE);
if (onDragSelectListener != null) {
onDragSelectListener.onPutDown(hideView);
}
}
mDragFrame.removeAllViews();
// mDragFrame.scrollTo(0, 0);
// isNotifyByDragSort = true;
if (hasPositionChange) {
hasPositionChange = false;
adapter.notifyDataSetChanged();
} else if (mDragMode == DRAG_BY_LONG_CLICK && itemLongClickListener != null) {
itemLongClickListener.onItemLongClick(mGridView, childAt(currentDragPosition), currentDragPosition, 0);
}
// 停止滚动
if (canScroll) {
int scrollStates2 = decodeTouchArea(ev);
if (scrollStates2 != 0) {
onTouchAreaChange(0);
mTouchArea = 0;
}
}
// 放手时取消拖动排序模式
if (mDragMode == DRAG_BY_LONG_CLICK) {
isDragable = false;
}
break;
default:
break;
}
}
/**
* @param ev 事件
* @return 0中间区域, 1底部,-1顶部
* @描述: 检查当前触摸事件位于哪个区域, 顶部1/5可能触发下滚,底部1/5可能触发上滚
* @作者 [pWX273343] 2015年6月30日
*/
private int decodeTouchArea(MotionEvent ev) {
if (ev.getY() > getHeight() * 4 / (double) 5) {
return 1;
} else if (ev.getY() < getHeight() / (double) 5) {
return -1;
} else {
return 0;
}
}
/**
* @param ev
* @return
* @描述 得到事件触发点, 摸到的是哪一个item
* @作者 [pWX273343] 2015年7月6日
*/
public int eventToPosition(MotionEvent ev) {
if (ev != null) {
int m = (int) ev.getX() / mColWidth;
int n = (int) (ev.getY() + mCurrentY) / mColHeight;
int position = n * mNumColumns + m;
if (position >= mChildCount) {
return mChildCount - 1;
} else {
return position;
}
}
return 0;
}
// 这里把控件作为假的横向ListView,所以返回position跟高度无关,暂时这样
// public int eventToPosition(MotionEvent ev) {
//
// if (ev != null) {
// int m = (int) ev.getX() / mColWidth;
// if (m >= mChildCount) {
// return mChildCount - 1;
// } else {
// return m;
// }
// }
// return 0;
// }
/**
* @param dragPosition
* @描述:复制一个镜像,并添加到透明层
* @作者 [pWX273343] 2015年7月6日
*/
private void copyView(int dragPosition) {
hideView = mChilds.get(dragPosition);
int realPosition = mGridView.indexOfChild(hideView);
if (!adapter.isUseCopyView()) {
mCopyView = adapter.getView(realPosition, mCopyView, mDragFrame);
} else {
mCopyView = adapter.copyView(realPosition, mCopyView, mDragFrame);
}
hideView.setVisibility(View.INVISIBLE);
mDragFrame.addView(mCopyView, mColWidth, mColHeight);
int[] l1 = new int[2];
int[] l2 = new int[2];
hideView.getLocationOnScreen(l1);
mDragFrame.getLocationOnScreen(l2);
// mCopyView.setX(hideView.getLeft());
// mCopyView.setY(hideView.getTop() - mCurrentY);
mCopyView.setX(l1[0] - l2[0]);
mCopyView.setY(l1[1] - l2[1]);
if (onDragSelectListener == null) {
mCopyView.setScaleX(1.2f);
mCopyView.setScaleY(1.2f);
} else {
onDragSelectListener.onDragSelect(mCopyView);
}
}
/**
* @param from
* @param to
* @描述:动画效果移动View
* @作者 [pWX273343] 2015年6月24日
*/
private void translateView(int from, int to) {
View view = mChilds.get(from);
int fromXValue = ((int[]) view.getTag(TAG_KEY))[0];
int fromYValue = ((int[]) view.getTag(TAG_KEY))[1];
int toXValue = to % mNumColumns - from % mNumColumns + fromXValue;
int toYValue = to / mNumColumns - from / mNumColumns + fromYValue;
Animation animation = new TranslateAnimation(1, fromXValue, 1, toXValue, 1, fromYValue, 1, toYValue);
animation.setDuration(ANIM_DURING);
animation.setFillAfter(true);
view.setTag(TAG_KEY, new int[]{toXValue, toYValue});
view.startAnimation(animation);
}
/**
* @param from
* @param to
* @描述:拖动View使位置发生改变时
* @作者 [pWX273343] 2015年7月6日
*/
private void onDragPositionChange(int from, int to) {
if (from > to) {
for (int i = to; i < from; i++) {
translateView(i, i + 1);
}
} else {
for (int i = to; i > from; i--) {
translateView(i, i - 1);
}
}
if (!hasPositionChange) {
hasPositionChange = true;
}
adapter.onDataModelMove(from, to);
View view = mChilds.remove(from);
mChilds.add(to, view);
currentDragPosition = to;
}
/**
* Function :setAdapter
* <p/>
* Author :[pWX273343] 2015年6月24日
* <p/>
* Description :设置适配器.该适配器必须实现一个方法,当view的位置发生变动时,对实际数据的改动
*
* @param adapter
* @see GridView#setAdapter(android.widget.ListAdapter)
*/
public void setAdapter(DragAdapter adapter) {
if (this.adapter != null && observer != null) {
this.adapter.unregisterDataSetObserver(observer);
}
this.adapter = adapter;
mGridView.setAdapter(adapter);
adapter.registerDataSetObserver(observer);
mChildCount = adapter.getCount();
}
public int getNumColumns() {
return mNumColumns;
}
/**
* 每行几个
*/
public void setNumColumns(int numColumns) {
this.mNumColumns = numColumns;
mGridView.setNumColumns(numColumns);
}
/**
* 设置前几个item不可以改变位置
*/
public void setNoPositionChangeItemCount(int count) {
headDragPosition = count;
}
/**
* 设置后几个item不可以改变位置
*/
public void setFootNoPositionChangeItemCount(int count) {
footDragPosition = count;
}
/**
* 控制自动滚屏的动画监听器.
*/
private ValueAnimator.AnimatorUpdateListener animUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int targetY = Math.round((Float) animation.getAnimatedValue());
if (targetY < 0) {
targetY = 0;
} else if (targetY > mMaxHeight - getHeight()) {
targetY = mMaxHeight - getHeight();
}
// mGridView.scrollTo(0, targetY);
mScrollView.smoothScrollTo(0, targetY);
// mCurrentY = targetY;
}
};
/**
* @param scrollStates
* @描述:触摸区域改变,做相应处理,开始滚动或停止滚动
* @作者 [pWX273343] 2015年6月29日
*/
protected void onTouchAreaChange(int scrollStates) {
if (!canScroll) {
return;
}
if (animator != null) {
animator.removeUpdateListener(animUpdateListener);
}
if (scrollStates == 1) {// 从普通区域进入触发向上滚动的区域
int instance = mMaxHeight - getHeight() - mCurrentY;
animator = ValueAnimator.ofFloat(mCurrentY, mMaxHeight - getHeight());
animator.setDuration((long) (instance / 0.5f));
animator.setTarget(mGridView);
animator.addUpdateListener(animUpdateListener);
animator.start();
} else if (scrollStates == -1) {// 进入触发向下滚动的区域
animator = ValueAnimator.ofFloat(mCurrentY, 0);
animator.setDuration((long) (mCurrentY / 0.5f));
animator.setTarget(mGridView);
animator.addUpdateListener(animUpdateListener);
animator.start();
}
}
private OnDragSelectListener onDragSelectListener;
/**
* @描述:一个item view刚被拖拽和放下时起来生成镜像时调用.
* @作者 [pWX273343] 2015年6月30日
*/
public void setOnDragSelectListener(OnDragSelectListener onDragSelectListener) {
this.onDragSelectListener = onDragSelectListener;
}
public interface OnDragSelectListener {
/**
* @param mirror 所拖拽起来的view生成的镜像 ,并不是实际的view.可对这个镜像实施变换效果,但是并不改变放下后的效果
* @描述:拖拽起一个view时调用
* @作者 [pWX273343] 2015年6月30日
*/
void onDragSelect(View mirror);
/**
* @param itemView
* @描述:拖拽的View放下时调用
* @作者 [pWX273343] 2015年7月3日
*/
void onPutDown(View itemView);
}
class NoScrollGridView extends GridView {
public NoScrollGridView(Context context) {
super(context);
}
/**
* @return
* @描述:兼容老版本的getColumWidth
* @作者 [pWX273343] 2015年7月1日
*/
public int getColumnWidth() {
return getWidth() / getNumColumns();
}
public NoScrollGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, mExpandSpec);
}
}
/**
* Copyright (C), 2008-2015, Huawei Tech. Co., Ltd.
* <p>
* Description : 监听滚动的scrollview,我们需要实时知道他已滚动的距离
*
* @author [pWX273343] 2015年7月22日
* @version V100R001
* @since V100R001
*/
class ListenScrollView extends ScrollView {
public ListenScrollView(Context context) {
super(context);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mCurrentY = getScrollY();
}
}
public View getChildViewAtIndex(int index) {
if (index < mChilds.size()) {
return mChilds.get(index);
}
return null;
}
// 转交给gridview一些常用监听器
private AdapterView.OnItemLongClickListener itemLongClickListener;
/**
* @param itemClickListener
* @描述:item 转交给gridview一些常用监听器
* @作者 [pWX273343] 2015年7月27日
*/
public void setOnItemClickListener(AdapterView.OnItemClickListener itemClickListener) {
mGridView.setOnItemClickListener(itemClickListener);
}
/**
* 长按监听器自己触发,点击拖动模式不存在长按
*
* @param
*/
public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener itemLongClickListener) {
this.itemLongClickListener = itemLongClickListener;
}
/**
* 点击拖动
*/
public static final int DRAG_WHEN_TOUCH = 0;
/**
* 长按拖动
*/
public static final int DRAG_BY_LONG_CLICK = 1;
private int mDragMode = DRAG_WHEN_TOUCH;
/**
* @param mode int类型
* @描述:设置拖动的策略是点击还是长按
* @作者 [pWX273343] 2015年7月20日 参考 DRAG_WHEN_TOUCH,DRAG_BY_LONG_CLICK
*/
public void setDragModel(int mode) {
this.mDragMode = mode;
isDragable = mode == DRAG_WHEN_TOUCH;
}
public View childAt(int index) {
return mGridView.getChildAt(index);
}
public int childCount() {
return mGridView.getChildCount();
}
public void setAnimFrame(FrameLayout mDragFrame) {
this.mDragFrame = mDragFrame;
}
private OnTouchListener l;
@Override
public void setOnTouchListener(OnTouchListener l) {
this.l = l;
}
private long dragLongPressTime = 600;
/**
* 设置长按需要用时
*
* @param time
*/
public void setDragLongPressTime(long time) {
dragLongPressTime = time;
}
/**
* 设置触摸事件冲突父控件
*
* @param touchClashparent
*/
public void setTouchClashparent(ViewGroup touchClashparent) {
this.touchClashparent = touchClashparent;
}
public ScrollView getmScrollView() {
return mScrollView;
}
}

@ -0,0 +1,69 @@
package xyz.fycz.myreader.custom;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.widget.AppCompatTextView;
public class MyTextView extends /*AppCompatEditText*/ AppCompatTextView {
/* private OnTouchListener mOnTouchListener;
private long timeDown;
private long timeUp;*/
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/* @Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (timeUp - timeDown > 0 && timeUp - timeDown < 1000){
return false;
}else {
return super.dispatchTouchEvent(event);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
timeDown = System.currentTimeMillis();
}else if (event.getAction() == MotionEvent.ACTION_UP){
timeUp = System.currentTimeMillis();
}
return super.onTouchEvent(event);
}*/
}

@ -0,0 +1,13 @@
package xyz.fycz.myreader.custom;
import android.content.Context;
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatTextView;
public class ReadTextView extends AppCompatTextView {
public ReadTextView(Context context){
super(context);
}
}

@ -0,0 +1,212 @@
package xyz.fycz.myreader.entity;
import java.io.Serializable;
import java.util.ArrayList;
/**
* Created by zhao on 2016/11/9.
* APP通迅录树结构
*
*/
public class ContactsTree implements Serializable {
private static final long serialVersionUID = 7184368467231587092L;
//根节点赋值字段(部门节点)
private String DepartId;//部门id
private String DepartName;//部门名
private String orgCode; //部门编码
private String departOrder; //部门排序
private boolean select;
//叶节点赋值字段(个人信息节点)
private String id;
private String userDepartId;//用户所属部门ID
private String userDepartName;//用户所属部门名
private boolean moreDepartUser;// 用户是否多部门 true是 false不是
private boolean transpondPerson;//是否转发人员 true是 false不是
private String userName;//用户名
private String realName;//姓名
private String wholeSpellName;//姓名全拼
private String firstLetterName;//拼音首字母 eg:hzh
private String mobilePhone;//手机
private String sex;//性别
private String email;//邮箱
private ArrayList<ContactsTree> children;//孩子节点
private ContactsTree parent;//父节点
public boolean isTranspondPerson() {
return transpondPerson;
}
public void setTranspondPerson(boolean transpondPerson) {
this.transpondPerson = transpondPerson;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDepartOrder() {
return departOrder;
}
public String getOrgCode() {
return orgCode;
}
public void setDepartOrder(String departOrder) {
this.departOrder = departOrder;
}
public void setOrgCode(String orgCode) {
this.orgCode = orgCode;
}
public boolean isMoreDepartUser() {
return moreDepartUser;
}
public void setMoreDepartUser(boolean moreDepartUser) {
this.moreDepartUser = moreDepartUser;
}
public String getUserDepartId() {
return userDepartId;
}
public String getUserDepartName() {
return userDepartName;
}
public void setUserDepartId(String userDepartId) {
this.userDepartId = userDepartId;
}
public void setUserDepartName(String userDepartName) {
this.userDepartName = userDepartName;
}
public boolean isSelect() {
return select;
}
public void setSelect(boolean select) {
this.select = select;
}
public ContactsTree getParent() {
return parent;
}
public void setParent(ContactsTree parent) {
this.parent = parent;
}
public ContactsTree(){
children = new ArrayList<>();
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getMobilePhone() {
return mobilePhone;
}
public void setMobilePhone(String mobilePhone) {
this.mobilePhone = mobilePhone;
}
public ArrayList<ContactsTree> getChildren() {
return children;
}
public String getDepartId() {
return DepartId;
}
public String getDepartName() {
return DepartName;
}
public String getFirstLetterName() {
return firstLetterName;
}
public String getRealName() {
return realName;
}
public String getUserName() {
return userName;
}
public String getWholeSpellName() {
return wholeSpellName;
}
public void setChildren(ArrayList<ContactsTree> children) {
this.children = children;
}
public void setDepartId(String departId) {
DepartId = departId;
}
public void setDepartName(String departName) {
DepartName = departName;
}
public void setFirstLetterName(String firstLetterName) {
this.firstLetterName = firstLetterName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setWholeSpellName(String wholeSpellName) {
this.wholeSpellName = wholeSpellName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "ContactsTree{" +
"DepartId='" + DepartId + '\'' +
", DepartName='" + DepartName + '\'' +
", userName='" + userName + '\'' +
", realName='" + realName + '\'' +
", wholeSpellName='" + wholeSpellName + '\'' +
", firstLetterName='" + firstLetterName + '\'' +
", mobilePhone='" + mobilePhone + '\'' +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
", children=" + children +
'}';
}
}

@ -0,0 +1,36 @@
package xyz.fycz.myreader.entity;
import java.io.Serializable;
public class Custom implements Serializable {
private static final long serialVersionUID = 5088810102696918656L;
private String id;
private String type;//类型
public String getId() {
return id;
}
public String getType() {
return type;
}
public void setId(String id) {
this.id = id;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Custom{" +
"id='" + id + '\'' +
", type='" + type + '\'' +
'}';
}
}

@ -0,0 +1,166 @@
package xyz.fycz.myreader.entity;
import xyz.fycz.myreader.util.CalendarHelper;
import java.io.Serializable;
public class Date implements Serializable {
private static final long serialVersionUID = -1251952358800941760L;
private int year; //年
private int month; //月
private int date; //日
private int day; //星期
private boolean festival;
public Date(){
}
public int compare(Date date){
if(year == date.getYear() && month == date.getMonth() && this.date == date.getDate()){
return 0;
}else if(year > date.getYear()
|| (year == date.getYear() && month > date.getMonth())
||(year == date.getYear() && month == date.getMonth() && this.date > date.getDate())){
return 1;
}else if(year < date.getYear()
|| (year == date.getYear() && month < date.getMonth())
||(year == date.getYear() && month == date.getMonth() && this.date < date.getDate())){
return -1;
}else {
return -101;
}
}
public boolean isSameDate(Date date){
return year == date.getYear() && month == date.getMonth() && this.date == date.getDate();
}
public Date copyDate(){
Date resDate = new Date();
resDate.setYear(year);
resDate.setMonth(month);
resDate.setDate(this.date);
resDate.setDay(day);
return resDate;
}
public void lastMonth(){
if(month == 1){
year--;
month = 12;
}else {
month--;
}
}
public void nextMonth(){
if(month == 12){
year++;
month = 1;
}else {
month++;
}
}
public Date lastDate(){
Date date = copyDate();
if(date.getDate() == 1){
if(month == 1){
date.setMonth(12);
date.setYear(date.getYear() - 1);
}else {
date.setMonth(date.getMonth() - 1);
}
date.setDate(CalendarHelper.getMonthDays(date.getYear(),date.getMonth()));
}else {
date.setDate(date.getDate() - 1);
}
if(date.getDay() == 0){
date.setDay(6);
}else {
date.setDay(date.getDay() - 1);
}
return date;
}
public Date nextDate(){
Date date = copyDate();
if(date.getDate() == CalendarHelper.getMonthDays(date.getYear(),date.getMonth())){
if(month == 12){
date.setMonth(1);
date.setYear(date.getYear() + 1);
}else {
date.setMonth(date.getMonth() + 1);
}
date.setDate(1);
}else {
date.setDate(date.getDate() + 1);
}
if(date.getDay() == 6){
date.setDay(0);
}else {
date.setDay(date.getDay() + 1);
}
return date;
}
public long toTime(){
java.util.Date date1 = new java.util.Date(year-1900,month-1,date);
return date1.getTime();
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getDate() {
return date;
}
public int getMonth() {
return month;
}
public int getYear() {
return year;
}
public void setDate(int date) {
this.date = date;
}
public void setMonth(int month) {
this.month = month;
}
public void setYear(int year) {
this.year = year;
}
public boolean isFestival() {
return festival;
}
public void setFestival(boolean festival) {
this.festival = festival;
}
}

@ -0,0 +1,105 @@
package xyz.fycz.myreader.entity;
import java.io.Serializable;
public class JsonModel implements Serializable {
private static final long serialVersionUID = -7169864463597942730L;
private int error;//错误码
private boolean success;//请求是否成功
private String result;//服务器返回的json数据存放与此
private String token;
private int datasize;
private String publicKey;
private int visibleLastIndex = 0;
private int visibleItemCount;
public JsonModel() {
}
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public int getError() {
return this.error;
}
public void setError(int error) {
this.error = error;
}
public String getResult() {
return this.result;
}
public void setResult(String result) {
this.result = result;
}
public String getToken() {
return this.token;
}
public void setToken(String token) {
this.token = token;
}
public boolean isSuccess() {
return this.success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public int getVisibleLastIndex() {
return this.visibleLastIndex;
}
public void setVisibleLastIndex(int visibleLastIndex) {
this.visibleLastIndex = visibleLastIndex;
}
public int getVisibleItemCount() {
return this.visibleItemCount;
}
public void setVisibleItemCount(int visibleItemCount) {
this.visibleItemCount = visibleItemCount;
}
public int getDatasize() {
return this.datasize;
}
public void setDatasize(int datasize) {
this.datasize = datasize;
}
@Override
public String toString() {
return "JsonModel{" +
"error=" + error +
", success=" + success +
", result='" + result + '\'' +
", token='" + token + '\'' +
", datasize=" + datasize +
", visibleLastIndex=" + visibleLastIndex +
", visibleItemCount=" + visibleItemCount +
'}';
}
}

@ -0,0 +1,53 @@
package xyz.fycz.myreader.entity;
import java.util.Objects;
/**
* @author fengyue
* @date 2020/5/19 9:19
*/
public class SearchBookBean {
private String name;//书名
private String author;//作者
public SearchBookBean() {
}
public SearchBookBean(String name, String author) {
this.name = name;
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SearchBookBean that = (SearchBookBean) o;
if (author == null){
return name.equals(that.name);
}
return name.equals(that.name) &&
author.equals(that.author);
}
@Override
public int hashCode() {
return Objects.hash(name, author);
}
}

@ -0,0 +1,173 @@
package xyz.fycz.myreader.entity;
import xyz.fycz.myreader.enums.BookcaseStyle;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.enums.Language;
import xyz.fycz.myreader.enums.ReadStyle;
import xyz.fycz.myreader.widget.page.PageMode;
import java.io.Serializable;
/**
* 用户设置
*
*/
public class Setting implements Serializable {
private static final long serialVersionUID = 2295691810299441757L;
private int readWordColor;//阅读字体颜色
private int readBgColor;//阅读背景颜色
private float readWordSize;//阅读字体大小
private ReadStyle readStyle;//阅读模式
private boolean dayStyle;//是否日间模式
private int brightProgress;//亮度 1- 100
private boolean brightFollowSystem;//亮度跟随系统
private Language language;//简繁体
private Font font;//字体
private int autoScrollSpeed = 5;//自动滑屏速度
private PageMode pageMode;//翻页模式
private boolean isVolumeTurnPage;//是否开启音量键翻页
private BookcaseStyle bookcaseStyle;//书架布局
private int newestVersionCode;//最新版本号
private String localFontName;//本地字体名字
private int settingVersion;//设置版本号
public int getAutoScrollSpeed() {
return autoScrollSpeed;
}
public void setAutoScrollSpeed(int autoScrollSpeed) {
this.autoScrollSpeed = autoScrollSpeed;
}
public Font getFont() {
return font;
}
public void setFont(Font font) {
this.font = font;
}
public Language getLanguage() {
return language;
}
public void setLanguage(Language language) {
this.language = language;
}
public boolean isBrightFollowSystem() {
return brightFollowSystem;
}
public void setBrightFollowSystem(boolean brightFollowSystem) {
this.brightFollowSystem = brightFollowSystem;
}
public void setBrightProgress(int brightProgress) {
this.brightProgress = brightProgress;
}
public int getBrightProgress() {
return brightProgress;
}
public boolean isDayStyle() {
return dayStyle;
}
public void setDayStyle(boolean dayStyle) {
this.dayStyle = dayStyle;
}
public int getReadWordColor() {
return readWordColor;
}
public void setReadWordColor(int readWordColor) {
this.readWordColor = readWordColor;
}
public int getReadBgColor() {
return readBgColor;
}
public void setReadBgColor(int readBgColor) {
this.readBgColor = readBgColor;
}
public float getReadWordSize() {
return readWordSize;
}
public void setReadWordSize(float readWordSize) {
this.readWordSize = readWordSize;
}
public ReadStyle getReadStyle() {
return readStyle;
}
public void setReadStyle(ReadStyle readStyle) {
this.readStyle = readStyle;
}
public PageMode getPageMode() {
return pageMode;
}
public void setPageMode(PageMode pageMode) {
this.pageMode = pageMode;
}
public boolean isVolumeTurnPage() {
return isVolumeTurnPage;
}
public void setVolumeTurnPage(boolean volumeTurnPage) {
isVolumeTurnPage = volumeTurnPage;
}
public BookcaseStyle getBookcaseStyle() {
return bookcaseStyle;
}
public void setBookcaseStyle(BookcaseStyle bookcaseStyle) {
this.bookcaseStyle = bookcaseStyle;
}
public int getNewestVersionCode() {
return newestVersionCode;
}
public void setNewestVersionCode(int newestVersionCode) {
this.newestVersionCode = newestVersionCode;
}
public String getLocalFontName() {
return localFontName;
}
public void setLocalFontName(String localFontName) {
this.localFontName = localFontName;
}
public int getSettingVersion() {
return settingVersion;
}
public void setSettingVersion(int settingVersion) {
this.settingVersion = settingVersion;
}
}

@ -0,0 +1,201 @@
package xyz.fycz.myreader.entity;
import android.os.Parcel;
import android.os.Parcelable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Time implements Parcelable{
private int year;
private int month;
private int date;
private int hour;
private int minute;
private int second;
public long getLongTime(){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(year);
if(month < 10){
stringBuilder.append("0"+month);
}else {
stringBuilder.append(month);
}
if(date < 10){
stringBuilder.append("0" + date);
}else {
stringBuilder.append(date);
}
if(hour < 10){
stringBuilder.append("0" + hour);
}else {
stringBuilder.append(hour);
}
if(minute < 10){
stringBuilder.append("0" + minute);
}else {
stringBuilder.append(minute);
}
if(second < 10){
stringBuilder.append("0" + second);
}else {
stringBuilder.append(second);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
java.util.Date date = null;
try {
date = sdf.parse(stringBuilder.toString());
return date.getTime();
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
public void init(Date date){
year = date.getYear() + 1900;
month = date.getMonth() + 1;
this.date = date.getDate();
hour = date.getHours();
minute = date.getMinutes();
second = 0;
}
public void init(xyz.fycz.myreader.entity.Date date){
Date date1 = new Date();
year = date.getYear();
month = date.getMonth();
this.date = date.getDate();
hour = date1.getHours();
minute = date1.getMinutes();
second = 0;
}
public void setToDayZero(){
hour = 0;
minute = 0;
second = 0;
}
public void setToDayLast(){
hour = 23;
minute = 59;
second = 59;
}
public void lastMonth(){
if(month == 1){
month = 12;
year = year - 1;
}else {
month = month - 1;
}
}
public void nextMonth(){
if(month == 12){
month = 1;
year = year + 1;
}else {
month = month + 1;
}
}
public void init(long time){
Date date = new Date(time);
init(date);
}
public int getDate() {
return date;
}
public int getHour() {
return hour;
}
public int getMinute() {
return minute;
}
public int getMonth() {
return month;
}
public int getSecond() {
return second;
}
public int getYear() {
return year;
}
public void setDate(int date) {
this.date = date;
}
public void setHour(int hour) {
this.hour = hour;
}
public void setMinute(int minute) {
this.minute = minute;
}
public void setMonth(int month) {
this.month = month;
}
public void setSecond(int second) {
this.second = second;
}
public void setYear(int year) {
this.year = year;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.year);
dest.writeInt(this.month);
dest.writeInt(this.date);
dest.writeInt(this.hour);
dest.writeInt(this.minute);
dest.writeInt(this.second);
}
public Time() {
}
protected Time(Parcel in) {
this.year = in.readInt();
this.month = in.readInt();
this.date = in.readInt();
this.hour = in.readInt();
this.minute = in.readInt();
this.second = in.readInt();
}
public static final Creator<Time> CREATOR = new Creator<Time>() {
@Override
public Time createFromParcel(Parcel source) {
return new Time(source);
}
@Override
public Time[] newArray(int size) {
return new Time[size];
}
};
}

@ -0,0 +1,37 @@
package xyz.fycz.myreader.entity;
import java.io.Serializable;
public class UpdateInfo implements Serializable{
private static final long serialVersionUID = 8136214121542689902L;
private int newestVersionCode;
private String newestVersionName;
private String downLoadUrl;
public int getNewestVersionCode() {
return newestVersionCode;
}
public void setNewestVersionCode(int newestVersionCode) {
this.newestVersionCode = newestVersionCode;
}
public String getNewestVersionName() {
return newestVersionName;
}
public void setNewestVersionName(String newestVersionName) {
this.newestVersionName = newestVersionName;
}
public String getDownLoadUrl() {
return downLoadUrl;
}
public void setDownLoadUrl(String downLoadUrl) {
this.downLoadUrl = downLoadUrl;
}
}

@ -0,0 +1,8 @@
package xyz.fycz.myreader.entity;
/**
* Created by newbiechen on 17-5-27.
*/
public final class Void {
}

@ -0,0 +1,28 @@
package xyz.fycz.myreader.entity.bookstore;
/**
* @author zhao
* @description: 书城小说分类
* @date :2020/4/13 11:46
*/
public class BookType {
private String typeName;//分类名称
private String url;//分类链接
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

@ -0,0 +1,30 @@
package xyz.fycz.myreader.enums;
/**
* 小说源
* Created by zhao on 2020/04/13.
*/
public enum BookSource {
tianlai("天籁小说"),
fynovel("风月小说"),
biquge44("笔趣阁44"),
pinshu("品书网"),
biquge("笔趣阁"),
local("本地书籍");
public String text;
BookSource(String text) {
this.text = text;
}
public static BookSource get(int var0) {
return values()[var0];
}
public static BookSource fromString(String string) {
return valueOf(string);
}
}

@ -0,0 +1,20 @@
package xyz.fycz.myreader.enums;
/**
* @author fengyue
* @date 2020/4/23 11:02
*/
public enum BookcaseStyle {
listMode,//列表模式
threePalaceMode;//三列宫格模式
BookcaseStyle() {
}
public static BookcaseStyle get(int var0) {
return values()[var0];
}
public static BookcaseStyle fromString(String string) {
return BookcaseStyle.valueOf(string);
}
}

@ -0,0 +1,37 @@
package xyz.fycz.myreader.enums;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.common.APPCONST;
/**
* Created by zhao on 2016/11/3.
*/
public enum Font {
默认字体("默认字体", "默认字体"),
方正楷体("fangzhengkaiti.ttf", "https://fycz.lanzous.com/ilLFMe6kefe"),
方正行楷("fangzhengxingkai.ttf", "https://fycz.lanzous.com/imFvne6keji"),
经典宋体("songti.ttf", "https://fycz.lanzous.com/idhI5e6keqf"),
迷你隶书("mini_lishu.ttf", "https://fycz.lanzous.com/ihaXVe6kekj"),
方正黄草("fangzhenghuangcao.ttf", "https://fycz.lanzous.com/iQg67e6keed"),
方正硬笔行书("fangzheng_yingbi_xingshu.ttf", "https://fycz.lanzous.com/ilVh6ep9xja"),
本地字体("本地字体", "默认字体");
public String fileName;
public String downloadPath;
Font(String fileName, String downloadPath) {
this.fileName = fileName;
this.downloadPath = downloadPath;
}
public static Font get(int var0) {
return values()[var0];
}
public static Font fromString(String string) {
return Font.valueOf(string);
}
}

@ -0,0 +1,24 @@
package xyz.fycz.myreader.enums;
/**
* Created by zhao on 2016/11/3.
*/
public enum Language {
simplified,//简体中文
traditional;//繁体中文
Language() {
}
public static Language get(int var0) {
return values()[var0];
}
public static Language fromString(String string) {
return Language.valueOf(string);
}
}

@ -0,0 +1,25 @@
package xyz.fycz.myreader.enums;
/**
* Created by zhao on 2016/11/3.
*/
public enum ReadStyle {
protectedEye,//护眼
common,//普通
blueDeep,//深蓝
leather,//羊皮纸
breen;//
ReadStyle() {
}
public static ReadStyle get(int var0) {
return values()[var0];
}
public static ReadStyle fromString(String string) {
return ReadStyle.valueOf(string);
}
}

@ -0,0 +1,34 @@
package xyz.fycz.myreader.greendao;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.greendao.gen.DaoMaster;
import xyz.fycz.myreader.greendao.gen.DaoSession;
import xyz.fycz.myreader.greendao.util.MySQLiteOpenHelper;
public class GreenDaoManager {
private static GreenDaoManager instance;
private static DaoMaster daoMaster;
private static MySQLiteOpenHelper mySQLiteOpenHelper;
public static GreenDaoManager getInstance() {
if (instance == null) {
instance = new GreenDaoManager();
}
return instance;
}
public GreenDaoManager(){
mySQLiteOpenHelper = new MySQLiteOpenHelper(MyApplication.getmContext(), "read" , null);
daoMaster = new DaoMaster(mySQLiteOpenHelper.getWritableDatabase());
}
public DaoSession getSession(){
return daoMaster.newSession();
}
}

@ -0,0 +1,259 @@
package xyz.fycz.myreader.greendao.entity;
import androidx.annotation.Nullable;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Transient;
import java.io.Serializable;
import java.util.Objects;
/**
*
* Created by zhao on 2017/7/24.
*/
@Entity
public class Book implements Serializable {
@Transient
private static final long serialVersionUID = 1L;
@Id
private String id;
private String name;//书名
private String chapterUrl;//书目Url(本地书籍为:本地书籍地址)
private String imgUrl;//封面图片url
private String desc;//简介
private String author;//作者
@Nullable
private String type;//类型(本地书籍为:本地书籍)
private String updateDate;//更新时间
@Nullable
private String newestChapterId;//最新章节id
@Nullable
private String newestChapterTitle;//最新章节标题
@Nullable
private String newestChapterUrl;//最新章节url
@Nullable
private String historyChapterId;//上次关闭时的章节ID
@Nullable
private int histtoryChapterNum;//上次关闭时的章节数
private int sortCode;//排序编码
private int noReadNum;//未读章数量
private int chapterTotalNum;//总章节数
private int lastReadPosition;//上次阅读到的章节的位置
@Nullable
private String source;
@Generated(hash = 1392889320)
public Book(String id, String name, String chapterUrl, String imgUrl,
String desc, String author, String type, String updateDate,
String newestChapterId, String newestChapterTitle,
String newestChapterUrl, String historyChapterId,
int histtoryChapterNum, int sortCode, int noReadNum,
int chapterTotalNum, int lastReadPosition, String source) {
this.id = id;
this.name = name;
this.chapterUrl = chapterUrl;
this.imgUrl = imgUrl;
this.desc = desc;
this.author = author;
this.type = type;
this.updateDate = updateDate;
this.newestChapterId = newestChapterId;
this.newestChapterTitle = newestChapterTitle;
this.newestChapterUrl = newestChapterUrl;
this.historyChapterId = historyChapterId;
this.histtoryChapterNum = histtoryChapterNum;
this.sortCode = sortCode;
this.noReadNum = noReadNum;
this.chapterTotalNum = chapterTotalNum;
this.lastReadPosition = lastReadPosition;
this.source = source;
}
public Book(Book book) {
this.id = book.id;
this.name = book.name;
this.chapterUrl = book.chapterUrl;
this.imgUrl = book.imgUrl;
this.desc = book.desc;
this.author = book.author;
this.type = book.type;
this.updateDate = book.updateDate;
this.newestChapterId = book.newestChapterId;
this.newestChapterTitle = book.newestChapterTitle;
this.newestChapterUrl = book.newestChapterUrl;
this.historyChapterId = book.historyChapterId;
this.histtoryChapterNum = book.histtoryChapterNum;
this.sortCode = book.sortCode;
this.noReadNum = book.noReadNum;
this.chapterTotalNum = book.chapterTotalNum;
this.lastReadPosition = book.lastReadPosition;
this.source = book.source;
}
@Generated(hash = 1839243756)
public Book() {
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getChapterUrl() {
return this.chapterUrl;
}
public void setChapterUrl(String chapterUrl) {
this.chapterUrl = chapterUrl;
}
public String getImgUrl() {
return this.imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public String getDesc() {
return this.desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getAuthor() {
return this.author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public String getUpdateDate() {
return this.updateDate;
}
public void setUpdateDate(String updateDate) {
this.updateDate = updateDate;
}
public String getNewestChapterId() {
return this.newestChapterId;
}
public void setNewestChapterId(String newestChapterId) {
this.newestChapterId = newestChapterId;
}
public String getNewestChapterTitle() {
return this.newestChapterTitle;
}
public void setNewestChapterTitle(String newestChapterTitle) {
this.newestChapterTitle = newestChapterTitle;
}
public String getNewestChapterUrl() {
return this.newestChapterUrl;
}
public void setNewestChapterUrl(String newestChapterUrl) {
this.newestChapterUrl = newestChapterUrl;
}
public String getHistoryChapterId() {
return this.historyChapterId;
}
public void setHistoryChapterId(String historyChapterId) {
this.historyChapterId = historyChapterId;
}
public int getHisttoryChapterNum() {
return this.histtoryChapterNum;
}
public void setHisttoryChapterNum(int histtoryChapterNum) {
this.histtoryChapterNum = histtoryChapterNum;
}
public int getSortCode() {
return this.sortCode;
}
public void setSortCode(int sortCode) {
this.sortCode = sortCode;
}
public int getNoReadNum() {
return this.noReadNum;
}
public void setNoReadNum(int noReadNum) {
this.noReadNum = noReadNum;
}
public int getChapterTotalNum() {
return this.chapterTotalNum;
}
public void setChapterTotalNum(int chapterTotalNum) {
this.chapterTotalNum = chapterTotalNum;
}
public int getLastReadPosition() {
return this.lastReadPosition;
}
public void setLastReadPosition(int lastReadPosition) {
this.lastReadPosition = lastReadPosition;
}
@Nullable
public String getSource() {
return source;
}
public void setSource(@Nullable String source) {
this.source = source;
}
@Override
public String toString() {
return "{\n" +
"id='" + id + '\'' +
",\nname='" + name + '\'' +
",\nchapterUrl='" + chapterUrl + '\'' +
",\nimgUrl='" + imgUrl + '\'' +
",\ndesc='" + desc + '\'' +
",\nauthor='" + author + '\'' +
",\ntype='" + type + '\'' +
",\nupdateDate='" + updateDate + '\'' +
",\nnewestChapterId='" + newestChapterId + '\'' +
",\nnewestChapterTitle='" + newestChapterTitle + '\'' +
",\nnewestChapterUrl='" + newestChapterUrl + '\'' +
",\nhistoryChapterId='" + historyChapterId + '\'' +
",\nhisttoryChapterNum='" + histtoryChapterNum + '\'' +
",\nsortCode='" + sortCode + '\'' +
",\nnoReadNum='" + noReadNum + '\'' +
",\nchapterTotalNum='" + chapterTotalNum + '\'' +
",\nlastReadPosition='" + lastReadPosition + '\'' +
",\nsource='" + source + '\'' +
"\n}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return name.equals(book.name) &&
chapterUrl.equals(book.chapterUrl) &&
author.equals(book.author) &&
source.equals(book.source);
}
@Override
public int hashCode() {
return Objects.hash(name, chapterUrl, author, source);
}
}

@ -0,0 +1,92 @@
package xyz.fycz.myreader.greendao.entity;
import org.greenrobot.greendao.annotation.*;
/**
* @author fengyue
* @date 2020/7/21 16:02
* 书签
*/
@Entity
public class BookMark {
@Transient
private static final long serialVersionUID = 1L;
@Id
private String id;//书签id
@NotNull
private String bookId;//书签所属书的ID
private int number;//书签序号
private String title;//书签标题
@NotNull
private int bookMarkChapterNum;//书签章节
private int bookMarkReadPosition;//书签章节的位置
@Generated(hash = 1704575762)
public BookMark() {
}
@Generated(hash = 126149112)
public BookMark(String id, @NotNull String bookId, int number, String title, int bookMarkChapterNum,
int bookMarkReadPosition) {
this.id = id;
this.bookId = bookId;
this.number = number;
this.title = title;
this.bookMarkChapterNum = bookMarkChapterNum;
this.bookMarkReadPosition = bookMarkReadPosition;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBookId() {
return bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getBookMarkChapterNum() {
return bookMarkChapterNum;
}
public void setBookMarkChapterNum(int bookMarkChapterNum) {
this.bookMarkChapterNum = bookMarkChapterNum;
}
public int getBookMarkReadPosition() {
return bookMarkReadPosition;
}
public void setBookMarkReadPosition(int bookMarkReadPosition) {
this.bookMarkReadPosition = bookMarkReadPosition;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}

@ -0,0 +1,89 @@
package xyz.fycz.myreader.greendao.entity;
import androidx.annotation.Nullable;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.File;
/**
* 章节
* Created by zhao on 2017/7/24.
*/
@Entity
public class Chapter {
@Id
private String id;
private String bookId;//章节所属书的ID
private int number;//章节序号
private String title;//章节标题
private String url;//章节链接(本地书籍为:字符编码)
@Nullable
private String content;//章节正文
@Generated(hash = 1019441369)
public Chapter(String id, String bookId, int number, String title, String url,
String content) {
this.id = id;
this.bookId = bookId;
this.number = number;
this.title = title;
this.url = url;
this.content = content;
}
@Generated(hash = 393170288)
public Chapter() {
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getBookId() {
return this.bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public int getNumber() {
return this.number;
}
public void setNumber(int number) {
this.number = number;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public String getContent() {
String filePath = APPCONST.BOOK_CACHE_PATH + bookId
+ File.separator + title + FileUtils.SUFFIX_FY;
File file = new File(filePath);
if (file.exists() && file.length() > 0){
this.content = filePath;
}else {
this.content = null;
}
return this.content;
}
public void setContent(String content) {
this.content = content;
}
}

@ -0,0 +1,66 @@
package xyz.fycz.myreader.greendao.entity;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.NotNull;
import org.greenrobot.greendao.annotation.Transient;
import java.io.Serializable;
import org.greenrobot.greendao.annotation.Generated;
/**
* 搜索记录
*/
@Entity
public class SearchHistory implements Serializable {
@Transient
private static final long serialVersionUID = 1L;
@Id
private String id;
@NotNull
private String content;//内容
@NotNull
private String createDate;//创建时间
@Generated(hash = 1175489714)
public SearchHistory(String id, @NotNull String content,
@NotNull String createDate) {
this.id = id;
this.content = content;
this.createDate = createDate;
}
@Generated(hash = 1905904755)
public SearchHistory() {
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
public String getCreateDate() {
return this.createDate;
}
public void setCreateDate(String createDate) {
this.createDate = createDate;
}
}

@ -0,0 +1,56 @@
package xyz.fycz.myreader.greendao.service;
import android.database.Cursor;
import xyz.fycz.myreader.greendao.gen.DaoSession;
import xyz.fycz.myreader.greendao.GreenDaoManager;
public class BaseService {
public void addEntity(Object entity){
DaoSession daoSession = GreenDaoManager.getInstance().getSession();
daoSession.insert(entity);
}
public void updateEntity(Object entity){
DaoSession daoSession = GreenDaoManager.getInstance().getSession();
daoSession.update(entity);
}
public void deleteEntity(Object entity){
DaoSession daoSession = GreenDaoManager.getInstance().getSession();
daoSession.delete(entity);
}
/**
* 通过SQL查找
* @param sql
* @param selectionArgs
* @return
*/
public Cursor selectBySql(String sql, String[] selectionArgs){
Cursor cursor = null;
try {
DaoSession daoSession = GreenDaoManager.getInstance().getSession();
cursor = daoSession.getDatabase().rawQuery(sql, selectionArgs);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return cursor;
}
/**
* 执行SQL进行增删改
* @param sql
* @param selectionArgs
*/
public void rawQuery(String sql, String[] selectionArgs) {
DaoSession daoSession = GreenDaoManager.getInstance().getSession();
Cursor cursor = daoSession.getDatabase().rawQuery(sql, selectionArgs);
}
}

@ -0,0 +1,170 @@
package xyz.fycz.myreader.greendao.service;
import android.database.Cursor;
import xyz.fycz.myreader.greendao.GreenDaoManager;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.BookMark;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.entity.SearchHistory;
import xyz.fycz.myreader.greendao.gen.BookDao;
import xyz.fycz.myreader.greendao.gen.BookMarkDao;
import xyz.fycz.myreader.greendao.gen.SearchHistoryDao;
import xyz.fycz.myreader.util.DateHelper;
import xyz.fycz.myreader.util.StringHelper;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class BookMarkService extends BaseService {
private ArrayList<BookMark> findBookMarks(String sql, String[] selectionArgs) {
ArrayList<BookMark> bookMarks = new ArrayList<>();
try {
Cursor cursor = selectBySql(sql, selectionArgs);
if (cursor == null) return bookMarks;
while (cursor.moveToNext()) {
BookMark bookMark = new BookMark();
bookMark.setId(cursor.getString(0));
bookMark.setBookId(cursor.getString(1));
bookMark.setNumber(cursor.getInt(2));
bookMark.setTitle(cursor.getString(3));
bookMark.setBookMarkChapterNum(cursor.getInt(4));
bookMark.setBookMarkReadPosition(cursor.getInt(5));
bookMarks.add(bookMark);
}
} catch (Exception e) {
e.printStackTrace();
return bookMarks;
}
return bookMarks;
}
/**
* 通过ID查书签
* @param id
* @return
*/
public BookMark getBookById(String id) {
BookMarkDao bookMarkDao = GreenDaoManager.getInstance().getSession().getBookMarkDao();
return bookMarkDao.load(id);
}
/**
* 根据内容查找历史记录
* @param title
* @return
*/
public BookMark findBookMarkByTitle(String title){
BookMark bookMark = null;
String sql = "select * from book_mark where title = ?";
Cursor cursor = selectBySql(sql,new String[]{title});
if (cursor == null) return bookMark;
if (cursor.moveToNext()){
bookMark = new BookMark();
bookMark.setId(cursor.getString(0));
bookMark.setBookId(cursor.getString(1));
bookMark.setNumber(cursor.getInt(2));
bookMark.setTitle(cursor.getString(3));
bookMark.setBookMarkChapterNum(cursor.getInt(4));
bookMark.setBookMarkReadPosition(cursor.getInt(5));
}
return bookMark;
}
/**
* 获取书的所有书签
*
* @return
*/
public List<BookMark> findBookAllBookMarkByBookId(String bookId) {
if (StringHelper.isEmpty(bookId)) return new ArrayList<>();
String sql = "select * from book_mark where book_id = ? order by number";
return findBookMarks(sql, new String[]{bookId});
}
/**
* 添加书签
* @param bookMark
*/
public void addBookMark(BookMark bookMark) {
bookMark.setId(StringHelper.getStringRandom(25));
bookMark.setNumber(countBookMarkTotalNumByBookId(bookMark.getBookId()) + 1);
addEntity(bookMark);
}
/**
* 删除书的所有书签
*
* @param bookId
*/
public void deleteBookALLBookMarkById(String bookId) {
String sel = "delete from book_mark where book_id = ?";
rawQuery(sel, new String[]{bookId});
}
/**
* 批量删除书签
*
* @param bookMarks
*/
public void deleteBookALLBookMarks(ArrayList<BookMark> bookMarks){
for (BookMark bookMark : bookMarks){
deleteBookMark(bookMark);
}
}
/**
* 通过ID删除书签
* @param id
*/
public void deleteBookMarkById(String id){
BookMarkDao bookMarkDao = GreenDaoManager.getInstance().getSession().getBookMarkDao();
bookMarkDao.deleteByKey(id);
}
/**
* 删除书签
* @param bookMark
*/
public void deleteBookMark(BookMark bookMark){
deleteEntity(bookMark);
}
/**
* 通过id查询书籍书签总数
* @return
*/
public int countBookMarkTotalNumByBookId(String bookId){
int num = 0;
try {
Cursor cursor = selectBySql("select count(*) n from book where book_id = " + bookId,null);
if (cursor.moveToNext()){
num = cursor.getInt(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return num;
}
/**
* 添加或更新书签
* @param newBookMark
*/
public void addOrUpdateBookMark(BookMark newBookMark){
BookMark oldBookMark = findBookMarkByTitle(newBookMark.getTitle());
if (oldBookMark == null){
addBookMark(newBookMark);
}else {
oldBookMark.setBookId(newBookMark.getBookId());
oldBookMark.setBookMarkReadPosition(newBookMark.getBookMarkReadPosition());
oldBookMark.setNumber(countBookMarkTotalNumByBookId(oldBookMark.getBookId() + 1));
updateEntity(oldBookMark);
}
}
}

@ -0,0 +1,288 @@
package xyz.fycz.myreader.greendao.service;
import android.database.Cursor;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.gen.BookDao;
import xyz.fycz.myreader.util.*;
import xyz.fycz.myreader.greendao.GreenDaoManager;
import xyz.fycz.myreader.greendao.entity.Book;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BookService extends BaseService {
private ChapterService mChapterService;
private BookMarkService mBookMarkService;
public BookService(){
mChapterService = new ChapterService();
mBookMarkService = new BookMarkService();
}
private List<Book> findBooks(String sql, String[] selectionArgs) {
ArrayList<Book> books = new ArrayList<>();
try {
Cursor cursor = selectBySql(sql, selectionArgs);
while (cursor.moveToNext()) {
Book book = new Book();
book.setId(cursor.getString(0));
book.setName(cursor.getString(1));
book.setChapterUrl(cursor.getString(2));
book.setImgUrl(cursor.getString(3));
book.setDesc(cursor.getString(4));
book.setAuthor(cursor.getString(5));
book.setType(cursor.getString(6));
book.setUpdateDate(cursor.getString(7));
book.setNewestChapterId(cursor.getString(8));
book.setNewestChapterTitle(cursor.getString(9));
book.setNewestChapterUrl(cursor.getString(10));
book.setHistoryChapterId(cursor.getString(11));
book.setHisttoryChapterNum(cursor.getInt(12));
book.setSortCode(cursor.getInt(13));
book.setNoReadNum(cursor.getInt(14));
book.setChapterTotalNum(cursor.getInt(15));
book.setLastReadPosition(cursor.getInt(16));
book.setSource(cursor.getString(17));
books.add(book);
}
} catch (Exception e) {
e.printStackTrace();
}
return books;
}
/**
* 通过ID查书
* @param id
* @return
*/
public Book getBookById(String id) {
BookDao bookDao = GreenDaoManager.getInstance().getSession().getBookDao();
return bookDao.load(id);
}
/**
* 获取所有的书
* @return
*/
public List<Book> getAllBooks(){
String sql = "select * from book order by sort_code";
return findBooks(sql, null);
}
/**
* 新增书
* @param book
*/
public void addBook(Book book){
// book.setSortCode(countBookTotalNum() + 1);
book.setSortCode(0);
book.setId(StringHelper.getStringRandom(25));
addEntity(book);
}
/**
* 批量添加书籍
* @param books
*/
public void addBooks(List<Book> books){
for (Book book : books){
addBook(book);
}
}
/**
* 查找书作者书名
* @param author
* @param bookName
* @return
*/
public Book findBookByAuthorAndName(String bookName, String author){
Book book = null;
try {
Cursor cursor = selectBySql("select id from book where author = ? and name = ?",new String[]{author,bookName});
if (cursor.moveToNext()){
String id = cursor.getString(0);
book = getBookById(id);
}
} catch (Exception e) {
e.printStackTrace();
}
return book;
}
/**
* 删除书
* @param id
*/
public void deleteBookById(String id){
BookDao bookDao = GreenDaoManager.getInstance().getSession().getBookDao();
bookDao.deleteByKey(id);
mChapterService.deleteBookALLChapterById(id);
mBookMarkService.deleteBookALLBookMarkById(id);
}
/**
* 删除书
* @param book
*/
public void deleteBook(Book book){
deleteEntity(book);
mChapterService.deleteBookALLChapterById(book.getId());
mBookMarkService.deleteBookALLBookMarkById(book.getId());
}
/**
* 删除所有书
*/
public void deleteAllBooks(){
for(Book book : getAllBooks()){
deleteBook(book);
}
}
/**
* 查询书籍总数
* @return
*/
public int countBookTotalNum(){
int num = 0;
try {
Cursor cursor = selectBySql("select count(*) n from book ",null);
if (cursor.moveToNext()){
num = cursor.getInt(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return num;
}
/**
* 更新书
* @param books
*/
public void updateBooks(List<Book> books){
BookDao bookDao = GreenDaoManager.getInstance().getSession().getBookDao();
bookDao.updateInTx(books);
}
/**
* 更新单本书
* @param book
*/
public void updateBook(Book book){
deleteBook(book);
book.setId(StringHelper.getStringRandom(25));
addEntity(book);
}
/**
* 删除旧书添加新书
* @param OldBook
* @param newBook
*/
public void updateBook(Book OldBook, Book newBook){
deleteBook(OldBook);
newBook.setId(StringHelper.getStringRandom(25));
addEntity(newBook);
}
/**
* 判断书籍是否存在
* @param book
* @return
*/
public boolean isBookCollected(Book book){
return findBookByAuthorAndName(book.getName(), book.getAuthor()) != null;
}
/**
* 保存全部章节名称和url
* @param book
* @param chapters
*//*
public void saveAllChapters(Book book, ArrayList<Chapter> chapters){
String filePath = APPCONST.BOOK_CACHE_PATH + book.getId() +
File.separator + "chapters.fy";
StringBuilder s = new StringBuilder();
for (Chapter chapter : chapters){
s.append(chapter.getTitle());
s.append("=");
s.append(chapter.getUrl());
s.append(",\n");
}
s.deleteCharAt(s.lastIndexOf(","));
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(filePath));
bw.write(s.toString());
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
*//**
* 读取全部章节对象
* @param book
* @return
*//*
public Map<String, String> readAllChapters(Book book){
if (!isChaptersObjExist(book)) return null;
String filePath = APPCONST.BOOK_CACHE_PATH + book.getId() +
File.separator + "chapters.fy";
Map<String, String> chapterMap = new HashMap<>();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(filePath));
String tem = "";
StringBuilder s = new StringBuilder();
while ((tem = br.readLine()) != null){
s.append(tem).append("\n");
}
String[] chapters = s.toString().split(",");
for (String chapter : chapters){
String[] chapterInfo = chapter.split("=");
chapterMap.put(chapterInfo[0], chapterInfo[1]);
}
return chapterMap;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
*//**
* 判断全部章节对象是否存在
* @param book
* @return
*//*
public boolean isChaptersObjExist(Book book){
return new File(APPCONST.BOOK_CACHE_PATH + book.getId() +
File.separator + "chapters.fy").exists();
}
*/
}

@ -0,0 +1,253 @@
package xyz.fycz.myreader.greendao.service;
import android.database.Cursor;
import android.util.Log;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.gen.ChapterDao;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.greendao.GreenDaoManager;
import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class ChapterService extends BaseService {
private List<Chapter> findChapters(String sql, String[] selectionArgs) {
ArrayList<Chapter> chapters = new ArrayList<>();
try {
Cursor cursor = selectBySql(sql, selectionArgs);
if (cursor == null) return chapters;
while (cursor.moveToNext()) {
Chapter chapter = new Chapter();
chapter.setId(cursor.getString(0));
chapter.setBookId(cursor.getString(1));
chapter.setNumber(cursor.getInt(2));
chapter.setTitle(cursor.getString(3));
chapter.setUrl(cursor.getString(4));
chapter.setContent(cursor.getString(5));
chapters.add(chapter);
}
} catch (Exception e) {
e.printStackTrace();
return chapters;
}
return chapters;
}
/**
* 通过ID查章节
*
* @param id
* @return
*/
public Chapter getChapterById(String id) {
ChapterDao chapterDao = GreenDaoManager.getInstance().getSession().getChapterDao();
return chapterDao.load(id);
}
/**
* 获取书的所有章节
*
* @return
*/
public List<Chapter> findBookAllChapterByBookId(String bookId) {
if (StringHelper.isEmpty(bookId)) return new ArrayList<>();
String sql = "select * from chapter where book_id = ? order by number";
return findChapters(sql, new String[]{bookId});
}
/**
* 新增章节
*
* @param chapter
*/
public void addChapter(Chapter chapter, String content) {
chapter.setId(StringHelper.getStringRandom(25));
addEntity(chapter);
saveChapterCacheFile(chapter, content);
}
/**
* 查找章节
*
* @param bookId
* @param title
* @return
*/
public Chapter findChapterByBookIdAndTitle(String bookId, String title) {
Chapter chapter = null;
try {
String sql = "select id from chapter where book_id = ? and title = ?";
Cursor cursor = selectBySql(sql, new String[]{bookId, title});
if (cursor == null) return null;
if (cursor.moveToNext()) {
String id = cursor.getString(0);
chapter = getChapterById(id);
}
} catch (Exception e) {
e.printStackTrace();
}
return chapter;
}
/**
* 删除书的所有章节
*
* @param bookId
*/
public void deleteBookALLChapterById(String bookId) {
String sel = "delete from chapter where book_id = ?";
rawQuery(sel, new String[]{bookId});
deleteAllChapterCacheFile(bookId);
}
/**
* 更新章节
*/
public void updateChapter(Chapter chapter) {
ChapterDao chapterDao = GreenDaoManager.getInstance().getSession().getChapterDao();
chapterDao.update(chapter);
}
/**
* 分段查找章节
*
* @param bookId
* @param from
* @param to
* @return
*/
public List<Chapter> findChapter(String bookId, int from, int to) {
String sql = "select * from " +
"(select row_number()over(order by number)rownumber,* from chapter where bookId = ? order by number)a " +
"where rownumber >= ? and rownumber <= ?";
return findChapters(sql, new String[]{bookId, String.valueOf(from), String.valueOf(to)});
}
/**
* 保存或更新章节
*
* @param chapter
*/
public void saveOrUpdateChapter(Chapter chapter, String content) {
chapter.setContent(APPCONST.BOOK_CACHE_PATH + chapter.getBookId()
+ File.separator + chapter.getTitle() + FileUtils.SUFFIX_FY);
if (!StringHelper.isEmpty(chapter.getId())) {
updateEntity(chapter);
} else {
addChapter(chapter, content);
}
saveChapterCacheFile(chapter, content);
}
/**
* 批量添加章节
*/
public void addChapters(List<Chapter> chapters) {
ChapterDao chapterDao = GreenDaoManager.getInstance().getSession().getChapterDao();
chapterDao.insertInTx(chapters);
}
/**
* 缓存章节
*
* @param chapter
*/
public void saveChapterCacheFile(Chapter chapter, String content) {
if (StringHelper.isEmpty(content)) {
return;
}
File file = getBookFile(chapter.getBookId(), chapter.getTitle());
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
bw.write(content.replace(chapter.getTitle(), ""));
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.close(bw);
}
}
/**
* 删除章节缓存
*
* @param chapter
*/
public void deleteChapterCacheFile(Chapter chapter) {
File file = getBookFile(chapter.getBookId(), chapter.getTitle());
file.delete();
}
/**
* 获取缓存章节内容
* @param chapter
* @return
*/
public String getChapterCatheContent(Chapter chapter){
File file = new File(APPCONST.BOOK_CACHE_PATH + chapter.getBookId()
+ File.separator + chapter.getTitle() + FileUtils.SUFFIX_FY);
if (!file.exists()) return null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
StringBuilder s = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null){
s.append(line);
s.append("\n");
}
return s.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}finally {
IOUtils.close(br);
}
}
/**
* 根据文件名判断是否被缓存过 (因为可能数据库显示被缓存过但是文件中却没有的情况所以需要根据文件判断是否被缓存
* )
*
* @param folderName : bookId
* @param fileName: chapterName
* @return
*/
public static boolean isChapterCached(String folderName, String fileName) {
File file = new File(APPCONST.BOOK_CACHE_PATH + folderName
+ File.separator + fileName + FileUtils.SUFFIX_FY);
return file.exists();
}
private void deleteAllChapterCacheFile(String bookId) {
FileUtils.deleteFile(APPCONST.BOOK_CACHE_PATH + bookId);
}
/**
* 创建或获取存储文件
*
* @param folderName
* @param fileName
* @return
*/
public static File getBookFile(String folderName, String fileName) {
return FileUtils.getFile(APPCONST.BOOK_CACHE_PATH + folderName
+ File.separator + fileName + FileUtils.SUFFIX_FY);
}
}

@ -0,0 +1,109 @@
package xyz.fycz.myreader.greendao.service;
import android.database.Cursor;
import xyz.fycz.myreader.greendao.entity.SearchHistory;
import xyz.fycz.myreader.greendao.gen.SearchHistoryDao;
import xyz.fycz.myreader.util.DateHelper;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.greendao.GreenDaoManager;
import java.util.ArrayList;
import java.util.Date;
public class SearchHistoryService extends BaseService {
private ArrayList<SearchHistory> findSearchHistorys(String sql, String[] selectionArgs) {
ArrayList<SearchHistory> searchHistories = new ArrayList<>();
try {
Cursor cursor = selectBySql(sql, selectionArgs);
if (cursor == null) return searchHistories;
while (cursor.moveToNext()) {
SearchHistory searchHistory = new SearchHistory();
searchHistory.setId(cursor.getString(0));
searchHistory.setContent(cursor.getString(1));
searchHistory.setCreateDate(cursor.getString(2));
searchHistories.add(searchHistory);
}
} catch (Exception e) {
e.printStackTrace();
return searchHistories;
}
return searchHistories;
}
/**
* 返回所有历史记录按时间从大到小
* @return
*/
public ArrayList<SearchHistory> findAllSearchHistory() {
String sql = "select * from search_history order by create_date desc";
return findSearchHistorys(sql, null);
}
/**
* 添加历史记录
* @param searchHistory
*/
public void addSearchHistory(SearchHistory searchHistory) {
searchHistory.setId(StringHelper.getStringRandom(25));
searchHistory.setCreateDate(DateHelper.longToTime(new Date().getTime()));
addEntity(searchHistory);
}
/**
* 删除历史记录
* @param searchHistory
*/
public void deleteHistory(SearchHistory searchHistory){
deleteEntity(searchHistory);
}
/**
* 清空历史记录
*/
public void clearHistory(){
SearchHistoryDao searchHistoryDao = GreenDaoManager.getInstance().getSession().getSearchHistoryDao();
searchHistoryDao.deleteAll();
}
/**
* 根据内容查找历史记录
* @param content
* @return
*/
public SearchHistory findHistoryByContent(String content){
SearchHistory searchHistory = null;
String sql = "select * from search_history where content = ?";
Cursor cursor = selectBySql(sql,new String[]{content});
if (cursor == null) return searchHistory;
if (cursor.moveToNext()){
searchHistory = new SearchHistory();
searchHistory.setId(cursor.getString(0));
searchHistory.setContent(cursor.getString(1));
searchHistory.setCreateDate(cursor.getString(2));
}
return searchHistory;
}
/**
* 添加或更新历史记录
* @param history
*/
public void addOrUpadteHistory(String history){
SearchHistory searchHistory = findHistoryByContent(history);
if (searchHistory == null){
searchHistory = new SearchHistory();
searchHistory.setContent(history);
addSearchHistory(searchHistory);
}else {
searchHistory.setCreateDate(DateHelper.longToTime(new Date().getTime()));
updateEntity(searchHistory);
}
}
}

@ -0,0 +1,164 @@
package xyz.fycz.myreader.greendao.util;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import xyz.fycz.myreader.greendao.gen.DaoMaster;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.internal.DaoConfig;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class GreenDaoUpgrade {
private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
private static GreenDaoUpgrade instance;
public static GreenDaoUpgrade getInstance() {
if (instance == null) {
instance = new GreenDaoUpgrade();
}
return instance;
}
private static List<String> getColumns(Database db, String tableName) {
List<String> columns = new ArrayList<>();
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
if (cursor != null) {
columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return columns;
}
public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateTempTables(db, daoClasses);
DaoMaster.dropAllTables(db, true);
DaoMaster.createAllTables(db, false);
restoreData(db, daoClasses);
}
private void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String divider = "";
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList<>();
StringBuilder createTableStringBuilder = new StringBuilder();
createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tableName).contains(columnName)) {
properties.add(columnName);
String type = null;
try {
type = getTypeByClass(daoConfig.properties[j].type);
} catch (Exception exception) {
exception.printStackTrace();
}
createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);
if (daoConfig.properties[j].primaryKey) {
createTableStringBuilder.append(" PRIMARY KEY");
}
divider = ",";
}
}
createTableStringBuilder.append(");");
db.execSQL(createTableStringBuilder.toString());
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(" FROM ").append(tableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
}
}
private void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList();
ArrayList<String> propertiesQuery = new ArrayList();
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tempTableName).contains(columnName)) {
properties.add(columnName);
propertiesQuery.add(columnName);
} else {
try {
if (getTypeByClass(daoConfig.properties[j].type).equals("INTEGER")) {
propertiesQuery.add("0 as " + columnName);
properties.add(columnName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", propertiesQuery));
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(insertTableStringBuilder.toString());
db.execSQL(dropTableStringBuilder.toString());
}
}
private String getTypeByClass(Class<?> type) throws Exception {
if (type.equals(String.class)) {
return "TEXT";
}
if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class) || type.equals(int.class)) {
return "INTEGER";
}
if (type.equals(Boolean.class) || type.equals(boolean.class)) {
return "BOOLEAN";
}
Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
exception.printStackTrace();
throw exception;
}
}

@ -0,0 +1,29 @@
package xyz.fycz.myreader.greendao.util;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import xyz.fycz.myreader.greendao.gen.*;
import org.greenrobot.greendao.database.Database;
public class MySQLiteOpenHelper extends DaoMaster.OpenHelper {
private Context mContext;
public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
mContext = context;
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
//加入你要新建的或者修改的表的信息
GreenDaoUpgrade.getInstance().migrate(db, BookDao.class, ChapterDao.class, SearchHistoryDao.class);
}
}

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

Loading…
Cancel
Save