parent
c8c36c1de4
commit
423cfcb897
@ -1,8 +0,0 @@ |
|||||||
# Default ignored files |
|
||||||
/shelf/ |
|
||||||
/workspace.xml |
|
||||||
# Datasource local storage ignored files |
|
||||||
/dataSources/ |
|
||||||
/dataSources.local.xml |
|
||||||
# Editor-based HTTP Client requests |
|
||||||
/httpRequests/ |
|
Binary file not shown.
@ -1,116 +0,0 @@ |
|||||||
<component name="ProjectCodeStyleConfiguration"> |
|
||||||
<code_scheme name="Project" version="173"> |
|
||||||
<codeStyleSettings language="XML"> |
|
||||||
<indentOptions> |
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" /> |
|
||||||
</indentOptions> |
|
||||||
<arrangement> |
|
||||||
<rules> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>xmlns:android</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>xmlns:.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>BY_NAME</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*:id</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*:name</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>name</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>style</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>BY_NAME</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>ANDROID_ATTRIBUTE_ORDER</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
<section> |
|
||||||
<rule> |
|
||||||
<match> |
|
||||||
<AND> |
|
||||||
<NAME>.*</NAME> |
|
||||||
<XML_ATTRIBUTE /> |
|
||||||
<XML_NAMESPACE>.*</XML_NAMESPACE> |
|
||||||
</AND> |
|
||||||
</match> |
|
||||||
<order>BY_NAME</order> |
|
||||||
</rule> |
|
||||||
</section> |
|
||||||
</rules> |
|
||||||
</arrangement> |
|
||||||
</codeStyleSettings> |
|
||||||
</code_scheme> |
|
||||||
</component> |
|
@ -1,11 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true"> |
|
||||||
<data-source source="LOCAL" name="fyreader" uuid="d7778050-1d7b-46c3-8f3e-6a61ef69ce07"> |
|
||||||
<driver-ref>mysql.8</driver-ref> |
|
||||||
<synchronize>true</synchronize> |
|
||||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver> |
|
||||||
<jdbc-url>jdbc:mysql://47.105.152.62:3306/fyreader</jdbc-url> |
|
||||||
</data-source> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,6 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="Encoding"> |
|
||||||
<file url="file://$PROJECT_DIR$/release/output.json" charset="GBK" /> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,36 +0,0 @@ |
|||||||
<component name="InspectionProjectProfileManager"> |
|
||||||
<profile version="1.0"> |
|
||||||
<option name="myName" value="Project Default" /> |
|
||||||
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true"> |
|
||||||
<option name="TOP_LEVEL_CLASS_OPTIONS"> |
|
||||||
<value> |
|
||||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> |
|
||||||
<option name="REQUIRED_TAGS" value="" /> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
<option name="INNER_CLASS_OPTIONS"> |
|
||||||
<value> |
|
||||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> |
|
||||||
<option name="REQUIRED_TAGS" value="" /> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
<option name="METHOD_OPTIONS"> |
|
||||||
<value> |
|
||||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> |
|
||||||
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" /> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
<option name="FIELD_OPTIONS"> |
|
||||||
<value> |
|
||||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> |
|
||||||
<option name="REQUIRED_TAGS" value="" /> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
<option name="IGNORE_DEPRECATED" value="false" /> |
|
||||||
<option name="IGNORE_JAVADOC_PERIOD" value="true" /> |
|
||||||
<option name="IGNORE_DUPLICATED_THROWS" value="false" /> |
|
||||||
<option name="IGNORE_POINT_TO_ITSELF" value="false" /> |
|
||||||
<option name="myAdditionalJavadocTags" value="date,r(oldStr," /> |
|
||||||
</inspection_tool> |
|
||||||
</profile> |
|
||||||
</component> |
|
@ -1,10 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="MarkdownEnhProjectSettings"> |
|
||||||
<AnnotatorSettings targetHasSpaces="true" linkCaseMismatch="true" wikiCaseMismatch="true" wikiLinkHasDashes="true" notUnderWikiHome="true" targetNotWikiPageExt="true" notUnderSourceWikiHome="true" targetNameHasAnchor="true" targetPathHasAnchor="true" wikiLinkHasSlash="true" wikiLinkHasSubdir="true" wikiLinkHasOnlyAnchor="true" linkTargetsWikiHasExt="true" linkTargetsWikiHasBadExt="true" notUnderSameRepo="true" targetNotUnderVcs="false" linkNeedsExt="true" linkHasBadExt="true" linkTargetNeedsExt="true" linkTargetHasBadExt="true" wikiLinkNotInWiki="true" imageTargetNotInRaw="true" repoRelativeAcrossVcsRoots="true" multipleWikiTargetsMatch="true" unresolvedLinkReference="true" linkIsIgnored="true" anchorIsIgnored="true" anchorIsUnresolved="true" anchorLineReferenceIsUnresolved="true" anchorLineReferenceFormat="true" anchorHasDuplicates="true" abbreviationDuplicates="true" abbreviationNotUsed="true" attributeIdDuplicateDefinition="true" attributeIdNotUsed="true" footnoteDuplicateDefinition="true" footnoteUnresolved="true" footnoteDuplicates="true" footnoteNotUsed="true" macroDuplicateDefinition="true" macroUnresolved="true" macroDuplicates="true" macroNotUsed="true" referenceDuplicateDefinition="true" referenceUnresolved="true" referenceDuplicates="true" referenceNotUsed="true" referenceUnresolvedNumericId="true" enumRefDuplicateDefinition="true" enumRefUnresolved="true" enumRefDuplicates="true" enumRefNotUsed="true" enumRefLinkUnresolved="true" enumRefLinkDuplicates="true" simTocUpdateNeeded="true" simTocTitleSpaceNeeded="true" /> |
|
||||||
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="css" scriptDir="js" plainHtml="false" imageDir="" copyLinkedImages="false" imagePathType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" /> |
|
||||||
<LinkMapSettings> |
|
||||||
<textMaps /> |
|
||||||
</LinkMapSettings> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,62 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="MarkdownProjectSettings"> |
|
||||||
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true" lastLayoutSetsDefault="false"> |
|
||||||
<PanelProvider> |
|
||||||
<provider providerId="com.vladsch.md.nav.editor.swing.html.panel" providerName="Default - Swing" /> |
|
||||||
</PanelProvider> |
|
||||||
</PreviewSettings> |
|
||||||
<ParserSettings gitHubSyntaxChange="false" correctedInvalidSettings="false" emojiShortcuts="1" emojiImages="0"> |
|
||||||
<PegdownExtensions> |
|
||||||
<option name="ATXHEADERSPACE" value="true" /> |
|
||||||
<option name="FENCED_CODE_BLOCKS" value="true" /> |
|
||||||
<option name="INTELLIJ_DUMMY_IDENTIFIER" value="true" /> |
|
||||||
<option name="RELAXEDHRULES" value="true" /> |
|
||||||
<option name="STRIKETHROUGH" value="true" /> |
|
||||||
<option name="TABLES" value="true" /> |
|
||||||
<option name="TASKLISTITEMS" value="true" /> |
|
||||||
</PegdownExtensions> |
|
||||||
<ParserOptions> |
|
||||||
<option name="COMMONMARK_LISTS" value="true" /> |
|
||||||
<option name="EMOJI_SHORTCUTS" value="true" /> |
|
||||||
<option name="GFM_TABLE_RENDERING" value="true" /> |
|
||||||
<option name="PRODUCTION_SPEC_PARSER" value="true" /> |
|
||||||
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" /> |
|
||||||
</ParserOptions> |
|
||||||
</ParserSettings> |
|
||||||
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" addPageHeader="false" addAnchorLinks="false" anchorLinksWrapText="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false" defaultUrlTitle="false" migratedPlantUml="true" migratedAnchorLinks="true" plantUmlConversion="0"> |
|
||||||
<GeneratorProvider> |
|
||||||
<provider providerId="com.vladsch.md.nav.editor.text.html.generator" providerName="Unmodified HTML Generator" /> |
|
||||||
</GeneratorProvider> |
|
||||||
<headerTop /> |
|
||||||
<headerBottom /> |
|
||||||
<bodyTop /> |
|
||||||
<bodyBottom /> |
|
||||||
<fencedCodeConversions> |
|
||||||
<option name="c4plantuml" value="NONE" /> |
|
||||||
<option name="ditaa" value="NONE" /> |
|
||||||
<option name="erd" value="NONE" /> |
|
||||||
<option name="graphviz" value="NONE" /> |
|
||||||
<option name="latex" value="KATEX" /> |
|
||||||
<option name="math" value="KATEX" /> |
|
||||||
<option name="mermaid" value="NONE" /> |
|
||||||
<option name="nomnoml" value="NONE" /> |
|
||||||
<option name="plantuml" value="NONE" /> |
|
||||||
<option name="puml" value="NONE" /> |
|
||||||
<option name="svgbob" value="NONE" /> |
|
||||||
<option name="umlet" value="NONE" /> |
|
||||||
<option name="vega" value="NONE" /> |
|
||||||
<option name="vegalite" value="NONE" /> |
|
||||||
<option name="wavedrom" value="NONE" /> |
|
||||||
</fencedCodeConversions> |
|
||||||
</HtmlSettings> |
|
||||||
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true"> |
|
||||||
<StylesheetProvider> |
|
||||||
<provider providerId="com.vladsch.md.nav.editor.text.html.css" providerName="No Stylesheet" /> |
|
||||||
</StylesheetProvider> |
|
||||||
<ScriptProviders /> |
|
||||||
<cssText /> |
|
||||||
<cssUriHistory /> |
|
||||||
</CssSettings> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,57 +1,4 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||||
<project version="4"> |
<project version="4"> |
||||||
<component name="FrameworkDetectionExcludesConfiguration"> |
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="Android API 29 Platform" project-jdk-type="Android SDK" /> |
||||||
<file type="web" url="file://$PROJECT_DIR$" /> |
|
||||||
</component> |
|
||||||
<component name="NullableNotNullManager"> |
|
||||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" /> |
|
||||||
<option name="myDefaultNotNull" value="io.reactivex.annotations.NonNull" /> |
|
||||||
<option name="myNullables"> |
|
||||||
<value> |
|
||||||
<list size="15"> |
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> |
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> |
|
||||||
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" /> |
|
||||||
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> |
|
||||||
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> |
|
||||||
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" /> |
|
||||||
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" /> |
|
||||||
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" /> |
|
||||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" /> |
|
||||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" /> |
|
||||||
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.Nullable" /> |
|
||||||
<item index="11" class="java.lang.String" itemvalue="android.annotation.Nullable" /> |
|
||||||
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" /> |
|
||||||
<item index="13" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" /> |
|
||||||
<item index="14" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" /> |
|
||||||
</list> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
<option name="myNotNulls"> |
|
||||||
<value> |
|
||||||
<list size="14"> |
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> |
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> |
|
||||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> |
|
||||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> |
|
||||||
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" /> |
|
||||||
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" /> |
|
||||||
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" /> |
|
||||||
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" /> |
|
||||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" /> |
|
||||||
<item index="9" class="java.lang.String" itemvalue="com.android.annotations.NonNull" /> |
|
||||||
<item index="10" class="java.lang.String" itemvalue="android.annotation.NonNull" /> |
|
||||||
<item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" /> |
|
||||||
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" /> |
|
||||||
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" /> |
|
||||||
</list> |
|
||||||
</value> |
|
||||||
</option> |
|
||||||
</component> |
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> |
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" /> |
|
||||||
</component> |
|
||||||
<component name="ProjectType"> |
|
||||||
<option name="id" value="Android" /> |
|
||||||
</component> |
|
||||||
</project> |
</project> |
@ -1,55 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="navEditor-manualLayoutAlgorithm2"> |
|
||||||
<option name="myPositions"> |
|
||||||
<map> |
|
||||||
<entry key="mobile_navigation.xml"> |
|
||||||
<value> |
|
||||||
<LayoutPositions> |
|
||||||
<option name="myPositions"> |
|
||||||
<map> |
|
||||||
<entry key="navigation_dashboard"> |
|
||||||
<value> |
|
||||||
<LayoutPositions> |
|
||||||
<option name="myPosition"> |
|
||||||
<Point> |
|
||||||
<option name="x" value="256" /> |
|
||||||
<option name="y" value="12" /> |
|
||||||
</Point> |
|
||||||
</option> |
|
||||||
</LayoutPositions> |
|
||||||
</value> |
|
||||||
</entry> |
|
||||||
<entry key="navigation_home"> |
|
||||||
<value> |
|
||||||
<LayoutPositions> |
|
||||||
<option name="myPosition"> |
|
||||||
<Point> |
|
||||||
<option name="x" value="12" /> |
|
||||||
<option name="y" value="12" /> |
|
||||||
</Point> |
|
||||||
</option> |
|
||||||
</LayoutPositions> |
|
||||||
</value> |
|
||||||
</entry> |
|
||||||
<entry key="navigation_notifications"> |
|
||||||
<value> |
|
||||||
<LayoutPositions> |
|
||||||
<option name="myPosition"> |
|
||||||
<Point> |
|
||||||
<option name="x" value="12" /> |
|
||||||
<option name="y" value="368" /> |
|
||||||
</Point> |
|
||||||
</option> |
|
||||||
</LayoutPositions> |
|
||||||
</value> |
|
||||||
</entry> |
|
||||||
</map> |
|
||||||
</option> |
|
||||||
</LayoutPositions> |
|
||||||
</value> |
|
||||||
</entry> |
|
||||||
</map> |
|
||||||
</option> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,12 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="RunConfigurationProducerService"> |
|
||||||
<option name="ignoredProducers"> |
|
||||||
<set> |
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> |
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> |
|
||||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> |
|
||||||
</set> |
|
||||||
</option> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,7 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="SqlDialectMappings"> |
|
||||||
<file url="file://$PROJECT_DIR$/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java" dialect="GenericSQL" /> |
|
||||||
<file url="PROJECT" dialect="MySQL" /> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,124 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<project version="4"> |
|
||||||
<component name="Palette2"> |
|
||||||
<group name="Swing"> |
|
||||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> |
|
||||||
</item> |
|
||||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> |
|
||||||
<initial-values> |
|
||||||
<property name="text" value="Button" /> |
|
||||||
</initial-values> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> |
|
||||||
<initial-values> |
|
||||||
<property name="text" value="RadioButton" /> |
|
||||||
</initial-values> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> |
|
||||||
<initial-values> |
|
||||||
<property name="text" value="CheckBox" /> |
|
||||||
</initial-values> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> |
|
||||||
<initial-values> |
|
||||||
<property name="text" value="Label" /> |
|
||||||
</initial-values> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> |
|
||||||
<preferred-size width="150" height="-1" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> |
|
||||||
<preferred-size width="150" height="-1" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> |
|
||||||
<preferred-size width="150" height="-1" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
|
||||||
<preferred-size width="150" height="50" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
|
||||||
<preferred-size width="150" height="50" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
|
||||||
<preferred-size width="150" height="50" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
|
||||||
<preferred-size width="150" height="50" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> |
|
||||||
<preferred-size width="150" height="50" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
|
||||||
<preferred-size width="150" height="50" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> |
|
||||||
<preferred-size width="200" height="200" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> |
|
||||||
<preferred-size width="200" height="200" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> |
|
||||||
<preferred-size width="-1" height="20" /> |
|
||||||
</default-constraints> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> |
|
||||||
</item> |
|
||||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> |
|
||||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> |
|
||||||
</item> |
|
||||||
</group> |
|
||||||
</component> |
|
||||||
</project> |
|
@ -1,6 +1,6 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||||
<project version="4"> |
<project version="4"> |
||||||
<component name="VcsDirectoryMappings"> |
<component name="VcsDirectoryMappings"> |
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" /> |
<mapping directory="" vcs="Git" /> |
||||||
</component> |
</component> |
||||||
</project> |
</project> |
@ -0,0 +1,18 @@ |
|||||||
|
{ |
||||||
|
"version": 2, |
||||||
|
"artifactType": { |
||||||
|
"type": "APK", |
||||||
|
"kind": "Directory" |
||||||
|
}, |
||||||
|
"applicationId": "xyz.fycz.myreader", |
||||||
|
"variantName": "processDebugResources", |
||||||
|
"elements": [ |
||||||
|
{ |
||||||
|
"type": "SINGLE", |
||||||
|
"filters": [], |
||||||
|
"versionCode": 193, |
||||||
|
"versionName": "1.9.3", |
||||||
|
"outputFile": "风月读书v1.9.3.apk" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,18 @@ |
|||||||
|
{ |
||||||
|
"version": 2, |
||||||
|
"artifactType": { |
||||||
|
"type": "APK", |
||||||
|
"kind": "Directory" |
||||||
|
}, |
||||||
|
"applicationId": "xyz.fycz.myreader", |
||||||
|
"variantName": "processReleaseResources", |
||||||
|
"elements": [ |
||||||
|
{ |
||||||
|
"type": "SINGLE", |
||||||
|
"filters": [], |
||||||
|
"versionCode": 194, |
||||||
|
"versionName": "1.9.4", |
||||||
|
"outputFile": "风月读书v1.9.4.apk" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,182 @@ |
|||||||
|
package xyz.fycz.myreader.ai; |
||||||
|
|
||||||
|
import android.util.Log; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Random; |
||||||
|
|
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Chapter; |
||||||
|
import xyz.fycz.myreader.greendao.service.ChapterService; |
||||||
|
|
||||||
|
/** |
||||||
|
* 预测书籍字数 |
||||||
|
* |
||||||
|
* @author fengyue |
||||||
|
* @date 2021/4/18 16:54 |
||||||
|
*/ |
||||||
|
public class BookWordCountPre { |
||||||
|
private static final String TAG = BookWordCountPre.class.getSimpleName(); |
||||||
|
|
||||||
|
private double lr = 0.001; |
||||||
|
private Book book; |
||||||
|
private List<Chapter> chapters; |
||||||
|
private double[][] trainData; |
||||||
|
private double[][] target; |
||||||
|
private double[][] weights1; |
||||||
|
//private double[][] weights2;
|
||||||
|
private double[][] testData; |
||||||
|
private double median; |
||||||
|
private static Random rand = new Random(); |
||||||
|
|
||||||
|
|
||||||
|
public BookWordCountPre(Book book) { |
||||||
|
this.book = book; |
||||||
|
this.chapters = ChapterService.getInstance().findBookAllChapterByBookId(book.getId()); |
||||||
|
} |
||||||
|
|
||||||
|
//进行训练
|
||||||
|
public boolean train() { |
||||||
|
if (!preData()) { |
||||||
|
Log.i(TAG, String.format("《%s》缓存章节数量过少,无法进行训练", book.getName())); |
||||||
|
return false; |
||||||
|
} |
||||||
|
Log.i(TAG, String.format("《%s》开始进行训练", book.getName())); |
||||||
|
double loss = 0; |
||||||
|
double eps = 0.0000000001; |
||||||
|
double[][] gradient1; |
||||||
|
//double[][] gradient2;
|
||||||
|
double[][] adagrad1 = new double[trainData[0].length][1]; |
||||||
|
//double[][] adagrad2 = new double[trainData[0].length][1];
|
||||||
|
//double[][] dl_dw = new double[trainData[0].length][1];
|
||||||
|
int maxEpoch; |
||||||
|
maxEpoch = 1000 / trainData.length; |
||||||
|
if (maxEpoch < 10) maxEpoch = 10; |
||||||
|
for (int epoch = 0; epoch < maxEpoch; epoch++) { |
||||||
|
shuffle(trainData, target); |
||||||
|
for (int j = 0; j < trainData.length; j++) { |
||||||
|
double[][] oneData = MatrixUtil.to2dMatrix(trainData[j], false); |
||||||
|
double[][] oneTarget = MatrixUtil.to2dMatrix(target[j], true); |
||||||
|
double[][] out = getOut(oneData); |
||||||
|
loss = Math.sqrt(MatrixUtil.sum(MatrixUtil.pow(MatrixUtil.sub(out, oneTarget), 2)) / 2); |
||||||
|
/*dl_dw = MatrixUtil.sub( |
||||||
|
MatrixUtil.add( |
||||||
|
MatrixUtil.dot(MatrixUtil.pow(oneData, 2), weights2), |
||||||
|
MatrixUtil.dot(oneData, weights2)), |
||||||
|
oneTarget |
||||||
|
);*/ |
||||||
|
|
||||||
|
gradient1 = MatrixUtil.dot(MatrixUtil.transpose(oneData), MatrixUtil.sub(out, oneTarget)); |
||||||
|
//gradient1 = MatrixUtil.dot(MatrixUtil.transpose(MatrixUtil.pow(oneData, 2)), dl_dw);
|
||||||
|
//gradient2 = MatrixUtil.dot(MatrixUtil.transpose(oneData), dl_dw);
|
||||||
|
adagrad1 = MatrixUtil.add(adagrad1, MatrixUtil.pow(gradient1, 2)); |
||||||
|
//adagrad2 = MatrixUtil.add(adagrad1, MatrixUtil.pow(gradient2, 2));
|
||||||
|
weights1 = MatrixUtil.sub(weights1, MatrixUtil.divide(MatrixUtil.dot(gradient1, lr), |
||||||
|
MatrixUtil.sqrt(MatrixUtil.add(adagrad1, eps)))); |
||||||
|
/*weights2 = MatrixUtil.sub(weights2, MatrixUtil.divide(MatrixUtil.dot(gradient2, lr), |
||||||
|
MatrixUtil.sqrt(MatrixUtil.add(adagrad2, eps))));*/ |
||||||
|
} |
||||||
|
Log.i(TAG, String.format("《%s》-> epoch=%d,loss=%f", book.getName(), epoch, loss)); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
//进行预测并获得书籍总字数
|
||||||
|
public int predict() { |
||||||
|
double[][] pre = getOut(testData); |
||||||
|
double[] preVec = MatrixUtil.toVector(pre); |
||||||
|
Arrays.sort(preVec); |
||||||
|
int k = (int) (preVec[preVec.length / 2 + 1] / median); |
||||||
|
//int k = (int) ((MatrixUtil.sum(pre) / pre.length) / median);
|
||||||
|
pre = MatrixUtil.divide(pre, k); |
||||||
|
/*for (int i = 0; i < pre.length; i++) { |
||||||
|
pre[i][0] = median; |
||||||
|
}*/ |
||||||
|
Log.i(TAG, String.format("k=%d->《%s》的预测数据%s", k, book.getName(), |
||||||
|
Arrays.toString(MatrixUtil.toVector(pre)))); |
||||||
|
return (int) (MatrixUtil.sum(pre) + MatrixUtil.sum(target)); |
||||||
|
} |
||||||
|
|
||||||
|
private double[][] getOut(double[][] data) { |
||||||
|
/*return MatrixUtil.add(MatrixUtil.dot(MatrixUtil.pow(data, 2), weights2), |
||||||
|
MatrixUtil.dot(data, weights1));*/ |
||||||
|
return MatrixUtil.dot(data, weights1); |
||||||
|
} |
||||||
|
|
||||||
|
//准备训练数据
|
||||||
|
private boolean preData() { |
||||||
|
rand.setSeed(10); |
||||||
|
List<Chapter> catheChapters = new ArrayList<>(); |
||||||
|
List<Chapter> unCatheChapters = new ArrayList<>(); |
||||||
|
//章节最长标题长度
|
||||||
|
int maxTitleLen = 0; |
||||||
|
//获取已缓存章节
|
||||||
|
for (Chapter chapter : chapters) { |
||||||
|
if (ChapterService.isChapterCached(book.getId(), chapter.getTitle())) { |
||||||
|
catheChapters.add(chapter); |
||||||
|
} else { |
||||||
|
unCatheChapters.add(chapter); |
||||||
|
} |
||||||
|
if (maxTitleLen < chapter.getTitle().length()) { |
||||||
|
maxTitleLen = chapter.getTitle().length(); |
||||||
|
} |
||||||
|
} |
||||||
|
Log.i(TAG, String.format("《%s》已缓存章节数量:%d,最大章节标题长度:%d", |
||||||
|
book.getName(), catheChapters.size(), maxTitleLen)); |
||||||
|
if (catheChapters.size() <= 10) return false; |
||||||
|
//创建训练数据
|
||||||
|
trainData = new double[catheChapters.size()][maxTitleLen + 1]; |
||||||
|
//创建测试数据
|
||||||
|
testData = new double[chapters.size() - catheChapters.size()][maxTitleLen + 1]; |
||||||
|
//创建权重矩阵
|
||||||
|
weights1 = new double[maxTitleLen + 1][1]; |
||||||
|
//weights2 = new double[maxTitleLen + 1][1];
|
||||||
|
//创建目标矩阵
|
||||||
|
target = new double[catheChapters.size()][1]; |
||||||
|
for (int i = 0; i < catheChapters.size(); i++) { |
||||||
|
Chapter chapter = catheChapters.get(i); |
||||||
|
char[] charArr = chapter.getTitle().replaceAll("[((【{]", "").toCharArray(); |
||||||
|
for (int j = 0; j < charArr.length; j++) { |
||||||
|
trainData[i][j] = charArr[j]; |
||||||
|
} |
||||||
|
trainData[i][maxTitleLen] = 1; |
||||||
|
target[i][0] = ChapterService.countChar(book.getId(), chapter.getTitle()); |
||||||
|
} |
||||||
|
for (int i = 0; i < maxTitleLen + 1; i++) { |
||||||
|
weights1[i][0] = rand.nextDouble(); |
||||||
|
//weights2[i][0] = Math.random();
|
||||||
|
} |
||||||
|
for (int i = 0; i < unCatheChapters.size(); i++) { |
||||||
|
Chapter chapter = unCatheChapters.get(i); |
||||||
|
char[] charArr = chapter.getTitle().toCharArray(); |
||||||
|
for (int j = 0; j < charArr.length; j++) { |
||||||
|
testData[i][j] = charArr[j]; |
||||||
|
} |
||||||
|
testData[i][maxTitleLen] = 1; |
||||||
|
} |
||||||
|
/*double[] tem = MatrixUtil.toVector(target); |
||||||
|
Arrays.sort(tem); |
||||||
|
median = tem[tem.length / 2 + 1];*/ |
||||||
|
median = MatrixUtil.sum(target) / target.length; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static <T> void swap(T[] a, int i, int j) { |
||||||
|
T temp = a[i]; |
||||||
|
a[i] = a[j]; |
||||||
|
a[j] = temp; |
||||||
|
} |
||||||
|
|
||||||
|
public static <T> void shuffle(T[]... arr) { |
||||||
|
int length = arr[0].length; |
||||||
|
for (int i = length; i > 0; i--) { |
||||||
|
int randInd = rand.nextInt(i); |
||||||
|
for (T[] ts : arr) { |
||||||
|
swap(ts, randInd, i - 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,295 @@ |
|||||||
|
package xyz.fycz.myreader.ai; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2021/4/7 16:10 |
||||||
|
*/ |
||||||
|
public class MatrixUtil { |
||||||
|
//矩阵加法 C=A+B
|
||||||
|
public static double[][] add(double[][] m1, double[][] m2) { |
||||||
|
if (m1 == null || m2 == null || |
||||||
|
m1.length != m2.length || |
||||||
|
m1[0].length != m2[0].length) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
double[][] m = new double[m1.length][m1[0].length]; |
||||||
|
|
||||||
|
for (int i = 0; i < m.length; ++i) { |
||||||
|
for (int j = 0; j < m[i].length; ++j) { |
||||||
|
m[i][j] = m1[i][j] + m2[i][j]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return m; |
||||||
|
} |
||||||
|
|
||||||
|
public static double[][] add(double[][] m, double a) { |
||||||
|
if (m == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
double[][] retM = new double[m.length][m[0].length]; |
||||||
|
|
||||||
|
for (int i = 0; i < retM.length; ++i) { |
||||||
|
for (int j = 0; j < retM[i].length; ++j) { |
||||||
|
retM[i][j] = m[i][j] + a; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return retM; |
||||||
|
} |
||||||
|
|
||||||
|
public static double[][] sub(double[][] m1, double[][] m2) { |
||||||
|
if (m1 == null || m2 == null || |
||||||
|
m1.length != m2.length || |
||||||
|
m1[0].length != m2[0].length) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
double[][] m = new double[m1.length][m1[0].length]; |
||||||
|
|
||||||
|
for (int i = 0; i < m.length; ++i) { |
||||||
|
for (int j = 0; j < m[i].length; ++j) { |
||||||
|
m[i][j] = m1[i][j] - m2[i][j]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return m; |
||||||
|
} |
||||||
|
|
||||||
|
//矩阵转置
|
||||||
|
public static double[][] transpose(double[][] m) { |
||||||
|
if (m == null) return null; |
||||||
|
double[][] mt = new double[m[0].length][m.length]; |
||||||
|
for (int i = 0; i < m.length; ++i) { |
||||||
|
for (int j = 0; j < m[i].length; ++j) { |
||||||
|
mt[j][i] = m[i][j]; |
||||||
|
} |
||||||
|
} |
||||||
|
return mt; |
||||||
|
} |
||||||
|
|
||||||
|
//矩阵相乘 C=A*B
|
||||||
|
public static double[][] dot(double[][] m1, double[][] m2) { |
||||||
|
if (m1 == null || m2 == null || m1[0].length != m2.length) |
||||||
|
return null; |
||||||
|
|
||||||
|
double[][] m = new double[m1.length][m2[0].length]; |
||||||
|
for (int i = 0; i < m1.length; ++i) { |
||||||
|
for (int j = 0; j < m2[0].length; ++j) { |
||||||
|
for (int k = 0; k < m1[i].length; ++k) { |
||||||
|
m[i][j] += m1[i][k] * m2[k][j]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return m; |
||||||
|
} |
||||||
|
|
||||||
|
//数乘矩阵
|
||||||
|
public static double[][] dot(double[][] m, double k) { |
||||||
|
if (m == null) return null; |
||||||
|
double[][] retM = new double[m.length][m[0].length]; |
||||||
|
for (int i = 0; i < m.length; i++) { |
||||||
|
for (int j = 0; j < m[0].length; j++) { |
||||||
|
retM[i][j] = m[i][j] * k; |
||||||
|
} |
||||||
|
} |
||||||
|
return retM; |
||||||
|
} |
||||||
|
|
||||||
|
//同型矩阵除法
|
||||||
|
public static double[][] divide(double[][] m1, double[][] m2) { |
||||||
|
if (m1 == null || m2 == null || |
||||||
|
m1.length != m2.length || |
||||||
|
m1[0].length != m2[0].length) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
double[][] retM = new double[m1.length][m1[0].length]; |
||||||
|
for (int i = 0; i < retM.length; ++i) { |
||||||
|
for (int j = 0; j < retM[i].length; ++j) { |
||||||
|
retM[i][j] = m1[i][j] / m2[i][j]; |
||||||
|
} |
||||||
|
} |
||||||
|
return retM; |
||||||
|
} |
||||||
|
|
||||||
|
//矩阵除数
|
||||||
|
public static double[][] divide(double[][] m, double k) { |
||||||
|
if (m == null) return null; |
||||||
|
double[][] retM = new double[m.length][m[0].length]; |
||||||
|
for (int i = 0; i < m.length; i++) { |
||||||
|
for (int j = 0; j < m[0].length; j++) { |
||||||
|
retM[i][j] = m[i][j] / k; |
||||||
|
} |
||||||
|
} |
||||||
|
return retM; |
||||||
|
} |
||||||
|
|
||||||
|
//求矩阵行列式(需为方阵)
|
||||||
|
public static double det(double[][] m) { |
||||||
|
if (m == null || m.length != m[0].length) |
||||||
|
return 0; |
||||||
|
|
||||||
|
if (m.length == 1) |
||||||
|
return m[0][0]; |
||||||
|
else if (m.length == 2) |
||||||
|
return det2(m); |
||||||
|
else if (m.length == 3) |
||||||
|
return det3(m); |
||||||
|
else { |
||||||
|
int re = 0; |
||||||
|
for (int i = 0; i < m.length; ++i) { |
||||||
|
re += (((i + 1) % 2) * 2 - 1) * det(companion(m, i, 0)) * m[i][0]; |
||||||
|
} |
||||||
|
return re; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//求二阶行列式
|
||||||
|
public static double det2(double[][] m) { |
||||||
|
if (m == null || m.length != 2 || m[0].length != 2) |
||||||
|
return 0; |
||||||
|
|
||||||
|
return m[0][0] * m[1][1] - m[1][0] * m[0][1]; |
||||||
|
} |
||||||
|
|
||||||
|
//求三阶行列式
|
||||||
|
public static double det3(double[][] m) { |
||||||
|
if (m == null || m.length != 3 || m[0].length != 3) |
||||||
|
return 0; |
||||||
|
|
||||||
|
double re = 0; |
||||||
|
for (int i = 0; i < 3; ++i) { |
||||||
|
int temp1 = 1; |
||||||
|
for (int j = 0, k = i; j < 3; ++j, ++k) { |
||||||
|
temp1 *= m[j][k % 3]; |
||||||
|
} |
||||||
|
re += temp1; |
||||||
|
temp1 = 1; |
||||||
|
for (int j = 0, k = i; j < 3; ++j, --k) { |
||||||
|
if (k < 0) k += 3; |
||||||
|
temp1 *= m[j][k]; |
||||||
|
} |
||||||
|
re -= temp1; |
||||||
|
} |
||||||
|
|
||||||
|
return re; |
||||||
|
} |
||||||
|
|
||||||
|
//求矩阵的逆(需方阵)
|
||||||
|
public static double[][] inv(double[][] m) { |
||||||
|
if (m == null || m.length != m[0].length) |
||||||
|
return null; |
||||||
|
|
||||||
|
double A = det(m); |
||||||
|
double[][] mi = new double[m.length][m[0].length]; |
||||||
|
for (int i = 0; i < m.length; ++i) { |
||||||
|
for (int j = 0; j < m[i].length; ++j) { |
||||||
|
double[][] temp = companion(m, i, j); |
||||||
|
mi[j][i] = (((i + j + 1) % 2) * 2 - 1) * det(temp) / A; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return mi; |
||||||
|
} |
||||||
|
|
||||||
|
//求方阵代数余子式
|
||||||
|
public static double[][] companion(double[][] m, int x, int y) { |
||||||
|
if (m == null || m.length <= x || m[0].length <= y || |
||||||
|
m.length == 1 || m[0].length == 1) |
||||||
|
return null; |
||||||
|
|
||||||
|
double[][] cm = new double[m.length - 1][m[0].length - 1]; |
||||||
|
|
||||||
|
int dx = 0; |
||||||
|
for (int i = 0; i < m.length; ++i) { |
||||||
|
if (i != x) { |
||||||
|
int dy = 0; |
||||||
|
for (int j = 0; j < m[i].length; ++j) { |
||||||
|
if (j != y) { |
||||||
|
cm[dx][dy++] = m[i][j]; |
||||||
|
} |
||||||
|
} |
||||||
|
++dx; |
||||||
|
} |
||||||
|
} |
||||||
|
return cm; |
||||||
|
} |
||||||
|
|
||||||
|
//生成全为0的矩阵
|
||||||
|
public static double[][] zeros(int rows, int cols){ |
||||||
|
return new double[rows][cols]; |
||||||
|
} |
||||||
|
|
||||||
|
//生成全为1的矩阵
|
||||||
|
public static double[][] ones(int rows, int cols){ |
||||||
|
return add(zeros(rows, cols), 1); |
||||||
|
} |
||||||
|
|
||||||
|
public static double sum(double[][] matrix){ |
||||||
|
double sum = 0; |
||||||
|
for (double[] doubles : matrix) { |
||||||
|
for (double aDouble : doubles) { |
||||||
|
sum += aDouble; |
||||||
|
} |
||||||
|
} |
||||||
|
return sum; |
||||||
|
} |
||||||
|
|
||||||
|
public static double[][] pow(double[][] matrix, int exponent){ |
||||||
|
if (matrix == null) return null; |
||||||
|
double[][] retM = new double[matrix.length][matrix[0].length]; |
||||||
|
for (int i = 0; i < matrix.length; i++) { |
||||||
|
for (int j = 0; j < matrix[i].length; j++) { |
||||||
|
retM[i][j] = Math.pow(matrix[i][j], exponent); |
||||||
|
} |
||||||
|
} |
||||||
|
return retM; |
||||||
|
} |
||||||
|
|
||||||
|
public static double[][] sqrt(double[][] matrix){ |
||||||
|
if (matrix == null) return null; |
||||||
|
double[][] retM = new double[matrix.length][matrix[0].length]; |
||||||
|
for (int i = 0; i < matrix.length; i++) { |
||||||
|
for (int j = 0; j < matrix[i].length; j++) { |
||||||
|
retM[i][j] = Math.sqrt(matrix[i][j]); |
||||||
|
} |
||||||
|
} |
||||||
|
return matrix; |
||||||
|
} |
||||||
|
|
||||||
|
public static double[][] to2dMatrix(double[] vector, boolean isCol){ |
||||||
|
if (vector == null) return null; |
||||||
|
double[][] retM; |
||||||
|
if (isCol) { |
||||||
|
retM = new double[vector.length][1]; |
||||||
|
}else { |
||||||
|
retM = new double[1][vector.length]; |
||||||
|
} |
||||||
|
for (int i = 0; i < vector.length; i++) { |
||||||
|
if (isCol) { |
||||||
|
retM[i][0] = vector[i]; |
||||||
|
}else { |
||||||
|
retM[0][i] = vector[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
return retM; |
||||||
|
} |
||||||
|
|
||||||
|
public static double[] toVector(double[][] matrix){ |
||||||
|
double[] retV = null; |
||||||
|
if (matrix.length == 1){ |
||||||
|
retV = new double[matrix[0].length]; |
||||||
|
double[] doubles = matrix[0]; |
||||||
|
System.arraycopy(doubles, 0, retV, 0, doubles.length); |
||||||
|
}else if (matrix[0].length == 1){ |
||||||
|
retV = new double[matrix.length]; |
||||||
|
for (int i = 0; i < matrix.length; i++) { |
||||||
|
retV[i] = matrix[i][0]; |
||||||
|
} |
||||||
|
} |
||||||
|
return retV; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
package xyz.fycz.myreader.ui.activity; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import androidx.appcompat.widget.Toolbar; |
||||||
|
|
||||||
|
import io.reactivex.Single; |
||||||
|
import io.reactivex.annotations.NonNull; |
||||||
|
import io.reactivex.disposables.Disposable; |
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.application.SysManager; |
||||||
|
import xyz.fycz.myreader.base.BaseActivity; |
||||||
|
import xyz.fycz.myreader.base.observer.MySingleObserver; |
||||||
|
import xyz.fycz.myreader.databinding.ActivityAdSettingBinding; |
||||||
|
import xyz.fycz.myreader.ui.dialog.DialogCreator; |
||||||
|
import xyz.fycz.myreader.ui.dialog.LoadingDialog; |
||||||
|
import xyz.fycz.myreader.ui.dialog.MyAlertDialog; |
||||||
|
import xyz.fycz.myreader.util.DateHelper; |
||||||
|
import xyz.fycz.myreader.util.SharedPreUtils; |
||||||
|
import xyz.fycz.myreader.util.ToastUtils; |
||||||
|
import xyz.fycz.myreader.util.utils.AdUtils; |
||||||
|
import xyz.fycz.myreader.util.utils.FileUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2021/4/23 12:51 |
||||||
|
*/ |
||||||
|
public class AdSettingActivity extends BaseActivity { |
||||||
|
|
||||||
|
private ActivityAdSettingBinding binding; |
||||||
|
private LoadingDialog loadingDialog; |
||||||
|
private SharedPreUtils spu; |
||||||
|
private int curAdTimes; |
||||||
|
private int curAdCount; |
||||||
|
private boolean bookDetailAd; |
||||||
|
private Disposable cancelDis; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void bindView() { |
||||||
|
binding = ActivityAdSettingBinding.inflate(getLayoutInflater()); |
||||||
|
setContentView(binding.getRoot()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void setUpToolbar(Toolbar toolbar) { |
||||||
|
super.setUpToolbar(toolbar); |
||||||
|
setStatusBarColor(R.color.colorPrimary, true); |
||||||
|
getSupportActionBar().setTitle(getString(R.string.ad_setting)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initData(Bundle savedInstanceState) { |
||||||
|
spu = SharedPreUtils.getInstance(); |
||||||
|
curAdTimes = spu.getInt("curAdTimes", 3); |
||||||
|
String splashAdCount = spu.getString("splashAdCount"); |
||||||
|
String[] splashAdCounts = splashAdCount.split(":"); |
||||||
|
String today = DateHelper.getYearMonthDay1(); |
||||||
|
if (today.equals(splashAdCounts[0])){ |
||||||
|
curAdCount = Integer.parseInt(splashAdCounts[1]); |
||||||
|
}else { |
||||||
|
curAdCount = 0; |
||||||
|
} |
||||||
|
bookDetailAd = spu.getBoolean("bookDetailAd", false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initWidget() { |
||||||
|
loadingDialog = new LoadingDialog(this, "正在加载", () -> { |
||||||
|
if (cancelDis != null && !cancelDis.isDisposed()) { |
||||||
|
cancelDis.dispose(); |
||||||
|
} |
||||||
|
}); |
||||||
|
loadingDialog.show(); |
||||||
|
AdUtils.checkHasAd().subscribe(new MySingleObserver<Boolean>() { |
||||||
|
@Override |
||||||
|
public void onSubscribe(Disposable d) { |
||||||
|
cancelDis = d; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onSuccess(@NonNull Boolean aBoolean) { |
||||||
|
binding.scAd.setChecked(aBoolean); |
||||||
|
if (aBoolean) { |
||||||
|
binding.llAdSetting.setVisibility(View.VISIBLE); |
||||||
|
} |
||||||
|
loadingDialog.dismiss(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Throwable e) { |
||||||
|
loadingDialog.dismiss(); |
||||||
|
} |
||||||
|
}); |
||||||
|
String curAdTimesStr = getAdTimesStr(curAdTimes); |
||||||
|
binding.tvSplashCurAdTimes.setText(getString(R.string.splash_cur_ad_times, curAdTimesStr, curAdCount + "次")); |
||||||
|
binding.scBookDetailAd.setChecked(bookDetailAd); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initClick() { |
||||||
|
binding.llSplashAdTimes.setOnClickListener(v -> { |
||||||
|
loadingDialog.show(); |
||||||
|
AdUtils.adTimes().subscribe(new MySingleObserver<int[]>() { |
||||||
|
@Override |
||||||
|
public void onSubscribe(Disposable d) { |
||||||
|
cancelDis = d; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onSuccess(@NonNull int[] ints) { |
||||||
|
loadingDialog.dismiss(); |
||||||
|
int checked = 0; |
||||||
|
CharSequence[] adTimes = new CharSequence[ints.length]; |
||||||
|
for (int i = 0; i < ints.length; i++) { |
||||||
|
int k = ints[i]; |
||||||
|
adTimes[i] = getAdTimesStr(k); |
||||||
|
if (k == curAdTimes) { |
||||||
|
checked = i; |
||||||
|
} |
||||||
|
} |
||||||
|
MyAlertDialog.build(AdSettingActivity.this) |
||||||
|
.setTitle(getString(R.string.splash_ad_times)) |
||||||
|
.setSingleChoiceItems(adTimes, checked, (dialog, which) -> { |
||||||
|
curAdTimes = ints[which]; |
||||||
|
spu.putInt("curAdTimes", curAdTimes); |
||||||
|
binding.tvSplashCurAdTimes.setText(getString(R.string.splash_cur_ad_times, adTimes[which], curAdCount + "次")); |
||||||
|
dialog.dismiss(); |
||||||
|
}).setNegativeButton("取消", null).show(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Throwable e) { |
||||||
|
loadingDialog.dismiss(); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
binding.rlBookDetailAd.setOnClickListener(v -> { |
||||||
|
bookDetailAd = !bookDetailAd; |
||||||
|
spu.putBoolean("bookDetailAd", bookDetailAd); |
||||||
|
binding.scBookDetailAd.setChecked(bookDetailAd); |
||||||
|
}); |
||||||
|
binding.rlDeleteAdFile.setOnClickListener(v -> { |
||||||
|
FileUtils.deleteFile(FileUtils.getFilePath()); |
||||||
|
ToastUtils.showSuccess("广告文件删除成功"); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void processLogic() { |
||||||
|
super.processLogic(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDestroy() { |
||||||
|
if (loadingDialog != null) { |
||||||
|
loadingDialog.dismiss(); |
||||||
|
} |
||||||
|
super.onDestroy(); |
||||||
|
} |
||||||
|
|
||||||
|
private String getAdTimesStr(int adTimes) { |
||||||
|
if (adTimes == -1) { |
||||||
|
return "一直显示"; |
||||||
|
} else if (adTimes == 0) { |
||||||
|
return "不显示"; |
||||||
|
} else { |
||||||
|
return adTimes + "次"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,162 @@ |
|||||||
|
package xyz.fycz.myreader.ui.activity; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Intent; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.view.Menu; |
||||||
|
import android.view.MenuItem; |
||||||
|
|
||||||
|
import androidx.appcompat.widget.Toolbar; |
||||||
|
|
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.application.App; |
||||||
|
import xyz.fycz.myreader.base.BaseActivity; |
||||||
|
import xyz.fycz.myreader.common.APPCONST; |
||||||
|
import xyz.fycz.myreader.databinding.ActivityBookInfoEditBinding; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.service.BookService; |
||||||
|
import xyz.fycz.myreader.model.source.BookSourceManager; |
||||||
|
import xyz.fycz.myreader.ui.dialog.DialogCreator; |
||||||
|
import xyz.fycz.myreader.util.ToastUtils; |
||||||
|
import xyz.fycz.myreader.util.UriFileUtil; |
||||||
|
import xyz.fycz.myreader.util.utils.NetworkUtils; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2021/4/24 15:05 |
||||||
|
*/ |
||||||
|
public class BookInfoEditActivity extends BaseActivity { |
||||||
|
private ActivityBookInfoEditBinding binding; |
||||||
|
private Book mBook; |
||||||
|
private String imgUrl; |
||||||
|
private String bookName; |
||||||
|
private String author; |
||||||
|
private String desc; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void bindView() { |
||||||
|
binding = ActivityBookInfoEditBinding.inflate(getLayoutInflater()); |
||||||
|
setContentView(binding.getRoot()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void setUpToolbar(Toolbar toolbar) { |
||||||
|
super.setUpToolbar(toolbar); |
||||||
|
setStatusBarColor(R.color.colorPrimary, true); |
||||||
|
getSupportActionBar().setTitle("书籍信息编辑"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean initSwipeBackEnable() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initData(Bundle savedInstanceState) { |
||||||
|
super.initData(savedInstanceState); |
||||||
|
mBook = (Book) getIntent().getSerializableExtra(APPCONST.BOOK); |
||||||
|
if (mBook == null) { |
||||||
|
ToastUtils.showError("未读取到书籍信息"); |
||||||
|
finish(); |
||||||
|
} |
||||||
|
imgUrl = NetworkUtils.getAbsoluteURL(ReadCrawlerUtil.getReadCrawler(mBook.getSource()).getNameSpace(), mBook.getImgUrl()); |
||||||
|
bookName = mBook.getName(); |
||||||
|
author = mBook.getAuthor(); |
||||||
|
desc = mBook.getDesc(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initWidget() { |
||||||
|
super.initWidget(); |
||||||
|
initImg(imgUrl); |
||||||
|
binding.tieBookName.setText(bookName); |
||||||
|
binding.tieBookAuthor.setText(author); |
||||||
|
binding.tieCoverUrl.setText(imgUrl); |
||||||
|
binding.tieBookDesc.setText(desc); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initClick() { |
||||||
|
binding.btSelectLocalPic.setOnClickListener(v -> { |
||||||
|
ToastUtils.showInfo("请选择一张图片"); |
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); |
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE); |
||||||
|
intent.setType("image/*"); |
||||||
|
startActivityForResult(intent, APPCONST.REQUEST_SELECT_COVER); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// 添加菜单
|
||||||
|
@Override |
||||||
|
public boolean onCreateOptionsMenu(Menu menu) { |
||||||
|
getMenuInflater().inflate(R.menu.menu_book_info_edit, menu); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
//菜单
|
||||||
|
@Override |
||||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||||
|
if (item.getItemId() == R.id.action_save) { |
||||||
|
saveInfo(); |
||||||
|
} |
||||||
|
return super.onOptionsItemSelected(item); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
super.onActivityResult(requestCode, resultCode, data); |
||||||
|
if (requestCode == APPCONST.REQUEST_SELECT_COVER) { |
||||||
|
if (resultCode == RESULT_OK && null != data) { |
||||||
|
String imgUrl = UriFileUtil.getPath(this, data.getData()); |
||||||
|
binding.tieCoverUrl.setText(imgUrl); |
||||||
|
initImg(imgUrl); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void finish() { |
||||||
|
if (hasChange()) { |
||||||
|
DialogCreator.createThreeButtonDialog(this, "退出" |
||||||
|
, "当前书籍信息已更改,是否保存?", true, |
||||||
|
"直接退出", "取消", "保存并退出", |
||||||
|
(dialog, which) -> super.finish(), null, |
||||||
|
(dialog, which) -> { |
||||||
|
saveInfo(); |
||||||
|
super.finish(); |
||||||
|
} |
||||||
|
); |
||||||
|
} else { |
||||||
|
super.finish(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initImg(String imgUrl) { |
||||||
|
if (!App.isDestroy(this)) { |
||||||
|
binding.ivCover.load(imgUrl, mBook.getName(), mBook.getAuthor()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void saveInfo() { |
||||||
|
bookName = binding.tieBookName.getText().toString(); |
||||||
|
author = binding.tieBookAuthor.getText().toString(); |
||||||
|
imgUrl = binding.tieCoverUrl.getText().toString(); |
||||||
|
desc = binding.tieBookDesc.getText().toString(); |
||||||
|
mBook.setName(bookName); |
||||||
|
mBook.setAuthor(author); |
||||||
|
mBook.setImgUrl(imgUrl); |
||||||
|
mBook.setDesc(desc); |
||||||
|
BookService.getInstance().updateEntity(mBook); |
||||||
|
ToastUtils.showSuccess("书籍信息保存成功"); |
||||||
|
setResult(Activity.RESULT_OK); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean hasChange() { |
||||||
|
return !bookName.equals(binding.tieBookName.getText().toString()) || |
||||||
|
!author.equals(binding.tieBookAuthor.getText().toString()) || |
||||||
|
!imgUrl.equals(binding.tieCoverUrl.getText().toString()) || |
||||||
|
!desc.equals(binding.tieBookDesc.getText().toString()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
package xyz.fycz.myreader.ui.activity; |
||||||
|
|
||||||
|
import android.content.Intent; |
||||||
|
import android.net.Uri; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import androidx.appcompat.widget.Toolbar; |
||||||
|
|
||||||
|
import com.sigmob.sdk.common.models.sigdsp.pb.Ad; |
||||||
|
import com.weaction.ddsdk.ad.DdSdkFlowAd; |
||||||
|
import com.weaction.ddsdk.ad.DdSdkRewardAd; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import io.reactivex.annotations.NonNull; |
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.base.BaseActivity; |
||||||
|
import xyz.fycz.myreader.base.observer.MySingleObserver; |
||||||
|
import xyz.fycz.myreader.common.URLCONST; |
||||||
|
import xyz.fycz.myreader.databinding.ActivityDonateBinding; |
||||||
|
import xyz.fycz.myreader.util.SharedPreUtils; |
||||||
|
import xyz.fycz.myreader.util.ToastUtils; |
||||||
|
import xyz.fycz.myreader.util.utils.AdUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2021/4/23 21:23 |
||||||
|
*/ |
||||||
|
public class DonateActivity extends BaseActivity { |
||||||
|
|
||||||
|
private ActivityDonateBinding binding; |
||||||
|
private static final String TAG = DonateActivity.class.getSimpleName(); |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void bindView() { |
||||||
|
binding = ActivityDonateBinding.inflate(getLayoutInflater()); |
||||||
|
setContentView(binding.getRoot()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void setUpToolbar(Toolbar toolbar) { |
||||||
|
super.setUpToolbar(toolbar); |
||||||
|
setStatusBarColor(R.color.colorPrimary, true); |
||||||
|
getSupportActionBar().setTitle(getString(R.string.support_author)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initWidget() { |
||||||
|
AdUtils.checkHasAd().subscribe(new MySingleObserver<Boolean>() { |
||||||
|
@Override |
||||||
|
public void onSuccess(@NonNull Boolean aBoolean) { |
||||||
|
if (aBoolean) { |
||||||
|
AdUtils.initAd(); |
||||||
|
initAd(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void initAd() { |
||||||
|
binding.llAdSupport.setVisibility(View.VISIBLE); |
||||||
|
new DdSdkFlowAd().getFlowViews(DonateActivity.this, 6, 1, new DdSdkFlowAd.FlowCallback() { |
||||||
|
// 信息流广告拉取完毕后返回的 views
|
||||||
|
@Override |
||||||
|
public void getFlowViews(List<View> views) { |
||||||
|
Log.i(TAG, "信息流广告拉取完毕后返回了" + views.size() + "个view"); |
||||||
|
binding.llAdSupport.addView(views.get(0), 2); |
||||||
|
} |
||||||
|
|
||||||
|
// 信息流广告展示后调用
|
||||||
|
@Override |
||||||
|
public void show() { |
||||||
|
AdUtils.adRecord("flow","adShow"); |
||||||
|
Log.i(TAG, "信息流广告展示成功"); |
||||||
|
} |
||||||
|
|
||||||
|
// 广告拉取失败调用
|
||||||
|
@Override |
||||||
|
public void error(String msg) { |
||||||
|
Log.i(TAG, "广告拉取失败\n" + msg); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void initClick() { |
||||||
|
binding.llWxZsm.setOnClickListener(v -> goDonate(URLCONST.WX_ZSM)); |
||||||
|
binding.llZfbSkm.setOnClickListener(v -> goDonate(URLCONST.ZFB_SKM)); |
||||||
|
binding.llQqSkm.setOnClickListener(v -> goDonate(URLCONST.QQ_SKM)); |
||||||
|
binding.llRewardedVideo.setOnClickListener(v -> { |
||||||
|
DdSdkRewardAd.show(this, new DdSdkRewardAd.DdSdkRewardCallback() { |
||||||
|
@Override |
||||||
|
public void show() { |
||||||
|
Log.i(TAG, "激励视频展示成功"); |
||||||
|
AdUtils.adRecord("rewardVideo","adShow"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void click() { |
||||||
|
Log.i(TAG, "激励视频被点击"); |
||||||
|
AdUtils.adRecord("rewardVideo","adClick"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void error(String msg) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void skip() { |
||||||
|
Log.i(TAG, "激励视频被跳过"); |
||||||
|
AdUtils.adRecord("rewardVideo","adSkip"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void finishCountdown() { |
||||||
|
Log.i(TAG, "激励视频计时完成"); |
||||||
|
AdUtils.adRecord("rewardVideo","adFinishCount"); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void goDonate(String address) { |
||||||
|
try { |
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW); |
||||||
|
intent.setData(Uri.parse(address)); |
||||||
|
startActivity(intent); |
||||||
|
} catch (Exception e) { |
||||||
|
ToastUtils.showError(e.getLocalizedMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
package xyz.fycz.myreader.ui.dialog; |
||||||
|
|
||||||
|
import android.app.Dialog; |
||||||
|
import android.content.Context; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.KeyEvent; |
||||||
|
import android.view.WindowManager; |
||||||
|
import android.view.animation.LinearInterpolator; |
||||||
|
import android.view.animation.RotateAnimation; |
||||||
|
import android.widget.ImageView; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
|
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.common.APPCONST; |
||||||
|
import xyz.fycz.myreader.databinding.DialogLoadingBinding; |
||||||
|
import xyz.fycz.myreader.util.ToastUtils; |
||||||
|
|
||||||
|
import static org.jetbrains.anko.AnkoContextKt.setContentView; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2021/4/22 17:19 |
||||||
|
*/ |
||||||
|
public class LoadingDialog extends Dialog { |
||||||
|
|
||||||
|
private static final String TAG = "LoadingDialog"; |
||||||
|
|
||||||
|
private DialogLoadingBinding binding; |
||||||
|
|
||||||
|
private String mMessage; |
||||||
|
//private int mImageId;
|
||||||
|
private boolean mCancelable; |
||||||
|
private RotateAnimation mRotateAnimation; |
||||||
|
private long cancelTime; |
||||||
|
private OnCancelListener mOnCancelListener; |
||||||
|
|
||||||
|
public LoadingDialog(@NonNull Context context, String message, OnCancelListener onCancelListener) { |
||||||
|
this(context, R.style.LoadingDialog, message, false, onCancelListener); |
||||||
|
} |
||||||
|
|
||||||
|
public LoadingDialog(@NonNull Context context, int themeResId, String message, boolean cancelable, OnCancelListener onCancelListener) { |
||||||
|
super(context, themeResId); |
||||||
|
mMessage = message; |
||||||
|
//mImageId = imageId;
|
||||||
|
mCancelable = cancelable; |
||||||
|
mOnCancelListener = onCancelListener; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
binding = DialogLoadingBinding.inflate(getLayoutInflater()); |
||||||
|
setContentView(binding.getRoot()); |
||||||
|
initView(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initView() { |
||||||
|
// 设置窗口大小
|
||||||
|
WindowManager windowManager = getWindow().getWindowManager(); |
||||||
|
int screenWidth = windowManager.getDefaultDisplay().getWidth(); |
||||||
|
WindowManager.LayoutParams attributes = getWindow().getAttributes(); |
||||||
|
attributes.alpha = 0.3f; |
||||||
|
attributes.width = screenWidth / 3; |
||||||
|
attributes.height = attributes.width; |
||||||
|
getWindow().setAttributes(attributes); |
||||||
|
setCancelable(mCancelable); |
||||||
|
|
||||||
|
binding.tvLoading.setText(mMessage); |
||||||
|
//iv_loading.setImageResource(mImageId);
|
||||||
|
binding.ivLoading.measure(0, 0); |
||||||
|
mRotateAnimation = new RotateAnimation(0, 360, binding.ivLoading.getMeasuredWidth() / 2f, |
||||||
|
binding.ivLoading.getMeasuredHeight() / 2f); |
||||||
|
mRotateAnimation.setInterpolator(new LinearInterpolator()); |
||||||
|
mRotateAnimation.setDuration(1000); |
||||||
|
mRotateAnimation.setRepeatCount(-1); |
||||||
|
} |
||||||
|
|
||||||
|
public void setmMessage(String mMessage) { |
||||||
|
this.mMessage = mMessage; |
||||||
|
binding.tvLoading.setText(mMessage); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void show() { |
||||||
|
super.show(); |
||||||
|
binding.ivLoading.startAnimation(mRotateAnimation); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dismiss() { |
||||||
|
if (isShowing()) { |
||||||
|
mRotateAnimation.cancel(); |
||||||
|
super.dismiss(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onBackPressed() { |
||||||
|
if (mCancelable) { |
||||||
|
super.onBackPressed(); |
||||||
|
dismiss(); |
||||||
|
mOnCancelListener.cancel(); |
||||||
|
} else { |
||||||
|
if (System.currentTimeMillis() - cancelTime > APPCONST.exitConfirmTime) { |
||||||
|
ToastUtils.showInfo("再按一次取消"); |
||||||
|
cancelTime = System.currentTimeMillis(); |
||||||
|
} else { |
||||||
|
super.onBackPressed(); |
||||||
|
dismiss(); |
||||||
|
mOnCancelListener.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public interface OnCancelListener { |
||||||
|
void cancel(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,396 @@ |
|||||||
|
package xyz.fycz.myreader.ui.dialog; |
||||||
|
|
||||||
|
import android.Manifest; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.content.pm.PackageManager; |
||||||
|
import android.net.Uri; |
||||||
|
import android.os.Build; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.os.Environment; |
||||||
|
import android.os.Handler; |
||||||
|
import android.text.Html; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.text.method.ScrollingMovementMethod; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.LayoutInflater; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
|
||||||
|
import androidx.annotation.NonNull; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
import androidx.core.app.ActivityCompat; |
||||||
|
import androidx.core.content.FileProvider; |
||||||
|
import androidx.fragment.app.Fragment; |
||||||
|
import androidx.fragment.app.FragmentActivity; |
||||||
|
import androidx.fragment.app.FragmentTransaction; |
||||||
|
|
||||||
|
import com.liulishuo.filedownloader.BaseDownloadTask; |
||||||
|
import com.liulishuo.filedownloader.FileDownloadLargeFileListener; |
||||||
|
import com.liulishuo.filedownloader.FileDownloader; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.UnsupportedEncodingException; |
||||||
|
import java.net.HttpURLConnection; |
||||||
|
import java.net.URL; |
||||||
|
import java.net.URLDecoder; |
||||||
|
|
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
import xyz.fycz.myreader.application.App; |
||||||
|
import xyz.fycz.myreader.databinding.FragmentUpdateBinding; |
||||||
|
import xyz.fycz.myreader.util.ToastUtils; |
||||||
|
import xyz.fycz.myreader.webapi.CommonApi; |
||||||
|
import xyz.fycz.myreader.webapi.callback.ResultCallback; |
||||||
|
|
||||||
|
|
||||||
|
public class UpdateDialog extends Fragment { |
||||||
|
|
||||||
|
private String version, content, appUrl, path; |
||||||
|
private boolean isContentHtml = false; |
||||||
|
private boolean cancelable; |
||||||
|
// private OnConfirmListener listener;
|
||||||
|
private FragmentActivity mActivity; |
||||||
|
private final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0x100; |
||||||
|
private BaseDownloadTask baseDownloadTask; |
||||||
|
private boolean debug = false; |
||||||
|
private FragmentUpdateBinding binding; |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void onAttach(Context context) { |
||||||
|
super.onAttach(context); |
||||||
|
mActivity = (FragmentActivity) context; |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
@Override |
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |
||||||
|
binding = FragmentUpdateBinding.inflate(inflater, container, false); |
||||||
|
return binding.getRoot(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { |
||||||
|
super.onViewCreated(view, savedInstanceState); |
||||||
|
FileDownloader.setup(mActivity); |
||||||
|
initViews(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDestroyView() { |
||||||
|
super.onDestroyView(); |
||||||
|
if (baseDownloadTask != null && (!baseDownloadTask.isRunning() || baseDownloadTask.pause())) |
||||||
|
baseDownloadTask = null; |
||||||
|
} |
||||||
|
|
||||||
|
private void initViews() { |
||||||
|
binding.tvContent.setMovementMethod(ScrollingMovementMethod.getInstance()); |
||||||
|
binding.ivClose.setVisibility(cancelable ? View.VISIBLE : View.GONE); |
||||||
|
binding.btUpdate.setOnClickListener(v -> { |
||||||
|
// if (listener != null) {
|
||||||
|
// listener.onConfirm(v);
|
||||||
|
// } else {
|
||||||
|
if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { |
||||||
|
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_WRITE_EXTERNAL_STORAGE); |
||||||
|
} else { |
||||||
|
downloadApk(appUrl); |
||||||
|
} |
||||||
|
// }
|
||||||
|
}); |
||||||
|
binding.btBrowser.setOnClickListener(v -> { |
||||||
|
Intent intent = new Intent(); |
||||||
|
intent.setAction(Intent.ACTION_VIEW); |
||||||
|
intent.setData(Uri.parse(appUrl)); |
||||||
|
mActivity.startActivity(intent); |
||||||
|
}); |
||||||
|
binding.rlProgress.setOnClickListener(v -> { |
||||||
|
if (binding.barPercentView.getProgress() == 1) { |
||||||
|
install(new File(path), mActivity); |
||||||
|
} |
||||||
|
}); |
||||||
|
binding.ivClose.setOnClickListener(v -> { |
||||||
|
if (baseDownloadTask == null) { |
||||||
|
dismissUpdateDialog(); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!baseDownloadTask.isRunning() || baseDownloadTask.pause()) { |
||||||
|
dismissUpdateDialog(); |
||||||
|
} |
||||||
|
}); |
||||||
|
binding.tvVersion.setText(TextUtils.isEmpty(version) ? "" : version); |
||||||
|
if (isContentHtml) { |
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
||||||
|
binding.tvContent.setText(Html.fromHtml(TextUtils.isEmpty(content) ? "" : content, Html.FROM_HTML_MODE_LEGACY)); |
||||||
|
} else { |
||||||
|
binding.tvContent.setText(Html.fromHtml(TextUtils.isEmpty(content) ? "" : content)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
binding.tvContent.setText(content); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class Builder { |
||||||
|
|
||||||
|
private UpdateDialog updateDialog; |
||||||
|
|
||||||
|
public Builder() { |
||||||
|
this.updateDialog = new UpdateDialog(); |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setVersion(String version) { |
||||||
|
updateDialog.version = version; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setContent(String content) { |
||||||
|
updateDialog.content = content; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setContentHtml(boolean isContentHtml) { |
||||||
|
updateDialog.isContentHtml = isContentHtml; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setCancelable(boolean cancelable) { |
||||||
|
updateDialog.cancelable = cancelable; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setDebug(boolean debug) { |
||||||
|
updateDialog.debug = debug; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setDownloadUrl(String url) { |
||||||
|
updateDialog.appUrl = url; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
// public Builder setOnConfirmListener(OnConfirmListener listener) {
|
||||||
|
// updateDialog.listener = listener;
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public UpdateDialog build() { |
||||||
|
return updateDialog; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// public void setPercent(int progressPercent) {
|
||||||
|
// barPercentView.setPercentage(progressPercent);
|
||||||
|
// progressTv.setText(progressPercent + "%");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public interface OnConfirmListener {
|
||||||
|
// void onConfirm(View view);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void showUpdateDialog(FragmentActivity activity) { |
||||||
|
if (this.isAdded() && this.isVisible()) return; |
||||||
|
FragmentTransaction fragmentTransaction = activity.getSupportFragmentManager().beginTransaction(); |
||||||
|
fragmentTransaction.add(android.R.id.content, this); |
||||||
|
fragmentTransaction.setCustomAnimations(R.anim.push_bottom_in, R.anim.push_bottom_out); |
||||||
|
fragmentTransaction.show(this); |
||||||
|
if (activity.isFinishing() || activity.isDestroyed()) { |
||||||
|
fragmentTransaction.commitAllowingStateLoss(); |
||||||
|
} else { |
||||||
|
fragmentTransaction.commit(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void dismissUpdateDialog() { |
||||||
|
FragmentTransaction fragmentTransaction = mActivity.getSupportFragmentManager().beginTransaction(); |
||||||
|
if (this.isAdded() && this.isVisible()) { |
||||||
|
fragmentTransaction.setCustomAnimations(R.anim.push_bottom_in, R.anim.push_bottom_out); |
||||||
|
fragmentTransaction.hide(this); |
||||||
|
fragmentTransaction.commit(); |
||||||
|
new Handler().postDelayed(this::destroyUpdateDialog, 500); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void destroyUpdateDialog() { |
||||||
|
FragmentTransaction fragmentTransaction = mActivity.getSupportFragmentManager().beginTransaction(); |
||||||
|
fragmentTransaction.hide(this); |
||||||
|
fragmentTransaction.remove(this); |
||||||
|
fragmentTransaction.commit(); |
||||||
|
} |
||||||
|
|
||||||
|
private void downloadApk(String apkUrl) { |
||||||
|
if (TextUtils.isEmpty(apkUrl)) throw new NullPointerException("url can not be empty"); |
||||||
|
binding.rlProgress.setVisibility(View.VISIBLE); |
||||||
|
binding.btUpdate.setVisibility(View.GONE); |
||||||
|
binding.btBrowser.setVisibility(View.GONE); |
||||||
|
if (apkUrl.endsWith(".apk")) { |
||||||
|
downloadApkNormal(apkUrl); |
||||||
|
} else if (apkUrl.contains("fycz.lanzou")) { |
||||||
|
downloadWithLanzous(apkUrl); |
||||||
|
} else { |
||||||
|
downloadApkWithNoFileName(apkUrl); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void downloadApkNormal(String apkUrl) { |
||||||
|
String decodeUrl; |
||||||
|
try { |
||||||
|
decodeUrl = URLDecoder.decode(apkUrl, "UTF-8"); |
||||||
|
} catch (UnsupportedEncodingException e) { |
||||||
|
decodeUrl = apkUrl; |
||||||
|
} |
||||||
|
if (debug) Log.e("downloadApk", "originUrl------->" + apkUrl); |
||||||
|
if (debug) Log.e("downloadApk", "decodeUrl------->" + decodeUrl); |
||||||
|
// path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/" + decodeUrl.substring(decodeUrl.lastIndexOf("/") + 1);
|
||||||
|
path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/风月读书" + version + ".apk"; |
||||||
|
File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); |
||||||
|
if (!dir.exists() && dir.mkdir()) { |
||||||
|
startDownloadingApk(decodeUrl); |
||||||
|
} else { |
||||||
|
startDownloadingApk(decodeUrl); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void startDownloadingApk(String decodeUrl) { |
||||||
|
baseDownloadTask = FileDownloader.getImpl() |
||||||
|
.create(decodeUrl) |
||||||
|
.setPath(path, new File(path).isDirectory()) |
||||||
|
.setCallbackProgressMinInterval(100) |
||||||
|
.setListener(new FileDownloadLargeFileListener() { |
||||||
|
@Override |
||||||
|
protected void pending(BaseDownloadTask task, long soFarBytes, long totalBytes) { |
||||||
|
if (debug) Log.e("downloadApk", "pending-------"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void progress(BaseDownloadTask task, long soFarBytes, long totalBytes) { |
||||||
|
float percent = 1f * soFarBytes / totalBytes * 100; |
||||||
|
if (debug) Log.e("downloadApk", "progress-------" + percent); |
||||||
|
if (percent >= 3) { |
||||||
|
binding.barPercentView.setPercentage(percent); |
||||||
|
binding.tvProgress.setText((int) percent + "%"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void paused(BaseDownloadTask task, long soFarBytes, long totalBytes) { |
||||||
|
if (debug) Log.e("downloadApk", "paused-------"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void completed(BaseDownloadTask task) { |
||||||
|
if (debug) Log.e("downloadApk", "completed-------"); |
||||||
|
binding.barPercentView.setPercentage(100); |
||||||
|
binding.tvProgress.setText("100%"); |
||||||
|
install(new File(path), mActivity); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void error(BaseDownloadTask task, Throwable e) { |
||||||
|
if (debug) Log.e("downloadApk", "error-------" + e.toString()); |
||||||
|
UpdateDialog.this.error(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void warn(BaseDownloadTask task) { |
||||||
|
if (debug) Log.e("downloadApk", "warn-------"); |
||||||
|
} |
||||||
|
}); |
||||||
|
baseDownloadTask.setAutoRetryTimes(3); |
||||||
|
baseDownloadTask.start(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { |
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||||
|
if (requestCode == PERMISSION_WRITE_EXTERNAL_STORAGE) { |
||||||
|
if (grantResults.length > 0 |
||||||
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
||||||
|
// 权限被用户同意,可以去放肆了。
|
||||||
|
if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { |
||||||
|
// TODO: Consider calling
|
||||||
|
// ActivityCompat#requestPermissions
|
||||||
|
// here to request the missing permissions, and then overriding
|
||||||
|
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
||||||
|
// int[] grantResults)
|
||||||
|
// to handle the case where the user grants the permission. See the documentation
|
||||||
|
// for ActivityCompat#requestPermissions for more details.
|
||||||
|
return; |
||||||
|
} else { |
||||||
|
downloadApk(appUrl); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// 权限被用户拒绝了,洗洗睡吧。
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 安装 |
||||||
|
*/ |
||||||
|
private void install(File filePath, Context context) { |
||||||
|
if (filePath.exists()) { |
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW); |
||||||
|
//判断是否是Android N 以及更高的版本
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
||||||
|
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
||||||
|
Log.d("UpdateDialog", "install: " + filePath); |
||||||
|
Uri contentUri = FileProvider.getUriForFile(context, mActivity.getPackageName() + ".fileprovider", filePath); |
||||||
|
intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); |
||||||
|
} else { |
||||||
|
intent.setDataAndType(Uri.fromFile(filePath), "application/vnd.android.package-archive"); |
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
||||||
|
} |
||||||
|
context.startActivity(intent); |
||||||
|
} else { |
||||||
|
downloadApk(appUrl); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void downloadApkWithNoFileName(String appUrl) { |
||||||
|
App.getApplication().newThread(() -> { |
||||||
|
try { |
||||||
|
URL url = new URL(appUrl); |
||||||
|
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); |
||||||
|
urlConnection.setRequestMethod("GET"); |
||||||
|
urlConnection.setConnectTimeout(5000); |
||||||
|
int responseCode = urlConnection.getResponseCode(); |
||||||
|
if (responseCode == 200) { |
||||||
|
URL url1 = urlConnection.getURL(); |
||||||
|
downloadApkNormal(url1.toString()); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void downloadWithLanzous(String apkUrl) { |
||||||
|
binding.tvProgress.setText("正在获取下载链接..."); |
||||||
|
binding.barPercentView.setPercentage(0); |
||||||
|
CommonApi.getUrl(apkUrl, new ResultCallback() { |
||||||
|
@Override |
||||||
|
public void onFinish(Object o, int code) { |
||||||
|
String downloadUrl = (String) o; |
||||||
|
if (downloadUrl == null) { |
||||||
|
App.runOnUiThread(() -> error()); |
||||||
|
return; |
||||||
|
} |
||||||
|
App.runOnUiThread(() -> downloadApkNormal(downloadUrl)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Exception e) { |
||||||
|
App.runOnUiThread(() -> error()); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void error() { |
||||||
|
ToastUtils.showError("下载链接获取失败,请前往浏览器下载!"); |
||||||
|
binding.btBrowser.performClick(); |
||||||
|
binding.rlProgress.setVisibility(View.GONE); |
||||||
|
binding.btUpdate.setVisibility(View.VISIBLE); |
||||||
|
binding.btBrowser.setVisibility(View.VISIBLE); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,133 @@ |
|||||||
|
package xyz.fycz.myreader.util.utils; |
||||||
|
|
||||||
|
import android.util.Log; |
||||||
|
|
||||||
|
import com.weaction.ddsdk.base.DdSdkHelper; |
||||||
|
|
||||||
|
import org.json.JSONArray; |
||||||
|
import org.json.JSONException; |
||||||
|
import org.json.JSONObject; |
||||||
|
|
||||||
|
import io.reactivex.Single; |
||||||
|
import io.reactivex.SingleEmitter; |
||||||
|
import io.reactivex.SingleOnSubscribe; |
||||||
|
import io.reactivex.annotations.NonNull; |
||||||
|
import okhttp3.MediaType; |
||||||
|
import okhttp3.RequestBody; |
||||||
|
import xyz.fycz.myreader.application.App; |
||||||
|
import xyz.fycz.myreader.base.observer.MySingleObserver; |
||||||
|
import xyz.fycz.myreader.common.URLCONST; |
||||||
|
import xyz.fycz.myreader.model.backup.UserService; |
||||||
|
import xyz.fycz.myreader.util.DateHelper; |
||||||
|
import xyz.fycz.myreader.util.SharedPreUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author fengyue |
||||||
|
* @date 2021/4/22 19:00 |
||||||
|
*/ |
||||||
|
public class AdUtils { |
||||||
|
public static final String TAG = AdUtils.class.getSimpleName(); |
||||||
|
private static boolean hasInitAd = false; |
||||||
|
|
||||||
|
public static Single<Boolean> checkHasAd() { |
||||||
|
return Single.create((SingleOnSubscribe<Boolean>) emitter -> { |
||||||
|
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); |
||||||
|
String body = "type=hasAd" + UserService.makeSignalParam(); |
||||||
|
RequestBody requestBody = RequestBody.create(mediaType, body); |
||||||
|
String jsonStr = OkHttpUtils.getHtml(URLCONST.AD_URL, requestBody, "UTF-8"); |
||||||
|
boolean hasAd = false; |
||||||
|
try { |
||||||
|
JSONObject jsonObject = new JSONObject(jsonStr); |
||||||
|
int code = jsonObject.getInt("code"); |
||||||
|
if (code > 200) { |
||||||
|
Log.e(TAG, "checkHasAd-->errorCode:" + code); |
||||||
|
if (code == 213) { |
||||||
|
hasAd = true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
hasAd = jsonObject.getBoolean("result"); |
||||||
|
} |
||||||
|
Log.i(TAG, "hasAd:" + hasAd); |
||||||
|
} catch (JSONException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
emitter.onSuccess(hasAd); |
||||||
|
}).compose(RxUtils::toSimpleSingle); |
||||||
|
} |
||||||
|
|
||||||
|
public static void adRecord(String type, String name) { |
||||||
|
Single.create((SingleOnSubscribe<Boolean>) emitter -> { |
||||||
|
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); |
||||||
|
String body = "adType=" + type + "&type=" + name + UserService.makeSignalParam(); |
||||||
|
RequestBody requestBody = RequestBody.create(mediaType, body); |
||||||
|
OkHttpUtils.getHtml(URLCONST.AD_URL, requestBody, "UTF-8"); |
||||||
|
emitter.onSuccess(true); |
||||||
|
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<Boolean>() { |
||||||
|
@Override |
||||||
|
public void onSuccess(@NonNull Boolean aBoolean) { |
||||||
|
Log.i(TAG, name + "上报成功"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(Throwable e) { |
||||||
|
Log.e(TAG, name + "上报失败\n" + e.getLocalizedMessage()); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public static Single<int[]> adTimes() { |
||||||
|
return Single.create((SingleOnSubscribe<int[]>) emitter -> { |
||||||
|
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); |
||||||
|
String body = "type=adTimes" + UserService.makeSignalParam(); |
||||||
|
RequestBody requestBody = RequestBody.create(mediaType, body); |
||||||
|
String jsonStr = OkHttpUtils.getHtml(URLCONST.AD_URL, requestBody, "UTF-8"); |
||||||
|
JSONObject jsonObject = new JSONObject(jsonStr); |
||||||
|
int[] adTimes = new int[]{-1, 3, 5}; |
||||||
|
try { |
||||||
|
int code = jsonObject.getInt("code"); |
||||||
|
JSONArray adTimesArr = jsonObject.getJSONArray("result"); |
||||||
|
Log.i(TAG, "adTimesArr:" + adTimesArr.toString()); |
||||||
|
if (code > 200) { |
||||||
|
Log.e(TAG, "adTimes-->errorCode:" + code); |
||||||
|
if (code == 213) { |
||||||
|
adTimes = new int[]{-1}; |
||||||
|
} |
||||||
|
} else { |
||||||
|
adTimes = new int[adTimesArr.length()]; |
||||||
|
for (int i = 0; i < adTimesArr.length(); i++) { |
||||||
|
adTimes[i] = adTimesArr.getInt(i); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (JSONException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
emitter.onSuccess(adTimes); |
||||||
|
}).compose(RxUtils::toSimpleSingle); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean checkTodayShowAd() { |
||||||
|
SharedPreUtils spu = SharedPreUtils.getInstance(); |
||||||
|
String splashAdCount = spu.getString("splashAdCount"); |
||||||
|
boolean bookDetailAd = spu.getBoolean("bookDetailAd", true); |
||||||
|
int adTimes = spu.getInt("curAdTimes", 3); |
||||||
|
String[] splashAdCounts = splashAdCount.split(":"); |
||||||
|
String today = DateHelper.getYearMonthDay1(); |
||||||
|
int todayAdCount; |
||||||
|
if (today.equals(splashAdCounts[0])) { |
||||||
|
todayAdCount = Integer.parseInt(splashAdCounts[1]); |
||||||
|
} else { |
||||||
|
todayAdCount = 0; |
||||||
|
} |
||||||
|
return adTimes < 0 || todayAdCount < adTimes || bookDetailAd; |
||||||
|
} |
||||||
|
|
||||||
|
public static void initAd(){ |
||||||
|
if (!hasInitAd){ |
||||||
|
hasInitAd = true; |
||||||
|
DdSdkHelper.init("1234", "216", "51716a16fbdf50905704b6575b1b3b60", |
||||||
|
"142364", "35ce0efe5f3cc960b116db227498e238", |
||||||
|
"8167", "85bd159309c3da1b", |
||||||
|
App.getApplication(), true); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
package xyz.fycz.myreader.webapi.crawler.read; |
||||||
|
|
||||||
|
import android.text.Html; |
||||||
|
|
||||||
|
import org.jsoup.Jsoup; |
||||||
|
import org.jsoup.nodes.Document; |
||||||
|
import org.jsoup.nodes.Element; |
||||||
|
import org.jsoup.select.Elements; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
import xyz.fycz.myreader.entity.SearchBookBean; |
||||||
|
import xyz.fycz.myreader.enums.LocalBookSource; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Book; |
||||||
|
import xyz.fycz.myreader.greendao.entity.Chapter; |
||||||
|
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; |
||||||
|
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; |
||||||
|
|
||||||
|
|
||||||
|
public class QianDianReadCrawler implements ReadCrawler { |
||||||
|
public static final String NAME_SPACE = "https://www.qidian.com"; |
||||||
|
public static final String NOVEL_SEARCH = "https://www.qidian.com/search?kw={key}"; |
||||||
|
public static final String CHARSET = "UTF-8"; |
||||||
|
public static final String SEARCH_CHARSET = "UTF-8"; |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchLink() { |
||||||
|
return NOVEL_SEARCH; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getCharset() { |
||||||
|
return CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getNameSpace() { |
||||||
|
return NAME_SPACE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Boolean isPost() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getSearchCharset() { |
||||||
|
return SEARCH_CHARSET; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节正文 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getContentFormHtml(String html) { |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divContent = doc.getElementsByClass("read-content").first(); |
||||||
|
String content = Html.fromHtml(divContent.html()).toString(); |
||||||
|
char c = 160; |
||||||
|
String spaec = "" + c; |
||||||
|
content = content.replace(spaec, " "); |
||||||
|
return content; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从html中获取章节列表 |
||||||
|
* |
||||||
|
* @param html |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ArrayList<Chapter> getChaptersFromHtml(String html) { |
||||||
|
ArrayList<Chapter> chapters = new ArrayList<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element divList = doc.getElementsByClass("attentions").get(1); |
||||||
|
Elements elementsByTag = divList.getElementsByTag("a"); |
||||||
|
for (int i = 0; i < elementsByTag.size(); i++) { |
||||||
|
Element a = elementsByTag.get(i); |
||||||
|
String title = a.text(); |
||||||
|
String url = a.attr("href"); |
||||||
|
Chapter chapter = new Chapter(); |
||||||
|
chapter.setNumber(i); |
||||||
|
chapter.setTitle(title); |
||||||
|
chapter.setUrl(url); |
||||||
|
chapters.add(chapter); |
||||||
|
} |
||||||
|
return chapters; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 从搜索html中得到书列表 |
||||||
|
* <li> |
||||||
|
* <a class="pic" href="http://www.bjcan.com/book/74544.html" target="_blank"><img class="lazy" src="http://www.bjcan.com/uploads/novel/20200907/b38cea14e4a3de1e876395e378c1e544.jpeg" alt="大主宰:灵玖"></a> |
||||||
|
* <h5 class="tit"><a href="http://www.bjcan.com/book/74544.html" target="_blank">大主宰:灵玖</a></h5> |
||||||
|
* <p class="info">作者:<span>霞露</span><span>分类:其他</span><i class="serial">连载中</i></p> |
||||||
|
* <p class="intro">简介:她,身负系统,莫名来到了大主宰的时空,成为了聚灵族的最后族人。在她还对周围的情况一片模糊的时候,她遇到了林静这个小恶魔。拜林静所赐,她还遇到了武祖,成为了武柤的小徒弟。参与了灵路,遇到了牧尘和洛璃,她默默的在心中问着自己那一直在划水的系统:“你的活来了,说吧,我该干什么?”</p> |
||||||
|
* <a class="view" href="http://www.bjcan.com/book/74544.html" target="_blank">小说详情</a> |
||||||
|
* </li> |
||||||
|
*/ |
||||||
|
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) { |
||||||
|
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>(); |
||||||
|
Document doc = Jsoup.parse(html); |
||||||
|
Element div = doc.getElementById("result-list"); |
||||||
|
Elements elementsByTag = div.getElementsByTag("li"); |
||||||
|
for (int i = 0; i < elementsByTag.size(); i++) { |
||||||
|
Element element = elementsByTag.get(i); |
||||||
|
Elements as = element.getElementsByTag("a"); |
||||||
|
Elements ps = element.getElementsByTag("p"); |
||||||
|
Book book = new Book(); |
||||||
|
book.setImgUrl(element.getElementsByTag("img").attr("src")); |
||||||
|
book.setName(as.get(1).text()); |
||||||
|
Elements spans = ps.get(0).getElementsByTag("span"); |
||||||
|
book.setAuthor(spans.get(0).text()); |
||||||
|
book.setType(spans.get(1).text().replace("分类:", "")); |
||||||
|
book.setChapterUrl(as.get(2).attr("href")); |
||||||
|
book.setDesc(ps.get(1).text()); |
||||||
|
book.setNewestChapterTitle(""); |
||||||
|
book.setSource(LocalBookSource.bijian.toString()); |
||||||
|
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); |
||||||
|
books.add(sbb, book); |
||||||
|
} |
||||||
|
return books; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,162 @@ |
|||||||
|
package xyz.fycz.myreader.widget; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.res.TypedArray; |
||||||
|
import android.graphics.Canvas; |
||||||
|
import android.graphics.LinearGradient; |
||||||
|
import android.graphics.Paint; |
||||||
|
import android.graphics.RectF; |
||||||
|
import android.graphics.Shader; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import androidx.annotation.Keep; |
||||||
|
import androidx.annotation.Nullable; |
||||||
|
|
||||||
|
import xyz.fycz.myreader.R; |
||||||
|
|
||||||
|
|
||||||
|
public class BarPercentView extends View { |
||||||
|
|
||||||
|
public static final float MAX = 100f; |
||||||
|
public static final int RADIUS = R.dimen._15dp; // 圆角矩形半径
|
||||||
|
private RectF rectFBg; |
||||||
|
private RectF rectFProgress; |
||||||
|
private Paint mPaint; |
||||||
|
private int mWidth; |
||||||
|
private float progressPercent; |
||||||
|
private int bgColor, progressColor; |
||||||
|
private int mHeight; |
||||||
|
private int radius; |
||||||
|
private int startColor, endColor; |
||||||
|
private LinearGradient gradient; |
||||||
|
private boolean isGradient; |
||||||
|
|
||||||
|
public BarPercentView(Context context) { |
||||||
|
super(context); |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
public BarPercentView(Context context, @Nullable AttributeSet attrs) { |
||||||
|
super(context, attrs); |
||||||
|
//获取自定义属性
|
||||||
|
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BarPercentView); |
||||||
|
bgColor = typedArray.getColor(R.styleable.BarPercentView_barBgColor, getResources().getColor(R.color.gray_cfcfcf)); |
||||||
|
progressColor = typedArray.getColor(R.styleable.BarPercentView_barProgressColor, getResources().getColor(R.color.orange_ffc032)); |
||||||
|
mHeight = typedArray.getDimensionPixelSize(R.styleable.BarPercentView_barHeight, context.getResources().getDimensionPixelSize(R.dimen._10dp)); |
||||||
|
isGradient = typedArray.getBoolean(R.styleable.BarPercentView_barIsGradient, false); |
||||||
|
startColor = typedArray.getColor(R.styleable.BarPercentView_barStartColor, getResources().getColor(R.color.black_3A3D4E)); |
||||||
|
endColor = typedArray.getColor(R.styleable.BarPercentView_barEndColor, getResources().getColor(R.color.black_475B80)); |
||||||
|
radius = typedArray.getDimensionPixelSize(R.styleable.BarPercentView_barRadius, RADIUS); |
||||||
|
typedArray.recycle(); |
||||||
|
init(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public BarPercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { |
||||||
|
super(context, attrs, defStyleAttr); |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||||
|
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); |
||||||
|
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); |
||||||
|
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); |
||||||
|
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); |
||||||
|
if (widthSpecMode == MeasureSpec.EXACTLY || widthSpecMode == MeasureSpec.AT_MOST) { |
||||||
|
mWidth = widthSpecSize; |
||||||
|
} else { |
||||||
|
mWidth = 0; |
||||||
|
} |
||||||
|
if (heightSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.UNSPECIFIED) { |
||||||
|
mHeight = heightSpecSize; |
||||||
|
} else if (heightSpecMode == MeasureSpec.EXACTLY) { |
||||||
|
mHeight = heightSpecSize; |
||||||
|
} else { |
||||||
|
mHeight = getContext().getResources().getDimensionPixelSize(R.dimen._10dp); |
||||||
|
} |
||||||
|
setMeasuredDimension(mWidth, mHeight); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
||||||
|
super.onSizeChanged(w, h, oldw, oldh); |
||||||
|
gradient = new LinearGradient(0, 0, getWidth(), mHeight, startColor, endColor, Shader.TileMode.CLAMP); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDraw(Canvas canvas) { |
||||||
|
super.onDraw(canvas); |
||||||
|
//1、背景
|
||||||
|
mPaint.setShader(null); |
||||||
|
mPaint.setColor(bgColor); |
||||||
|
rectFBg.right = mWidth; //宽度
|
||||||
|
rectFBg.bottom = mHeight; //高度
|
||||||
|
canvas.drawRoundRect(rectFBg, radius, radius, mPaint); |
||||||
|
//2、进度条
|
||||||
|
rectFProgress.right = mWidth * progressPercent; |
||||||
|
rectFProgress.bottom = mHeight; |
||||||
|
//3、是否绘制渐变色
|
||||||
|
if (isGradient) { |
||||||
|
mPaint.setShader(gradient);//设置线性渐变
|
||||||
|
} else { |
||||||
|
mPaint.setColor(progressColor); |
||||||
|
} |
||||||
|
// if (progressPercent > 0 && rectFProgress.right < radius){
|
||||||
|
// //进度值小于半径时,设置大于半径的最小值,防止绘制不出圆弧矩形
|
||||||
|
//// rectFProgress.right = radius;
|
||||||
|
// canvas.drawRoundRect(rectFProgress, radius/2f, radius/2f, mPaint);//进度}
|
||||||
|
// }else {
|
||||||
|
canvas.drawRoundRect(rectFProgress, radius, radius, mPaint);//进度}
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Keep |
||||||
|
public void setPercentage(float percentage) { |
||||||
|
if (percentage / MAX >= 1) { |
||||||
|
this.progressPercent = 1; |
||||||
|
} else { |
||||||
|
this.progressPercent = percentage / MAX; |
||||||
|
} |
||||||
|
invalidate(); |
||||||
|
} |
||||||
|
|
||||||
|
private void init() { |
||||||
|
rectFBg = new RectF(0, 0, 0, mHeight); |
||||||
|
rectFProgress = new RectF(0, 0, 0, mHeight); |
||||||
|
mPaint = new Paint(); |
||||||
|
//设置抗锯齿
|
||||||
|
mPaint.setAntiAlias(true); |
||||||
|
} |
||||||
|
|
||||||
|
public float getProgress() { |
||||||
|
return progressPercent; |
||||||
|
} |
||||||
|
// public void setHeight(int mHeight) {
|
||||||
|
// this.mHeight = mHeight;
|
||||||
|
// invalidate();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setBgColor(int bgColor) {
|
||||||
|
// this.bgColor = bgColor;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setProgressColor(int progressColor) {
|
||||||
|
// this.progressColor = progressColor;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setStartColor(int startColor) {
|
||||||
|
// this.startColor = startColor;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setEndColor(int endColor) {
|
||||||
|
// this.endColor = endColor;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setGradient(boolean gradient) {
|
||||||
|
// isGradient = gradient;
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<translate |
||||||
|
android:duration="300" |
||||||
|
android:fromYDelta="100%p" |
||||||
|
android:toYDelta="0"/> |
||||||
|
|
||||||
|
<alpha |
||||||
|
android:duration="300" |
||||||
|
android:fromAlpha="0.0" |
||||||
|
android:toAlpha="1.0"/> |
||||||
|
</set> |
@ -0,0 +1,11 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<translate |
||||||
|
android:duration="200" |
||||||
|
android:fromYDelta="0" |
||||||
|
android:toYDelta="50"/> |
||||||
|
<alpha |
||||||
|
android:duration="200" |
||||||
|
android:fromAlpha="1.0" |
||||||
|
android:toAlpha="0.0"/> |
||||||
|
</set> |
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
|
||||||
|
<corners android:radius="10dp" /> |
||||||
|
<solid android:color="@android:color/black"/> |
||||||
|
</shape> |
@ -0,0 +1,9 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="24dp" |
||||||
|
android:height="24dp" |
||||||
|
android:viewportWidth="1024" |
||||||
|
android:viewportHeight="1024"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M512,960C265.6,960 64,758.4 64,512S265.6,64 512,64s448,201.6 448,448 -201.6,448 -448,448zM512,128c-211.2,0 -384,172.8 -384,384s172.8,384 384,384 384,-172.8 384,-384 -172.8,-384 -384,-384zM512,800c-17.6,0 -32,-14.4 -32,-32L480,480c0,-17.6 14.4,-32 32,-32s32,14.4 32,32v288c0,17.6 -14.4,32 -32,32zM672,672L352,672c-17.6,0 -32,-14.4 -32,-32s14.4,-32 32,-32h320c17.6,0 32,14.4 32,32s-14.4,32 -32,32zM672,512L352,512c-17.6,0 -32,-14.4 -32,-32s14.4,-32 32,-32h320c17.6,0 32,14.4 32,32s-14.4,32 -32,32zM512,512c-8,0 -16,-3.2 -22.4,-9.6L331.2,344c-12.8,-12.8 -12.8,-32 0,-44.8 12.8,-12.8 32,-12.8 44.8,0l158.4,158.4c12.8,12.8 12.8,32 0,44.8 -6.4,6.4 -14.4,9.6 -22.4,9.6zM512,512c-8,0 -16,-3.2 -22.4,-9.6 -12.8,-12.8 -12.8,-32 0,-44.8L648,299.2c12.8,-12.8 32,-12.8 44.8,0 12.8,12.8 12.8,32 0,44.8L534.4,502.4C528,508.8 520,512 512,512z"/> |
||||||
|
</vector> |
@ -0,0 +1,15 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="24dp" |
||||||
|
android:height="24dp" |
||||||
|
android:viewportWidth="1024" |
||||||
|
android:viewportHeight="1024"> |
||||||
|
<!-- <path |
||||||
|
android:pathData="M583.58,642.25V345.19s199.99,-19.61 199.99,143.16 -174.54,153.91 -199.99,153.91zM298.29,542.16l68.66,-147.05 57.8,147.05z" |
||||||
|
android:fillColor="#ffa115"/>--> |
||||||
|
<path |
||||||
|
android:pathData="M809.88,923.9H220.52c-73.42,0 -133.12,-59.7 -133.12,-133.12V218.62c0,-73.42 59.7,-133.12 133.12,-133.12h589.36c73.42,0 133.12,59.7 133.12,133.12v572.16c0,73.37 -59.75,133.12 -133.12,133.12zM220.52,146.94c-39.53,0 -71.68,32.15 -71.68,71.68v572.16c0,39.53 32.15,71.68 71.68,71.68h589.36c39.53,0 71.68,-32.15 71.68,-71.68V218.62c0,-39.53 -32.15,-71.68 -71.68,-71.68H220.52z" |
||||||
|
android:fillColor="#474A54"/> |
||||||
|
<path |
||||||
|
android:pathData="M506.57,625.36L388.92,342.02a30.74,30.74 0,0 0,-28.36 -18.94h-0.2c-12.49,0.05 -23.71,7.68 -28.31,19.25L218.32,625.66c-6.3,15.77 1.33,33.64 17.05,39.94a30.68,30.68 0,0 0,39.94 -17.1l26.88,-67.02L421.89,581.48l27.96,67.33a30.73,30.73 0,0 0,40.14 16.59c15.67,-6.4 23.09,-24.37 16.59,-40.04zM326.91,520.14l34.15,-85.09 35.33,85.09L326.91,520.14zM592.33,673.02c-1.74,0 -3.53,0 -5.32,-0.05l-3.43,-0.05c-16.95,0 -30.72,-13.77 -30.72,-30.72L552.86,345.19c0,-15.82 11.98,-29.03 27.7,-30.57 4.71,-0.46 115.66,-10.55 183.4,50.84 33.43,30.31 50.33,71.63 50.33,122.88 0,53.04 -16.23,95.95 -48.28,127.64 -38.3,37.84 -96.72,57.04 -173.67,57.04zM614.3,375.4v235.57c49,-2.92 85.45,-15.87 108.54,-38.71 20.22,-19.97 30.05,-47.41 30.05,-83.92 0,-33.54 -9.83,-58.83 -30.05,-77.21 -30.72,-28.01 -78.49,-34.61 -108.54,-35.74z" |
||||||
|
android:fillColor="#474A54"/> |
||||||
|
</vector> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue