Initial commit
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
OpenManga Next
|
||||||
120
.idea/codeStyles
generated
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
|
<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>
|
||||||
|
</project>
|
||||||
9
.idea/dictionaries/admin.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="admin">
|
||||||
|
<words>
|
||||||
|
<w>koin</w>
|
||||||
|
<w>kotatsu</w>
|
||||||
|
<w>manga</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
21
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="PLATFORM" />
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="1.8" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
25
.idea/jarRepositories.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="BintrayJCenter" />
|
||||||
|
<option name="name" value="BintrayJCenter" />
|
||||||
|
<option name="url" value="https://jcenter.bintray.com/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="Google" />
|
||||||
|
<option name="name" value="Google" />
|
||||||
|
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
7
.idea/kotlinCodeInsightSettings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinCodeInsightWorkspaceSettings">
|
||||||
|
<option name="addUnambiguousImportsOnTheFly" value="true" />
|
||||||
|
<option name="optimizeImportsOnTheFly" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" 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>
|
||||||
6
.idea/render.experimental.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RenderSettings">
|
||||||
|
<option name="quality" value="0.25" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
674
LICENSE
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
77
app/build.gradle
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
|
def gitCommits = 'git rev-list --all --count'.execute([], rootDir).text.trim().toInteger()
|
||||||
|
def gitBranch = 'git branch --show-current'.execute([], rootDir).text.trim()
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 29
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "org.koitharu.kotatsu"
|
||||||
|
minSdkVersion 21
|
||||||
|
targetSdkVersion 29
|
||||||
|
versionCode gitCommits
|
||||||
|
versionName "0.1"
|
||||||
|
|
||||||
|
buildConfigField 'String', 'GIT_BRANCH', "\"${gitBranch}\""
|
||||||
|
}
|
||||||
|
archivesBaseName = "kotatsu_${gitCommits}"
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lintOptions {
|
||||||
|
disable 'MissingTranslation'
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
androidExtensions {
|
||||||
|
experimental = true
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
|
||||||
|
|
||||||
|
implementation 'androidx.core:core-ktx:1.1.0'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03'
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha01'
|
||||||
|
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0-alpha01'
|
||||||
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
|
implementation 'com.google.android.material:material:1.2.0-alpha04'
|
||||||
|
|
||||||
|
implementation 'androidx.room:room-runtime:2.2.3'
|
||||||
|
implementation 'androidx.room:room-ktx:2.2.3'
|
||||||
|
kapt 'androidx.room:room-compiler:2.2.3'
|
||||||
|
|
||||||
|
implementation 'com.github.moxy-community:moxy:2.0.2'
|
||||||
|
implementation 'com.github.moxy-community:moxy-androidx:2.0.2'
|
||||||
|
implementation 'com.github.moxy-community:moxy-material:2.0.2'
|
||||||
|
implementation 'com.github.moxy-community:moxy-ktx:2.0.2'
|
||||||
|
kapt 'com.github.moxy-community:moxy-compiler:2.0.2'
|
||||||
|
|
||||||
|
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
|
||||||
|
implementation 'com.squareup.okio:okio:2.4.3'
|
||||||
|
implementation 'org.jsoup:jsoup:1.12.1'
|
||||||
|
|
||||||
|
implementation 'org.koin:koin-android:2.0.1'
|
||||||
|
implementation 'io.coil-kt:coil:0.9.2'
|
||||||
|
implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
|
||||||
|
|
||||||
|
testImplementation 'junit:junit:4.13'
|
||||||
|
}
|
||||||
21
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
26
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.koitharu.kotatsu">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:name="org.koitharu.kotatsu.KotatsuApp"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
android:fullBackupContent="@xml/backup_descriptor">
|
||||||
|
<activity android:name="org.koitharu.kotatsu.ui.main.MainActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
53
app/src/main/java/org/koitharu/kotatsu/KotatsuApp.kt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package org.koitharu.kotatsu
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.room.Room
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
|
import org.koin.android.ext.koin.androidLogger
|
||||||
|
import org.koin.core.context.startKoin
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||||
|
import org.koitharu.kotatsu.domain.MangaLoaderContext
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class KotatsuApp : Application() {
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
initKoin()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initKoin() {
|
||||||
|
startKoin {
|
||||||
|
androidLogger()
|
||||||
|
androidContext(applicationContext)
|
||||||
|
modules(listOf(
|
||||||
|
module {
|
||||||
|
factory {
|
||||||
|
okHttp().build()
|
||||||
|
}
|
||||||
|
}, module {
|
||||||
|
single {
|
||||||
|
MangaLoaderContext(applicationContext)
|
||||||
|
}
|
||||||
|
}, module {
|
||||||
|
single {
|
||||||
|
mangaDb().build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun okHttp() = OkHttpClient.Builder()
|
||||||
|
.connectTimeout(20, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(20, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
private fun mangaDb() = Room.databaseBuilder(
|
||||||
|
applicationContext,
|
||||||
|
MangaDatabase::class.java,
|
||||||
|
"kotatsu-db"
|
||||||
|
)
|
||||||
|
}
|
||||||
12
app/src/main/java/org/koitharu/kotatsu/core/db/HistoryDao.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.core.db
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import org.koitharu.kotatsu.core.db.entity.HistoryEntity
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface HistoryDao {
|
||||||
|
|
||||||
|
@Query("SELECT * FROM history")
|
||||||
|
suspend fun getAll(): List<HistoryEntity>
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.koitharu.kotatsu.core.db
|
||||||
|
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import org.koitharu.kotatsu.core.db.entity.HistoryEntity
|
||||||
|
|
||||||
|
@Database(entities = [HistoryEntity::class], version = 1)
|
||||||
|
abstract class MangaDatabase : RoomDatabase()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.koitharu.kotatsu.core.db.entity
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "history")
|
||||||
|
data class HistoryEntity(
|
||||||
|
@PrimaryKey val id: Long
|
||||||
|
)
|
||||||
21
app/src/main/java/org/koitharu/kotatsu/core/model/Manga.kt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Manga(
|
||||||
|
val id: Long,
|
||||||
|
val title: String,
|
||||||
|
val localizedTitle: String? = null,
|
||||||
|
val url: String,
|
||||||
|
val rating: Float = -1f, //normalized value [0..1] or -1
|
||||||
|
val coverUrl: String,
|
||||||
|
val largeCoverUrl: String? = null,
|
||||||
|
val summary: String,
|
||||||
|
val description: CharSequence? = null,
|
||||||
|
val tags: Set<MangaTag> = emptySet(),
|
||||||
|
val state: MangaState? = null,
|
||||||
|
val chapters: List<MangaChapter>? = null,
|
||||||
|
val source: MangaSource
|
||||||
|
) : Parcelable
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class MangaChapter(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val number: Int,
|
||||||
|
val url: String,
|
||||||
|
val source: MangaSource
|
||||||
|
) : Parcelable
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class MangaPage(
|
||||||
|
val id: Long,
|
||||||
|
val url: String,
|
||||||
|
val preview: String? = null,
|
||||||
|
val source: MangaSource
|
||||||
|
) : Parcelable
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
import org.koitharu.kotatsu.domain.MangaRepository
|
||||||
|
import org.koitharu.kotatsu.domain.repository.ReadmangaRepository
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
enum class MangaSource(val title: String, val cls: Class<out MangaRepository>): Parcelable {
|
||||||
|
READMANGA_RU("ReadManga", ReadmangaRepository::class.java)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
enum class MangaState {
|
||||||
|
ONGOING, FINISHED
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class MangaTag(
|
||||||
|
val title: String,
|
||||||
|
val key: String
|
||||||
|
) : Parcelable
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.koitharu.kotatsu.core.model
|
||||||
|
|
||||||
|
enum class SortOrder {
|
||||||
|
ALPHABETICAL, POPULARITY, UPDATED, NEWEST
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package org.koitharu.kotatsu.domain
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import okhttp3.*
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.inject
|
||||||
|
import org.koitharu.kotatsu.utils.ext.await
|
||||||
|
|
||||||
|
class MangaLoaderContext(context: Context) : KoinComponent {
|
||||||
|
|
||||||
|
private val okHttp by inject<OkHttpClient>()
|
||||||
|
private val preferences = context.getSharedPreferences("sources", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
suspend fun get(url: String, block: (Request.Builder.() -> Unit)? = null): Response {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.get()
|
||||||
|
.url(url)
|
||||||
|
if (block != null) {
|
||||||
|
request.block()
|
||||||
|
}
|
||||||
|
return okHttp.newCall(request.build()).await()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun post(
|
||||||
|
url: String,
|
||||||
|
form: Map<String, String>,
|
||||||
|
block: (Request.Builder.() -> Unit)? = null
|
||||||
|
): Response {
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
form.forEach { (k, v) ->
|
||||||
|
body.addEncoded(k, v)
|
||||||
|
}
|
||||||
|
val request = Request.Builder()
|
||||||
|
.post(body.build())
|
||||||
|
.url(url)
|
||||||
|
if (block != null) {
|
||||||
|
request.block()
|
||||||
|
}
|
||||||
|
return okHttp.newCall(request.build()).await()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getStringOption(name: String, default: String? = null) =
|
||||||
|
preferences.getString(name, default)
|
||||||
|
|
||||||
|
fun getIntOption(name: String, default: Int) = preferences.getInt(name, default)
|
||||||
|
|
||||||
|
fun getBooleanOption(name: String, default: Boolean) = preferences.getBoolean(name, default)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.koitharu.kotatsu.domain
|
||||||
|
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.get
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
|
|
||||||
|
object MangaProviderFactory : KoinComponent {
|
||||||
|
|
||||||
|
private val loaderContext get() = get<MangaLoaderContext>()
|
||||||
|
|
||||||
|
fun create(source: MangaSource): MangaRepository {
|
||||||
|
val constructor = source.cls.getConstructor(MangaLoaderContext::class.java)
|
||||||
|
return constructor.newInstance(loaderContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.koitharu.kotatsu.domain
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.core.model.*
|
||||||
|
|
||||||
|
abstract class MangaRepository(protected val loaderContext: MangaLoaderContext) {
|
||||||
|
|
||||||
|
open val sortOrders: Set<SortOrder> get() = emptySet()
|
||||||
|
|
||||||
|
open val isSearchAvailable get() = true
|
||||||
|
|
||||||
|
abstract suspend fun getList(offset: Int, query: String? = null, sortOrder: SortOrder? = null, tags: Set<String>? = null): List<Manga>
|
||||||
|
|
||||||
|
abstract suspend fun getDetails(manga: Manga) : Manga
|
||||||
|
|
||||||
|
abstract suspend fun getPages(chapter: MangaChapter) : List<MangaPage>
|
||||||
|
|
||||||
|
open suspend fun getPageFullUrl(page: MangaPage) : String = page.url
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package org.koitharu.kotatsu.domain.exceptions
|
||||||
|
|
||||||
|
class ParseException(message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause)
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package org.koitharu.kotatsu.domain.repository
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.core.model.*
|
||||||
|
import org.koitharu.kotatsu.domain.MangaLoaderContext
|
||||||
|
import org.koitharu.kotatsu.domain.MangaRepository
|
||||||
|
import org.koitharu.kotatsu.domain.exceptions.ParseException
|
||||||
|
import org.koitharu.kotatsu.utils.ext.longHashCode
|
||||||
|
import org.koitharu.kotatsu.utils.ext.parseHtml
|
||||||
|
import org.koitharu.kotatsu.utils.ext.safe
|
||||||
|
|
||||||
|
class ReadmangaRepository(loaderContext: MangaLoaderContext) : MangaRepository(loaderContext) {
|
||||||
|
|
||||||
|
override suspend fun getList(
|
||||||
|
offset: Int,
|
||||||
|
query: String?,
|
||||||
|
sortOrder: SortOrder?,
|
||||||
|
tags: Set<String>?
|
||||||
|
): List<Manga> {
|
||||||
|
val doc = loaderContext.get("https://readmanga.me/list?sortType=updated&offset=$offset")
|
||||||
|
.parseHtml()
|
||||||
|
val root = doc.body().getElementById("mangaBox")
|
||||||
|
?.selectFirst("div.tiles.row") ?: throw ParseException("Cannot find root")
|
||||||
|
return root.select("div.tile").mapNotNull { node ->
|
||||||
|
val imgDiv = node.selectFirst("div.img") ?: return@mapNotNull null
|
||||||
|
val descDiv = node.selectFirst("div.desc") ?: return@mapNotNull null
|
||||||
|
val href = imgDiv.selectFirst("a").attr("href") ?: return@mapNotNull null
|
||||||
|
val title = descDiv.selectFirst("h3")?.selectFirst("a")?.text()
|
||||||
|
?: return@mapNotNull null
|
||||||
|
Manga(
|
||||||
|
id = href.longHashCode(),
|
||||||
|
url = href,
|
||||||
|
localizedTitle = title,
|
||||||
|
title = descDiv.selectFirst("h4")?.text() ?: title,
|
||||||
|
coverUrl = imgDiv.selectFirst("img.lazy")?.attr("data-original").orEmpty(),
|
||||||
|
summary = "",
|
||||||
|
rating = safe {
|
||||||
|
node.selectFirst("div.rating")
|
||||||
|
?.attr("title")
|
||||||
|
?.substringBefore(' ')
|
||||||
|
?.toFloatOrNull()
|
||||||
|
?.div(10f)
|
||||||
|
} ?: -1f,
|
||||||
|
tags = safe {
|
||||||
|
descDiv.selectFirst("div.tile-info")
|
||||||
|
?.select("a.element-link")
|
||||||
|
?.map {
|
||||||
|
MangaTag(
|
||||||
|
title = it.text(),
|
||||||
|
key = it.attr("href").substringAfterLast('/')
|
||||||
|
)
|
||||||
|
}?.toSet()
|
||||||
|
}.orEmpty(),
|
||||||
|
state = when {
|
||||||
|
node.selectFirst("div.tags")
|
||||||
|
?.selectFirst("span.mangaCompleted") != null -> MangaState.FINISHED
|
||||||
|
else -> null
|
||||||
|
},
|
||||||
|
source = MangaSource.READMANGA_RU
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getDetails(manga: Manga): Manga {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common
|
||||||
|
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import moxy.MvpAppCompatActivity
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
|
||||||
|
abstract class BaseActivity : MvpAppCompatActivity(), KoinComponent {
|
||||||
|
|
||||||
|
override fun setContentView(layoutResID: Int) {
|
||||||
|
super.setContentView(layoutResID)
|
||||||
|
setupToolbar()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setContentView(view: View?) {
|
||||||
|
super.setContentView(view)
|
||||||
|
setupToolbar()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupToolbar() {
|
||||||
|
(findViewById<View>(R.id.toolbar) as? Toolbar)?.let(this::setSupportActionBar)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem) = if (item.itemId == android.R.id.home) {
|
||||||
|
onBackPressed()
|
||||||
|
true
|
||||||
|
} else super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.IdRes
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import moxy.MvpAppCompatFragment
|
||||||
|
import org.koitharu.kotatsu.utils.delegates.ParcelableArgumentDelegate
|
||||||
|
import org.koitharu.kotatsu.utils.delegates.StringArgumentDelegate
|
||||||
|
|
||||||
|
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) :
|
||||||
|
MvpAppCompatFragment(contentLayoutId) {
|
||||||
|
|
||||||
|
fun stringArg(name: String) = StringArgumentDelegate(name)
|
||||||
|
|
||||||
|
fun <T : Parcelable> arg(name: String) = ParcelableArgumentDelegate<T>(name)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancelChildren
|
||||||
|
import moxy.MvpPresenter
|
||||||
|
import moxy.MvpView
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
abstract class BasePresenter<V : MvpView> : MvpPresenter<V>(), KoinComponent, CoroutineScope {
|
||||||
|
|
||||||
|
private val job = SupervisorJob()
|
||||||
|
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = Dispatchers.Main + job
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
coroutineContext.cancelChildren()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.list
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class AdapterUpdater<T>(oldList: List<T>, newList: List<T>, getId: (T) -> Long) {
|
||||||
|
|
||||||
|
private val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
|
||||||
|
|
||||||
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
getId(oldList[oldItemPosition]) == getId(newList[newItemPosition])
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
Objects.equals(oldList[oldItemPosition], newList[newItemPosition])
|
||||||
|
|
||||||
|
override fun getOldListSize() = oldList.size
|
||||||
|
|
||||||
|
override fun getNewListSize() = newList.size
|
||||||
|
})
|
||||||
|
|
||||||
|
operator fun invoke(adapter: RecyclerView.Adapter<*>) {
|
||||||
|
diff.dispatchUpdatesTo(adapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.list
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koitharu.kotatsu.utils.ext.replaceWith
|
||||||
|
|
||||||
|
abstract class BaseRecyclerAdapter<T>(private val onItemClickListener: ((T) -> Unit)? = null) :
|
||||||
|
RecyclerView.Adapter<BaseViewHolder<T>>(),
|
||||||
|
KoinComponent {
|
||||||
|
|
||||||
|
private val dataSet = ArrayList<T>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
@Suppress("LeakingThis")
|
||||||
|
setHasStableIds(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: BaseViewHolder<T>, position: Int) {
|
||||||
|
holder.bind(dataSet[position])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItem(position: Int) = dataSet[position]
|
||||||
|
|
||||||
|
override fun getItemId(position: Int) = onGetItemId(dataSet[position])
|
||||||
|
|
||||||
|
protected fun findItemById(id: Long) = dataSet.find { x -> onGetItemId(x) == id }
|
||||||
|
|
||||||
|
protected fun findItemPositionById(id: Long) =
|
||||||
|
dataSet.indexOfFirst { x -> onGetItemId(x) == id }
|
||||||
|
|
||||||
|
fun replaceData(newData: List<T>) {
|
||||||
|
val updater = AdapterUpdater(dataSet, newData, this::onGetItemId)
|
||||||
|
dataSet.replaceWith(newData)
|
||||||
|
updater(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendData(newData: List<T>) {
|
||||||
|
val pos = dataSet.size
|
||||||
|
dataSet.addAll(newData)
|
||||||
|
notifyItemRangeInserted(pos, newData.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendItem(newItem: T) {
|
||||||
|
dataSet.add(newItem)
|
||||||
|
notifyItemInserted(dataSet.lastIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeItem(item: T) {
|
||||||
|
removeItemAt(dataSet.indexOf(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeItemAt(position: Int) {
|
||||||
|
if (position in dataSet.indices) {
|
||||||
|
dataSet.removeAt(position)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearData() {
|
||||||
|
dataSet.clear()
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun getItemCount() = dataSet.size
|
||||||
|
|
||||||
|
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<T> {
|
||||||
|
return onCreateViewHolder(parent).also { holder ->
|
||||||
|
if (onItemClickListener != null) {
|
||||||
|
holder.itemView.setOnClickListener {
|
||||||
|
onItemClickListener.invoke(holder.requireData())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.also(this::onViewHolderCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun onViewHolderCreated(holder: BaseViewHolder<T>) = Unit
|
||||||
|
|
||||||
|
protected abstract fun onCreateViewHolder(parent: ViewGroup): BaseViewHolder<T>
|
||||||
|
|
||||||
|
protected abstract fun onGetItemId(item: T): Long
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.list
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koitharu.kotatsu.utils.ext.inflate
|
||||||
|
|
||||||
|
abstract class BaseViewHolder<T> protected constructor(view: View) :
|
||||||
|
RecyclerView.ViewHolder(view), LayoutContainer, KoinComponent {
|
||||||
|
|
||||||
|
constructor(parent: ViewGroup, @LayoutRes resId: Int) : this(parent.inflate(resId))
|
||||||
|
|
||||||
|
override val containerView: View?
|
||||||
|
get() = itemView
|
||||||
|
|
||||||
|
protected var boundData: T? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
val context get() = itemView.context!!
|
||||||
|
|
||||||
|
fun bind(data: T) {
|
||||||
|
boundData = data
|
||||||
|
onBind(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireData() = boundData ?: throw IllegalStateException("Calling requireData() before bind()")
|
||||||
|
|
||||||
|
abstract fun onBind(data: T)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.list
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
abstract class BoundsScrollListener(private val offsetTop: Int, private val offsetBottom: Int) :
|
||||||
|
RecyclerView.OnScrollListener() {
|
||||||
|
|
||||||
|
constructor(offset: Int = 0) : this(offset, offset)
|
||||||
|
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
val layoutManager = (recyclerView.layoutManager as? LinearLayoutManager) ?: return
|
||||||
|
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||||
|
if (firstVisibleItemPosition <= offsetTop) {
|
||||||
|
onScrolledToTop(recyclerView)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val visibleItemCount = layoutManager.childCount
|
||||||
|
val totalItemCount = layoutManager.itemCount
|
||||||
|
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount - offsetBottom && firstVisibleItemPosition >= 0) {
|
||||||
|
onScrolledToEnd(recyclerView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun onScrolledToTop(recyclerView: RecyclerView)
|
||||||
|
|
||||||
|
abstract fun onScrolledToEnd(recyclerView: RecyclerView)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.list
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
class PaginationScrollListener(offset: Int, private val callback: Callback) : BoundsScrollListener(0, offset) {
|
||||||
|
|
||||||
|
private var lastTotalCount = 0
|
||||||
|
|
||||||
|
override fun onScrolledToTop(recyclerView: RecyclerView) = Unit
|
||||||
|
|
||||||
|
override fun onScrolledToEnd(recyclerView: RecyclerView) {
|
||||||
|
val total = recyclerView.adapter?.itemCount ?: 0
|
||||||
|
if (total > lastTotalCount) {
|
||||||
|
callback.onRequestMoreItems(total)
|
||||||
|
lastTotalCount = total
|
||||||
|
} else if (total < lastTotalCount) {
|
||||||
|
lastTotalCount = total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
|
||||||
|
fun onRequestMoreItems(offset: Int)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.list
|
||||||
|
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.Px
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
class SpacingItemDecoration(@Px private val spacing: Int) : RecyclerView.ItemDecoration() {
|
||||||
|
|
||||||
|
private val halfSpacing = spacing / 2
|
||||||
|
|
||||||
|
override fun getItemOffsets(
|
||||||
|
outRect: Rect,
|
||||||
|
view: View,
|
||||||
|
parent: RecyclerView,
|
||||||
|
state: RecyclerView.State
|
||||||
|
) {
|
||||||
|
val spans = (parent.layoutManager as? GridLayoutManager)?.spanCount ?: 1
|
||||||
|
val position = parent.getChildAdapterPosition(view)
|
||||||
|
outRect.set(
|
||||||
|
if (position % spans == 0) spacing else halfSpacing,
|
||||||
|
if (position < spans) spacing else halfSpacing,
|
||||||
|
if ((position + 1) % spans == 0) spacing else halfSpacing,
|
||||||
|
spacing //TODO check bottom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.widgets
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
|
|
||||||
|
|
||||||
|
class CoverImageView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
|
) : AppCompatImageView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
val originalWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
val calculatedHeight: Int = (originalWidth * ASPECT_RATIO_HEIGHT / ASPECT_RATIO_WIDTH).toInt()
|
||||||
|
|
||||||
|
super.onMeasure(
|
||||||
|
MeasureSpec.makeMeasureSpec(originalWidth, MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(calculatedHeight, MeasureSpec.EXACTLY)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
|
||||||
|
const val ASPECT_RATIO_HEIGHT = 18f
|
||||||
|
const val ASPECT_RATIO_WIDTH = 13f
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.common.widgets
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
|
||||||
|
class RectFrameLayout @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
|
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
public override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
super.onMeasure(widthMeasureSpec, widthMeasureSpec)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||||
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.ui.common.BaseActivity
|
||||||
|
import org.koitharu.kotatsu.ui.main.list.MangaListFragment
|
||||||
|
|
||||||
|
class MainActivity : BaseActivity() {
|
||||||
|
|
||||||
|
private lateinit var drawerToggle: ActionBarDrawerToggle
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
|
drawerToggle = ActionBarDrawerToggle(this, drawer, toolbar, R.string.open_menu, R.string.close_menu)
|
||||||
|
drawer.addDrawerListener(drawerToggle)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
supportActionBar?.setHomeButtonEnabled(true)
|
||||||
|
|
||||||
|
if (!supportFragmentManager.isStateSaved) {
|
||||||
|
supportFragmentManager.beginTransaction()
|
||||||
|
.replace(R.id.container, MangaListFragment.newInstance(MangaSource.READMANGA_RU))
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onPostCreate(savedInstanceState)
|
||||||
|
drawerToggle.syncState()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
drawerToggle.onConfigurationChanged(newConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
return drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import coil.api.load
|
||||||
|
import coil.request.RequestDisposable
|
||||||
|
import kotlinx.android.synthetic.main.item_manga_grid.*
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||||
|
|
||||||
|
class MangaGridHolder(parent: ViewGroup) : BaseViewHolder<Manga>(parent, R.layout.item_manga_grid) {
|
||||||
|
|
||||||
|
private var coverRequest: RequestDisposable? = null
|
||||||
|
|
||||||
|
override fun onBind(data: Manga) {
|
||||||
|
coverRequest?.dispose()
|
||||||
|
textView_title.text = data.title
|
||||||
|
coverRequest = imageView_cover.load(data.coverUrl) {
|
||||||
|
crossfade(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||||
|
|
||||||
|
class MangaListAdapter(onItemClickListener: ((Manga) -> Unit)?) :
|
||||||
|
BaseRecyclerAdapter<Manga>(onItemClickListener) {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup) = MangaGridHolder(parent)
|
||||||
|
|
||||||
|
override fun onGetItemId(item: Manga) = item.id
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlinx.android.synthetic.main.fragment_list.*
|
||||||
|
import moxy.ktx.moxyPresenter
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.ui.common.BaseFragment
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.SpacingItemDecoration
|
||||||
|
import org.koitharu.kotatsu.utils.ext.hasItems
|
||||||
|
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||||
|
|
||||||
|
class MangaListFragment : BaseFragment(R.layout.fragment_list), MangaListView,
|
||||||
|
PaginationScrollListener.Callback {
|
||||||
|
|
||||||
|
private val presenter by moxyPresenter(factory = ::MangaListPresenter)
|
||||||
|
|
||||||
|
private val source by arg<MangaSource>(ARG_SOURCE)
|
||||||
|
|
||||||
|
private lateinit var adapter: MangaListAdapter
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
adapter = MangaListAdapter {
|
||||||
|
|
||||||
|
}
|
||||||
|
recyclerView.addItemDecoration(SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.grid_spacing)))
|
||||||
|
recyclerView.adapter = adapter
|
||||||
|
recyclerView.addOnScrollListener(PaginationScrollListener(4, this))
|
||||||
|
swipeRefreshLayout.setOnRefreshListener {
|
||||||
|
presenter.loadList(source, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
presenter.loadList(source, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestMoreItems(offset: Int) {
|
||||||
|
presenter.loadList(source, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onListChanged(list: List<Manga>) {
|
||||||
|
adapter.replaceData(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onListAppended(list: List<Manga>) {
|
||||||
|
adapter.appendData(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLoadingChanged(isLoading: Boolean) {
|
||||||
|
val hasItems = recyclerView.hasItems
|
||||||
|
progressBar.isVisible = isLoading && !hasItems
|
||||||
|
swipeRefreshLayout.isRefreshing = isLoading && hasItems
|
||||||
|
swipeRefreshLayout.isEnabled = !progressBar.isVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val ARG_SOURCE = "provider"
|
||||||
|
|
||||||
|
fun newInstance(provider: MangaSource) = MangaListFragment().withArgs(1) {
|
||||||
|
putParcelable(ARG_SOURCE, provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import moxy.InjectViewState
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.domain.MangaProviderFactory
|
||||||
|
import org.koitharu.kotatsu.ui.common.BasePresenter
|
||||||
|
|
||||||
|
@InjectViewState
|
||||||
|
class MangaListPresenter : BasePresenter<MangaListView>() {
|
||||||
|
|
||||||
|
fun loadList(source: MangaSource, offset: Int) {
|
||||||
|
launch {
|
||||||
|
viewState.onLoadingChanged(true)
|
||||||
|
try {
|
||||||
|
val list = withContext(Dispatchers.IO) {
|
||||||
|
MangaProviderFactory.create(source)
|
||||||
|
.getList(offset)
|
||||||
|
}
|
||||||
|
if (offset == 0) {
|
||||||
|
viewState.onListChanged(list)
|
||||||
|
} else {
|
||||||
|
viewState.onListAppended(list)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
viewState.onLoadingChanged(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list
|
||||||
|
|
||||||
|
import moxy.MvpView
|
||||||
|
import moxy.viewstate.strategy.AddToEndSingleStrategy
|
||||||
|
import moxy.viewstate.strategy.AddToEndSingleTagStrategy
|
||||||
|
import moxy.viewstate.strategy.AddToEndStrategy
|
||||||
|
import moxy.viewstate.strategy.StateStrategyType
|
||||||
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
|
||||||
|
interface MangaListView : MvpView {
|
||||||
|
|
||||||
|
@StateStrategyType(AddToEndSingleTagStrategy::class, tag = "content")
|
||||||
|
fun onListChanged(list: List<Manga>)
|
||||||
|
|
||||||
|
@StateStrategyType(AddToEndStrategy::class, tag = "content")
|
||||||
|
fun onListAppended(list: List<Manga>)
|
||||||
|
|
||||||
|
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||||
|
fun onLoadingChanged(isLoading: Boolean)
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.delegates
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class ParcelableArgumentDelegate<T : Parcelable>(private val name: String) : ReadOnlyProperty<Fragment, T> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
|
||||||
|
return thisRef.requireArguments().getParcelable(name)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.delegates
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
class StringArgumentDelegate(private val name: String) : ReadOnlyProperty<Fragment, String?> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Fragment, property: KProperty<*>): String? {
|
||||||
|
return thisRef.arguments?.getString(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
fun <T> MutableCollection<T>.replaceWith(subject: Iterable<T>) {
|
||||||
|
clear()
|
||||||
|
addAll(subject)
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
|
|
||||||
|
inline fun <T, R> T.safe(action: T.() -> R?) = try {
|
||||||
|
this.action()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
|
suspend fun Call.await() = suspendCancellableCoroutine<Response> { cont ->
|
||||||
|
this.enqueue(object : Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
if (!cont.isCancelled) {
|
||||||
|
cont.resumeWithException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
cont.resume(response)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
cont.invokeOnCancellation {
|
||||||
|
safe {
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/src/main/java/org/koitharu/kotatsu/utils/ext/DateExt.kt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import org.intellij.lang.annotations.PrintFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
|
fun Date.format(pattern: String): String = SimpleDateFormat(pattern).format(this)
|
||||||
|
|
||||||
|
fun Date.calendar(): Calendar = Calendar.getInstance().also {
|
||||||
|
it.time = this
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
|
||||||
|
fun <T : Fragment> T.withArgs(size: Int, block: Bundle.() -> Unit): T {
|
||||||
|
val b = Bundle(size)
|
||||||
|
b.block()
|
||||||
|
this.arguments = b
|
||||||
|
return this
|
||||||
|
}
|
||||||
18
app/src/main/java/org/koitharu/kotatsu/utils/ext/JsoupExt.kt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.internal.closeQuietly
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
|
||||||
|
fun Response.parseHtml(): Document {
|
||||||
|
val stream = body?.byteStream() ?: throw NullPointerException("Response body is null")
|
||||||
|
val charset = body!!.contentType()?.charset()?.name()
|
||||||
|
val doc = Jsoup.parse(
|
||||||
|
stream,
|
||||||
|
charset,
|
||||||
|
this.request.url.toString()
|
||||||
|
)
|
||||||
|
closeQuietly()
|
||||||
|
return doc
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.text.NumberFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
fun Number?.asBoolean() = (this?.toInt() ?: 0) > 0
|
||||||
|
|
||||||
|
fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? = ' '): String {
|
||||||
|
val formatter = NumberFormat.getInstance(Locale.US) as DecimalFormat
|
||||||
|
val symbols = formatter.decimalFormatSymbols
|
||||||
|
if (thousandsSep != null) {
|
||||||
|
symbols.groupingSeparator = thousandsSep
|
||||||
|
formatter.isGroupingUsed = true
|
||||||
|
} else {
|
||||||
|
formatter.isGroupingUsed = false
|
||||||
|
}
|
||||||
|
symbols.decimalSeparator = decPoint
|
||||||
|
formatter.decimalFormatSymbols = symbols
|
||||||
|
formatter.minimumFractionDigits = decimals
|
||||||
|
formatter.maximumFractionDigits = decimals
|
||||||
|
return when (this) {
|
||||||
|
is Float,
|
||||||
|
is Double -> formatter.format(this.toDouble())
|
||||||
|
else -> formatter.format(this.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import androidx.annotation.Px
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Px
|
||||||
|
fun Resources.resolveDp(dp: Int) = (dp * displayMetrics.density).roundToInt()
|
||||||
|
|
||||||
|
@Px
|
||||||
|
fun Resources.resolveDp(dp: Float) = dp * displayMetrics.density
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
fun String.longHashCode(): Long {
|
||||||
|
var h = 1125899906842597L
|
||||||
|
val len: Int = this.length
|
||||||
|
for (i in 0 until len) {
|
||||||
|
h = 31 * h + this[i].toLong()
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
22
app/src/main/java/org/koitharu/kotatsu/utils/ext/ThemeExt.kt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Color
|
||||||
|
import androidx.annotation.AttrRes
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.Px
|
||||||
|
import androidx.core.content.res.use
|
||||||
|
|
||||||
|
@Px
|
||||||
|
fun Context.getThemeDimen(@AttrRes resId: Int) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||||
|
it.getDimension(0, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getThemeDrawable(@AttrRes resId: Int) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||||
|
it.getDrawable(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
fun Context.getThemeColor(@AttrRes resId: Int, @ColorInt default: Int = Color.TRANSPARENT) = obtainStyledAttributes(intArrayOf(resId)).use {
|
||||||
|
it.getColor(0, default)
|
||||||
|
}
|
||||||
55
app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
fun View.hideKeyboard() {
|
||||||
|
val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.hideSoftInputFromWindow(this.windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.showKeyboard() {
|
||||||
|
val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.showSoftInput(this, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
val EditText.plainText
|
||||||
|
get() = text?.toString().orEmpty()
|
||||||
|
|
||||||
|
inline fun <reified T : View> ViewGroup.inflate(@LayoutRes resId: Int) =
|
||||||
|
LayoutInflater.from(context).inflate(resId, this, false) as T
|
||||||
|
|
||||||
|
val TextView.hasText get() = !text.isNullOrEmpty()
|
||||||
|
|
||||||
|
fun RecyclerView.lookupSpanSize(callback: (Int) -> Int) {
|
||||||
|
(layoutManager as? GridLayoutManager)?.spanSizeLookup =
|
||||||
|
object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
override fun getSpanSize(position: Int) = callback(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val RecyclerView.hasItems: Boolean
|
||||||
|
get() = (adapter?.itemCount ?: 0) > 0
|
||||||
|
|
||||||
|
var TextView.drawableStart: Drawable?
|
||||||
|
get() = compoundDrawablesRelative[0]
|
||||||
|
set(value) {
|
||||||
|
val old = compoundDrawablesRelative
|
||||||
|
setCompoundDrawablesRelativeWithIntrinsicBounds(value, old[1], old[2], old[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
var TextView.drawableEnd: Drawable?
|
||||||
|
get() = compoundDrawablesRelative[2]
|
||||||
|
set(value) {
|
||||||
|
val old = compoundDrawablesRelative
|
||||||
|
setCompoundDrawablesRelativeWithIntrinsicBounds(old[0], old[1], value, old[3])
|
||||||
|
}
|
||||||
31
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_history.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" />
|
||||||
|
</vector>
|
||||||
171
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
14
app/src/main/res/drawable/ic_star_half.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<group>
|
||||||
|
<clip-path android:pathData="M0,0h24v24H0V0z M 0,0" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4V6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
|
|
||||||
10
app/src/main/res/drawable/ic_storage.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M18,4v16L6,20L6,8.83L10.83,4L18,4m0,-2h-8L4,8v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM9,7h2v4L9,11zM12,7h2v4h-2zM15,7h2v4h-2z" />
|
||||||
|
</vector>
|
||||||
45
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
android:id="@+id/drawer"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.main.MainActivity"
|
||||||
|
tools:openDrawer="start">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
|
app:popupTheme="@style/ThemeOverlay.MaterialComponents.Light" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.navigation.NavigationView
|
||||||
|
android:layout_width="260dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
app:menu="@menu/nav_drawer" />
|
||||||
|
|
||||||
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
49
app/src/main/res/layout/fragment_list.xml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:spanCount="3" />
|
||||||
|
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_holder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_holder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
33
app/src/main/res/layout/item_manga_grid.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<org.koitharu.kotatsu.ui.common.widgets.CoverImageView
|
||||||
|
android:id="@+id/imageView_cover"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleType="centerCrop" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:lines="2"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:text="?android:textColorPrimary"
|
||||||
|
tools:text="@tools:sample/lorem" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
36
app/src/main/res/menu/nav_drawer.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<group>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_local_storage"
|
||||||
|
android:icon="@drawable/ic_storage"
|
||||||
|
android:title="@string/local_storage" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_favourites"
|
||||||
|
android:icon="@drawable/ic_star_half"
|
||||||
|
android:title="@string/favourites" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_history"
|
||||||
|
android:icon="@drawable/ic_history"
|
||||||
|
android:title="@string/history" />
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<group android:id="@+id/group_menu">
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_test"
|
||||||
|
android:title="Test" />
|
||||||
|
</group>
|
||||||
|
<!--
|
||||||
|
<item android:title="Title 1">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_item_six"
|
||||||
|
android:icon="@drawable/ic_drafts_black_24dp"
|
||||||
|
android:title="Item 6" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_item_seven"
|
||||||
|
android:icon="@drawable/ic_drafts_black_24dp"
|
||||||
|
android:title="Item 7" />
|
||||||
|
</menu>
|
||||||
|
</item>-->
|
||||||
|
</menu>
|
||||||
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
6
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#6200EE</color>
|
||||||
|
<color name="colorPrimaryDark">#3700B3</color>
|
||||||
|
<color name="colorAccent">#03DAC5</color>
|
||||||
|
</resources>
|
||||||
4
app/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="grid_spacing">5dp</dimen>
|
||||||
|
</resources>
|
||||||
5
app/src/main/res/values/ids.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<item name="toolbar" type="id" />
|
||||||
|
<item name="container" type="id" />
|
||||||
|
</resources>
|
||||||
8
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name" translatable="false">Kotatsu</string>
|
||||||
|
<string name="close_menu">Close menu</string>
|
||||||
|
<string name="open_menu">Open menu</string>
|
||||||
|
<string name="local_storage">Local storage</string>
|
||||||
|
<string name="favourites">Favourites</string>
|
||||||
|
<string name="history">History</string>
|
||||||
|
</resources>
|
||||||
10
app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
5
app/src/main/res/xml/backup_descriptor.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<full-backup-content>
|
||||||
|
<!-- TODO: Exclude specific shared preferences that contain GCM registration Id -->
|
||||||
|
<!-- https://developer.android.com/guide/topics/data/autobackup -->
|
||||||
|
</full-backup-content>
|
||||||
26
build.gradle
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = "1.3.61"
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath "com.android.tools.build:gradle:4.0.0-alpha09"
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
21
gradle.properties
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app"s APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
|
android.enableJetifier=true
|
||||||
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
|
kotlin.code.style=official
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Wed Jan 29 18:34:45 EET 2020
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-rc-1-all.zip
|
||||||
172
gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
84
gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
2
settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
include ':app'
|
||||||
|
rootProject.name = "OpenManga Next"
|
||||||