Compare commits
95 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3159c343c1 | ||
|
|
ceaa930623 | ||
|
|
6a8539106b | ||
|
|
7a24c3c08e | ||
|
|
251abeb090 | ||
|
|
a61fe9f98c | ||
|
|
d29c7bf91a | ||
|
|
ed4911c441 | ||
|
|
d40b4f3748 | ||
|
|
f3c4fe1914 | ||
|
|
55ee841bd0 | ||
|
|
657fb488ee | ||
|
|
4eef0b93fb | ||
|
|
f2be56435c | ||
|
|
fa6b3ad7ba | ||
|
|
52c05e6888 | ||
|
|
865bf0ba83 | ||
|
|
3f827d1bad | ||
|
|
0561d5f55c | ||
|
|
1bf2e1dacc | ||
|
|
db5a221b56 | ||
|
|
295285f132 | ||
|
|
5052b6c074 | ||
|
|
f98f45dc54 | ||
|
|
8d16950f46 | ||
|
|
74033b9f4a | ||
|
|
e497d47374 | ||
|
|
a97af59260 | ||
|
|
2197de98ea | ||
|
|
c004c7f71a | ||
|
|
69fc3ad4e8 | ||
|
|
678a8f0914 | ||
|
|
08c4c0bf1f | ||
|
|
f2a2656837 | ||
|
|
2011572270 | ||
|
|
3b682667e1 | ||
|
|
6da8de6463 | ||
|
|
039d415871 | ||
|
|
776f53bde0 | ||
|
|
58e535595e | ||
|
|
96ad5f6a6c | ||
|
|
043f7bedd8 | ||
|
|
69bcd8f7c0 | ||
|
|
8a58564812 | ||
|
|
d346cf431f | ||
|
|
c0bce4f3b1 | ||
|
|
94d258ddbb | ||
|
|
6bdba49284 | ||
|
|
9b99baf4bc | ||
|
|
5ad2a538bc | ||
|
|
28703e9bf2 | ||
|
|
e664efefe9 | ||
|
|
27a8694938 | ||
|
|
e0a6102d4d | ||
|
|
7106cf04ed | ||
|
|
2afdc5591a | ||
|
|
8eed4b67c3 | ||
|
|
edacef0f2b | ||
|
|
d28894f8cd | ||
|
|
ee8e921e1a | ||
|
|
480d4b1e9a | ||
|
|
a79c023220 | ||
|
|
efc50df243 | ||
|
|
905ea766b1 | ||
|
|
bce26f4557 | ||
|
|
a74b2c9b49 | ||
|
|
22bdf61bb3 | ||
|
|
1d812487a6 | ||
|
|
dfb78bed69 | ||
|
|
c64b6f112b | ||
|
|
bd88a8a8d3 | ||
|
|
5ccc96aeb9 | ||
|
|
ef72d10344 | ||
|
|
573f0b40d1 | ||
|
|
48f49edb19 | ||
|
|
aa22d9fdd8 | ||
|
|
ec98e4e9a4 | ||
|
|
5b10a781a6 | ||
|
|
29637b234c | ||
|
|
34dc238ef1 | ||
|
|
3c2675e650 | ||
|
|
3992a07340 | ||
|
|
2046d87031 | ||
|
|
0618d8c6f8 | ||
|
|
5bfc27835b | ||
|
|
cdc545ea32 | ||
|
|
449db97a2b | ||
|
|
e01380090d | ||
|
|
6d1505241e | ||
|
|
f303e49e97 | ||
|
|
0e6b50e302 | ||
|
|
868af1e6a2 | ||
|
|
34f7b111ee | ||
|
|
df27907c57 | ||
|
|
75583b9e65 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,3 +14,6 @@
|
|||||||
|
|
||||||
#Github pages
|
#Github pages
|
||||||
/gh-pages
|
/gh-pages
|
||||||
|
|
||||||
|
#Private files
|
||||||
|
/app/google-services.json
|
||||||
17
.idea/codeStyles/Project.xml
generated
17
.idea/codeStyles/Project.xml
generated
@@ -1,18 +1,12 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<AndroidXmlCodeStyleSettings>
|
<AndroidXmlCodeStyleSettings>
|
||||||
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
|
||||||
</AndroidXmlCodeStyleSettings>
|
</AndroidXmlCodeStyleSettings>
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<XML>
|
|
||||||
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
|
||||||
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
|
||||||
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
|
||||||
</XML>
|
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
@@ -23,6 +17,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>xmlns:android</NAME>
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -33,6 +28,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>xmlns:.*</NAME>
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -44,6 +40,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>.*:id</NAME>
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -54,6 +51,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>.*:name</NAME>
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -64,6 +62,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>name</NAME>
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -74,6 +73,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>style</NAME>
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -84,6 +84,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>.*</NAME>
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -95,6 +96,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>.*</NAME>
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
@@ -106,6 +108,7 @@
|
|||||||
<match>
|
<match>
|
||||||
<AND>
|
<AND>
|
||||||
<NAME>.*</NAME>
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
</AND>
|
</AND>
|
||||||
</match>
|
</match>
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<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" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -1,2 +1,27 @@
|
|||||||
# Pupil
|
# Pupil
|
||||||
Hitomi.la viewer for Android
|
|
||||||
|

|
||||||
|
*Pupil, Hitomi.la viewer for Android*
|
||||||
|
|
||||||
|
# Screenshot
|
||||||
|

|
||||||
|
*Main Screen*
|
||||||
|
|
||||||
|

|
||||||
|
*Reader Screen*
|
||||||
|
|
||||||
|
Images are censored to be SFW
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
Go [Releases page](https://github.com/tom5079/Pupil/releases) and get latest version or
|
||||||
|
Visit [github page](https://tom5079.github.io/Pupil/) (only available in Korean)
|
||||||
|
or Build app yourself
|
||||||
|
|
||||||
|
# Manual
|
||||||
|
|
||||||
|
[Manual](https://tom5079.github.io/Pupil/2019/06/06/manual-kr.html) is only available in Korean. Consider using translator.
|
||||||
|
|
||||||
|
# Contribution
|
||||||
|
|
||||||
|
Any kind of contribution is appriciated. Feel free to leave PR!
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ android {
|
|||||||
applicationId "xyz.quaver.pupil"
|
applicationId "xyz.quaver.pupil"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 21
|
versionCode 31
|
||||||
versionName "3.0"
|
versionName "4.2-beta1"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -26,11 +26,16 @@ android {
|
|||||||
}
|
}
|
||||||
buildTypes.each {
|
buildTypes.each {
|
||||||
it.buildConfigField('boolean', 'PRERELEASE', 'false')
|
it.buildConfigField('boolean', 'PRERELEASE', 'false')
|
||||||
|
it.buildConfigField('boolean', 'CENSOR', 'false')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
freeCompilerArgs += '-Xuse-experimental=kotlin.Experimental'
|
freeCompilerArgs += '-Xuse-experimental=kotlin.Experimental'
|
||||||
}
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -41,18 +46,21 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
|
||||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
implementation 'androidx.preference:preference:1.1.0-beta01'
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
|
implementation "androidx.biometric:biometric:1.0.0"
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||||
implementation 'com.google.firebase:firebase-core:17.0.0'
|
implementation 'com.google.android.material:material:1.2.0-alpha02'
|
||||||
implementation 'com.google.firebase:firebase-perf:18.0.1'
|
implementation 'com.google.firebase:firebase-core:17.2.1'
|
||||||
|
implementation 'com.google.firebase:firebase-perf:19.0.3'
|
||||||
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
|
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
|
||||||
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
||||||
implementation 'com.github.clans:fab:1.6.4'
|
implementation 'com.github.clans:fab:1.6.4'
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":21,"versionName":"2.12","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":31,"versionName":"4.2-beta1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
||||||
@@ -25,15 +25,21 @@ import android.util.Log
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import xyz.quaver.hitomi.fetchNozomi
|
|
||||||
import xyz.quaver.hiyobi.cookie
|
import xyz.quaver.hiyobi.cookie
|
||||||
|
import xyz.quaver.hiyobi.createImgList
|
||||||
import xyz.quaver.hiyobi.getReader
|
import xyz.quaver.hiyobi.getReader
|
||||||
import xyz.quaver.hiyobi.user_agent
|
import xyz.quaver.hiyobi.user_agent
|
||||||
import xyz.quaver.pupil.ui.LockActivity
|
import xyz.quaver.pupil.ui.LockActivity
|
||||||
|
import xyz.quaver.pupil.util.getDownloadDirectory
|
||||||
|
import xyz.quaver.pupil.util.updateOldReaderGalleries
|
||||||
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
@@ -49,9 +55,8 @@ class ExampleInstrumentedTest {
|
|||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
// Context of the app under test.
|
// Context of the app under test.
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
Log.i("PUPILD", getDownloadDirectory(appContext).absolutePath ?: "")
|
||||||
assertEquals("xyz.quaver.pupil", appContext.packageName)
|
assertEquals("xyz.quaver.pupil", appContext.packageName)
|
||||||
|
|
||||||
Log.d("Pupil", fetchNozomi().first.size.toString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -60,17 +65,15 @@ class ExampleInstrumentedTest {
|
|||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
|
||||||
activityTestRule.launchActivity(Intent())
|
activityTestRule.launchActivity(Intent())
|
||||||
|
|
||||||
while(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_doSearch() {
|
fun test_doSearch() {
|
||||||
val reader = getReader(1426382)
|
val reader = getReader( 1426382)
|
||||||
|
|
||||||
val data: ByteArray
|
val data: ByteArray
|
||||||
|
|
||||||
with(URL(reader.readerItems[0].url).openConnection() as HttpsURLConnection) {
|
with(URL(createImgList(1426382, reader)[0].path).openConnection() as HttpsURLConnection) {
|
||||||
setRequestProperty("User-Agent", user_agent)
|
setRequestProperty("User-Agent", user_agent)
|
||||||
setRequestProperty("Cookie", cookie)
|
setRequestProperty("Cookie", cookie)
|
||||||
|
|
||||||
@@ -79,4 +82,38 @@ class ExampleInstrumentedTest {
|
|||||||
|
|
||||||
Log.d("Pupil", data.size.toString())
|
Log.d("Pupil", data.size.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||||
|
@Test
|
||||||
|
fun test_deleteCodeFromReader() {
|
||||||
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
|
||||||
|
val json = Json(JsonConfiguration.Stable)
|
||||||
|
|
||||||
|
listOf(
|
||||||
|
getDownloadDirectory(context),
|
||||||
|
File(context.cacheDir, "imageCache")
|
||||||
|
).forEach { root ->
|
||||||
|
root.listFiles()?.forEach gallery@{ gallery ->
|
||||||
|
val reader = json.parseJson(File(gallery, "reader.json").apply {
|
||||||
|
if (!exists())
|
||||||
|
return@gallery
|
||||||
|
}.readText())
|
||||||
|
.jsonObject.toMutableMap()
|
||||||
|
|
||||||
|
Log.d("PUPILD", gallery.name)
|
||||||
|
|
||||||
|
reader.remove("code")
|
||||||
|
|
||||||
|
File(gallery, "reader.json").writeText(JsonObject(reader).toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_updateOldReader() {
|
||||||
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
|
||||||
|
updateOldReaderGalleries(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="xyz.quaver.pupil">
|
package="xyz.quaver.pupil">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="28"/>
|
android:maxSdkVersion="28"/>
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||||
|
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Pupil"
|
android:name=".Pupil"
|
||||||
@@ -15,7 +17,20 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
tools:replace="android:theme">
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths"/>
|
||||||
|
|
||||||
|
</provider>
|
||||||
|
|
||||||
<activity android:name=".ui.LockActivity"/>
|
<activity android:name=".ui.LockActivity"/>
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -18,13 +18,10 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
import android.app.DownloadManager
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
@@ -33,7 +30,11 @@ import androidx.preference.PreferenceManager
|
|||||||
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
|
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
|
||||||
import com.google.android.gms.common.GooglePlayServicesRepairableException
|
import com.google.android.gms.common.GooglePlayServicesRepairableException
|
||||||
import com.google.android.gms.security.ProviderInstaller
|
import com.google.android.gms.security.ProviderInstaller
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import xyz.quaver.pupil.util.Histories
|
import xyz.quaver.pupil.util.Histories
|
||||||
|
import xyz.quaver.pupil.util.updateOldReaderGalleries
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class Pupil : MultiDexApplication() {
|
class Pupil : MultiDexApplication() {
|
||||||
@@ -76,6 +77,15 @@ class Pupil : MultiDexApplication() {
|
|||||||
preference.edit().putBoolean("channel_created", true).apply()
|
preference.edit().putBoolean("channel_created", true).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppCompatDelegate.setDefaultNightMode(when (preference.getBoolean("dark_mode", false)) {
|
||||||
|
true -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|
false -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
|
})
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
updateOldReaderGalleries(this@Pupil)
|
||||||
|
}
|
||||||
|
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,22 +18,22 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.adapters
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.util.SparseBooleanArray
|
import android.util.SparseBooleanArray
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.RequestManager
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import com.daimajia.swipe.SwipeLayout
|
||||||
|
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
|
||||||
|
import com.daimajia.swipe.interfaces.SwipeAdapterInterface
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import kotlinx.android.synthetic.main.item_galleryblock.view.*
|
import kotlinx.android.synthetic.main.item_galleryblock.view.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -44,18 +44,21 @@ import kotlinx.serialization.json.Json
|
|||||||
import kotlinx.serialization.json.JsonConfiguration
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
import xyz.quaver.hitomi.GalleryBlock
|
import xyz.quaver.hitomi.GalleryBlock
|
||||||
import xyz.quaver.hitomi.Reader
|
import xyz.quaver.hitomi.Reader
|
||||||
|
import xyz.quaver.pupil.BuildConfig
|
||||||
import xyz.quaver.pupil.Pupil
|
import xyz.quaver.pupil.Pupil
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.types.Tag
|
import xyz.quaver.pupil.types.Tag
|
||||||
|
import xyz.quaver.pupil.util.GalleryDownloader
|
||||||
import xyz.quaver.pupil.util.Histories
|
import xyz.quaver.pupil.util.Histories
|
||||||
import xyz.quaver.pupil.util.getCachedGallery
|
import xyz.quaver.pupil.util.getCachedGallery
|
||||||
|
import xyz.quaver.pupil.util.wordCapitalize
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
|
|
||||||
class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferred<String>>>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
class GalleryBlockAdapter(private val glide: RequestManager, private val galleries: List<Pair<GalleryBlock, Deferred<String>>>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
|
||||||
|
|
||||||
enum class ViewType {
|
enum class ViewType {
|
||||||
NEXT,
|
NEXT,
|
||||||
@@ -65,8 +68,8 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
|
|
||||||
private lateinit var favorites: Histories
|
private lateinit var favorites: Histories
|
||||||
|
|
||||||
inner class GalleryViewHolder(val view: CardView) : RecyclerView.ViewHolder(view) {
|
inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||||
fun bind(holder: GalleryViewHolder, item: Pair<GalleryBlock, Deferred<String>>) {
|
fun bind(item: Pair<GalleryBlock, Deferred<String>>) {
|
||||||
with(view) {
|
with(view) {
|
||||||
val resources = context.resources
|
val resources = context.resources
|
||||||
val languages = resources.getStringArray(R.array.languages).map {
|
val languages = resources.getStringArray(R.array.languages).map {
|
||||||
@@ -83,11 +86,16 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val cache = thumbnail.await()
|
val cache = thumbnail.await()
|
||||||
|
|
||||||
Glide.with(holder.view)
|
glide
|
||||||
.load(cache)
|
.load(cache)
|
||||||
.skipMemoryCache(true)
|
.skipMemoryCache(true)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.error(R.drawable.image_broken_variant)
|
.error(R.drawable.image_broken_variant)
|
||||||
|
.apply {
|
||||||
|
if (BuildConfig.CENSOR)
|
||||||
|
override(5, 8)
|
||||||
|
}
|
||||||
|
.fitCenter()
|
||||||
.into(galleryblock_thumbnail)
|
.into(galleryblock_thumbnail)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,12 +103,19 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
val readerCache = { File(getCachedGallery(context, galleryBlock.id), "reader.json") }
|
val readerCache = { File(getCachedGallery(context, galleryBlock.id), "reader.json") }
|
||||||
val imageCache = { File(getCachedGallery(context, galleryBlock.id), "images") }
|
val imageCache = { File(getCachedGallery(context, galleryBlock.id), "images") }
|
||||||
|
|
||||||
|
try {
|
||||||
|
Json(JsonConfiguration.Stable)
|
||||||
|
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||||
|
} catch(e: Exception) {
|
||||||
|
readerCache.invoke().delete()
|
||||||
|
}
|
||||||
|
|
||||||
if (readerCache.invoke().exists()) {
|
if (readerCache.invoke().exists()) {
|
||||||
val reader = Json(JsonConfiguration.Stable)
|
val reader = Json(JsonConfiguration.Stable)
|
||||||
.parse(Reader.serializer(), readerCache.invoke().readText())
|
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||||
|
|
||||||
with(galleryblock_progressbar) {
|
with(galleryblock_progressbar) {
|
||||||
max = reader.readerItems.size
|
max = reader.galleryInfo.size
|
||||||
progress = imageCache.invoke().list()?.size ?: 0
|
progress = imageCache.invoke().list()?.size ?: 0
|
||||||
|
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
@@ -125,7 +140,7 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
if (visibility == View.GONE) {
|
if (visibility == View.GONE) {
|
||||||
val reader = Json(JsonConfiguration.Stable)
|
val reader = Json(JsonConfiguration.Stable)
|
||||||
.parse(Reader.serializer(), readerCache.invoke().readText())
|
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||||
max = reader.readerItems.size
|
max = reader.galleryInfo.size
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,8 +161,6 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
view.galleryblock_progress_complete.visibility = View.INVISIBLE
|
view.galleryblock_progress_complete.visibility = View.INVISIBLE
|
||||||
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,19 +176,6 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
artists.isNotEmpty() -> View.VISIBLE
|
artists.isNotEmpty() -> View.VISIBLE
|
||||||
else -> View.GONE
|
else -> View.GONE
|
||||||
}
|
}
|
||||||
setOnClickListener {
|
|
||||||
if (artists.size > 1) {
|
|
||||||
AlertDialog.Builder(context).apply {
|
|
||||||
setAdapter(ArrayAdapter(context, android.R.layout.select_dialog_item, artists)) { _, index ->
|
|
||||||
for (callback in onChipClickedHandler)
|
|
||||||
callback.invoke(Tag("artist", artists[index]))
|
|
||||||
}
|
|
||||||
}.show()
|
|
||||||
} else {
|
|
||||||
for(callback in onChipClickedHandler)
|
|
||||||
callback.invoke(Tag("artist", artists.first()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
with(galleryblock_series) {
|
with(galleryblock_series) {
|
||||||
text =
|
text =
|
||||||
@@ -186,31 +186,8 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
series.isNotEmpty() -> View.VISIBLE
|
series.isNotEmpty() -> View.VISIBLE
|
||||||
else -> View.GONE
|
else -> View.GONE
|
||||||
}
|
}
|
||||||
setOnClickListener {
|
|
||||||
setOnClickListener {
|
|
||||||
if (series.size > 1) {
|
|
||||||
AlertDialog.Builder(context).apply {
|
|
||||||
setAdapter(ArrayAdapter(context, android.R.layout.select_dialog_item, series)) { _, index ->
|
|
||||||
for (callback in onChipClickedHandler)
|
|
||||||
callback.invoke(Tag("series", series[index]))
|
|
||||||
}
|
|
||||||
}.show()
|
|
||||||
} else {
|
|
||||||
for(callback in onChipClickedHandler)
|
|
||||||
callback.invoke(Tag("series", series.first()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
with(galleryblock_type) {
|
|
||||||
text = resources.getString(R.string.galleryblock_type, galleryBlock.type).wordCapitalize()
|
|
||||||
setOnClickListener {
|
|
||||||
setOnClickListener {
|
|
||||||
for(callback in onChipClickedHandler)
|
|
||||||
callback.invoke(Tag("type", galleryBlock.type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
galleryblock_type.text = resources.getString(R.string.galleryblock_type, galleryBlock.type).wordCapitalize()
|
||||||
with(galleryblock_language) {
|
with(galleryblock_language) {
|
||||||
text =
|
text =
|
||||||
resources.getString(R.string.galleryblock_language, languages[galleryBlock.language])
|
resources.getString(R.string.galleryblock_language, languages[galleryBlock.language])
|
||||||
@@ -218,16 +195,11 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
galleryBlock.language.isNotEmpty() -> View.VISIBLE
|
galleryBlock.language.isNotEmpty() -> View.VISIBLE
|
||||||
else -> View.GONE
|
else -> View.GONE
|
||||||
}
|
}
|
||||||
setOnClickListener {
|
|
||||||
setOnClickListener {
|
|
||||||
for(callback in onChipClickedHandler)
|
|
||||||
callback.invoke(Tag("language", galleryBlock.language))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
galleryblock_tag_group.removeAllViews()
|
galleryblock_tag_group.removeAllViews()
|
||||||
galleryBlock.relatedTags.forEach {
|
galleryBlock.relatedTags.forEach {
|
||||||
|
galleryblock_tag_group.addView(Chip(context).apply {
|
||||||
val tag = Tag.parse(it).let { tag ->
|
val tag = Tag.parse(it).let { tag ->
|
||||||
when {
|
when {
|
||||||
tag.area != null -> tag
|
tag.area != null -> tag
|
||||||
@@ -235,31 +207,25 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val chip = LayoutInflater.from(context)
|
chipIcon = when(tag.area) {
|
||||||
.inflate(R.layout.tag_chip, this, false) as Chip
|
|
||||||
|
|
||||||
val icon = when(tag.area) {
|
|
||||||
"male" -> {
|
"male" -> {
|
||||||
chip.setChipBackgroundColorResource(R.color.material_blue_700)
|
setChipBackgroundColorResource(R.color.material_blue_700)
|
||||||
chip.setTextColor(ContextCompat.getColor(context, android.R.color.white))
|
setTextColor(ContextCompat.getColor(context, android.R.color.white))
|
||||||
ContextCompat.getDrawable(context, R.drawable.ic_gender_male_white)
|
ContextCompat.getDrawable(context, R.drawable.ic_gender_male_white)
|
||||||
}
|
}
|
||||||
"female" -> {
|
"female" -> {
|
||||||
chip.setChipBackgroundColorResource(R.color.material_pink_600)
|
setChipBackgroundColorResource(R.color.material_pink_600)
|
||||||
chip.setTextColor(ContextCompat.getColor(context, android.R.color.white))
|
setTextColor(ContextCompat.getColor(context, android.R.color.white))
|
||||||
ContextCompat.getDrawable(context, R.drawable.ic_gender_female_white)
|
ContextCompat.getDrawable(context, R.drawable.ic_gender_female_white)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
text = tag.tag.wordCapitalize()
|
||||||
chip.chipIcon = icon
|
setOnClickListener {
|
||||||
chip.text = tag.tag.wordCapitalize()
|
|
||||||
chip.setOnClickListener {
|
|
||||||
for (callback in onChipClickedHandler)
|
for (callback in onChipClickedHandler)
|
||||||
callback.invoke(tag)
|
callback.invoke(tag)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
galleryblock_tag_group.addView(chip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
galleryblock_id.text = galleryBlock.id.toString()
|
galleryblock_id.text = galleryBlock.id.toString()
|
||||||
@@ -311,19 +277,12 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.wordCapitalize() : String {
|
|
||||||
val result = ArrayList<String>()
|
|
||||||
|
|
||||||
for (word in this.split(" "))
|
|
||||||
result.add(word.capitalize())
|
|
||||||
|
|
||||||
return result.joinToString(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val refreshTasks = HashMap<GalleryViewHolder, TimerTask>()
|
private val refreshTasks = HashMap<GalleryViewHolder, TimerTask>()
|
||||||
val completeFlag = SparseBooleanArray()
|
val completeFlag = SparseBooleanArray()
|
||||||
|
|
||||||
val onChipClickedHandler = ArrayList<((Tag) -> Unit)>()
|
val onChipClickedHandler = ArrayList<((Tag) -> Unit)>()
|
||||||
|
var onDownloadClickedHandler: ((Int) -> Unit)? = null
|
||||||
|
var onDeleteClickedHandler: ((Int) -> Unit)? = null
|
||||||
|
|
||||||
var showNext = false
|
var showNext = false
|
||||||
var showPrev = false
|
var showPrev = false
|
||||||
@@ -349,8 +308,47 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
if (holder is GalleryViewHolder)
|
if (holder is GalleryViewHolder) {
|
||||||
holder.bind(holder, galleries[position-(if (showPrev) 1 else 0)])
|
val gallery = galleries[position-(if (showPrev) 1 else 0)]
|
||||||
|
|
||||||
|
holder.bind(gallery)
|
||||||
|
|
||||||
|
with(holder.view.galleryblock_primary) {
|
||||||
|
setOnClickListener {
|
||||||
|
holder.view.performClick()
|
||||||
|
}
|
||||||
|
setOnLongClickListener {
|
||||||
|
holder.view.performLongClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.view.galleryblock_download.setOnClickListener {
|
||||||
|
onDownloadClickedHandler?.invoke(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.view.galleryblock_delete.setOnClickListener {
|
||||||
|
onDeleteClickedHandler?.invoke(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
mItemManger.bindView(holder.view, position)
|
||||||
|
|
||||||
|
holder.view.galleryblock_swipe_layout.addSwipeListener(object: SwipeLayout.SwipeListener {
|
||||||
|
override fun onStartOpen(layout: SwipeLayout?) {
|
||||||
|
mItemManger.closeAllExcept(layout)
|
||||||
|
|
||||||
|
holder.view.galleryblock_download.text = when(GalleryDownloader.get(gallery.first.id)) {
|
||||||
|
null -> holder.view.context.getString(R.string.main_download)
|
||||||
|
else -> holder.view.context.getString(android.R.string.cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClose(layout: SwipeLayout?) {}
|
||||||
|
override fun onHandRelease(layout: SwipeLayout?, xvel: Float, yvel: Float) {}
|
||||||
|
override fun onOpen(layout: SwipeLayout?) {}
|
||||||
|
override fun onStartClose(layout: SwipeLayout?) {}
|
||||||
|
override fun onUpdate(layout: SwipeLayout?, leftOffset: Int, topOffset: Int) {}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
|
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
|
||||||
@@ -376,4 +374,6 @@ class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Deferre
|
|||||||
else -> ViewType.GALLERY
|
else -> ViewType.GALLERY
|
||||||
}.ordinal
|
}.ordinal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getSwipeLayoutResourceId(position: Int) = R.id.galleryblock_swipe_layout
|
||||||
}
|
}
|
||||||
@@ -23,39 +23,44 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
import com.bumptech.glide.RequestManager
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import xyz.quaver.pupil.BuildConfig
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
|
import xyz.quaver.pupil.util.getCachedGallery
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class ReaderAdapter(private val images: List<String>) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
|
class ReaderAdapter(private val glide: RequestManager,
|
||||||
|
private val galleryID: Int,
|
||||||
|
private val images: List<String>) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
|
||||||
|
|
||||||
var isFullScreen = false
|
var isFullScreen = false
|
||||||
|
|
||||||
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
|
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
LayoutInflater.from(parent.context).inflate(
|
return LayoutInflater.from(parent.context).inflate(
|
||||||
R.layout.item_reader, parent, false
|
R.layout.item_reader, parent, false
|
||||||
).let {
|
).let {
|
||||||
return ViewHolder(it)
|
ViewHolder(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val progressDrawable = CircularProgressDrawable(holder.view.context).apply {
|
holder.view as ImageView
|
||||||
strokeWidth = 10f
|
|
||||||
centerRadius = 100f
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
|
|
||||||
Glide.with(holder.view)
|
glide
|
||||||
.load(images[position])
|
.load(File(getCachedGallery(holder.view.context, galleryID), images[position]))
|
||||||
|
.dontTransform()
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.skipMemoryCache(true)
|
.skipMemoryCache(true)
|
||||||
.placeholder(progressDrawable)
|
|
||||||
.error(R.drawable.image_broken_variant)
|
.error(R.drawable.image_broken_variant)
|
||||||
.into(holder.view as ImageView)
|
.apply {
|
||||||
|
if (BuildConfig.CENSOR)
|
||||||
|
override(5, 8)
|
||||||
|
}
|
||||||
|
.fitCenter()
|
||||||
|
.into(holder.view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount() = images.size
|
override fun getItemCount() = images.size
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Pupil, Hitomi.la viewer for Android
|
||||||
|
* Copyright (C) 2019 tom5079
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.bumptech.glide.RequestManager
|
||||||
|
import xyz.quaver.pupil.BuildConfig
|
||||||
|
|
||||||
|
class ThumbnailAdapter(private val glide: RequestManager, private val thumbnails: List<String>) : RecyclerView.Adapter<ThumbnailAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
class ViewHolder(val view: ImageView) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
return ViewHolder(ImageView(parent.context))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
glide
|
||||||
|
.load(thumbnails[position])
|
||||||
|
.apply {
|
||||||
|
if (BuildConfig.CENSOR)
|
||||||
|
override(5, 8)
|
||||||
|
}
|
||||||
|
.fitCenter()
|
||||||
|
.into(holder.view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = thumbnails.size
|
||||||
|
|
||||||
|
}
|
||||||
275
app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt
Normal file
275
app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
/*
|
||||||
|
* Pupil, Hitomi.la viewer for Android
|
||||||
|
* Copyright (C) 2019 tom5079
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package xyz.quaver.pupil.ui
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.LinearLayout.LayoutParams
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.google.android.material.chip.Chip
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.android.synthetic.main.dialog_galleryblock.*
|
||||||
|
import kotlinx.android.synthetic.main.gallery_details.view.*
|
||||||
|
import kotlinx.android.synthetic.main.item_gallery_details.view.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import xyz.quaver.hitomi.Gallery
|
||||||
|
import xyz.quaver.hitomi.GalleryBlock
|
||||||
|
import xyz.quaver.hitomi.getGallery
|
||||||
|
import xyz.quaver.hitomi.getGalleryBlock
|
||||||
|
import xyz.quaver.pupil.BuildConfig
|
||||||
|
import xyz.quaver.pupil.Pupil
|
||||||
|
import xyz.quaver.pupil.R
|
||||||
|
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||||
|
import xyz.quaver.pupil.adapters.ThumbnailAdapter
|
||||||
|
import xyz.quaver.pupil.types.Tag
|
||||||
|
import xyz.quaver.pupil.util.ItemClickSupport
|
||||||
|
import xyz.quaver.pupil.util.wordCapitalize
|
||||||
|
|
||||||
|
class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(context) {
|
||||||
|
|
||||||
|
private val languages = context.resources.getStringArray(R.array.languages).map {
|
||||||
|
it.split("|").let { split ->
|
||||||
|
Pair(split[0], split[1])
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
private val glide = Glide.with(context)
|
||||||
|
|
||||||
|
val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.dialog_galleryblock)
|
||||||
|
|
||||||
|
window?.attributes.apply {
|
||||||
|
this ?: return@apply
|
||||||
|
|
||||||
|
width = LayoutParams.MATCH_PARENT
|
||||||
|
height = LayoutParams.MATCH_PARENT
|
||||||
|
}
|
||||||
|
|
||||||
|
with(gallery_fab) {
|
||||||
|
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.arrow_right))
|
||||||
|
setOnClickListener {
|
||||||
|
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||||
|
putExtra("galleryID", galleryID)
|
||||||
|
})
|
||||||
|
(context.applicationContext as Pupil).histories.add(galleryID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val gallery = getGallery(galleryID)
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
gallery_progressbar.visibility = View.GONE
|
||||||
|
gallery_title.text = gallery.title
|
||||||
|
gallery_artist.text = gallery.artists.joinToString(", ") { it.wordCapitalize() }
|
||||||
|
|
||||||
|
with(gallery_type) {
|
||||||
|
text = gallery.type.wordCapitalize()
|
||||||
|
setOnClickListener {
|
||||||
|
gallery.type.let {
|
||||||
|
when (it) {
|
||||||
|
"artist CG" -> "artistcg"
|
||||||
|
"game CG" -> "gamecg"
|
||||||
|
else -> it
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
onChipClickedHandler.forEach { handler ->
|
||||||
|
handler.invoke(Tag("type", it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Glide.with(context)
|
||||||
|
.load(gallery.cover)
|
||||||
|
.apply {
|
||||||
|
if (BuildConfig.CENSOR)
|
||||||
|
override(5, 8)
|
||||||
|
}.into(gallery_cover)
|
||||||
|
|
||||||
|
addDetails(gallery)
|
||||||
|
addThumbnails(gallery)
|
||||||
|
addRelated(gallery)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Snackbar.make(gallery_layout, R.string.unable_to_connect, Snackbar.LENGTH_INDEFINITE).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addDetails(gallery: Gallery) {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
|
||||||
|
inflater.inflate(R.layout.gallery_details, gallery_contents, false).apply {
|
||||||
|
gallery_details.setText(R.string.gallery_details)
|
||||||
|
|
||||||
|
listOf(
|
||||||
|
R.string.gallery_artists,
|
||||||
|
R.string.gallery_groups,
|
||||||
|
R.string.gallery_language,
|
||||||
|
R.string.gallery_series,
|
||||||
|
R.string.gallery_characters,
|
||||||
|
R.string.gallery_tags
|
||||||
|
).zip(
|
||||||
|
listOf(
|
||||||
|
gallery.artists.map { Tag("artist", it) },
|
||||||
|
gallery.groups.map { Tag("group", it) },
|
||||||
|
listOf(gallery.language).map { Tag("language", it) },
|
||||||
|
gallery.series.map { Tag("series", it) },
|
||||||
|
gallery.characters.map { Tag("character", it) },
|
||||||
|
gallery.tags.map {
|
||||||
|
Tag.parse(it).let { tag ->
|
||||||
|
when {
|
||||||
|
tag.area != null -> tag
|
||||||
|
else -> Tag("tag", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).filter {
|
||||||
|
(_, content) -> content.isNotEmpty()
|
||||||
|
}.forEach { (title, content) ->
|
||||||
|
inflater.inflate(R.layout.item_gallery_details, gallery_details_contents, false).apply {
|
||||||
|
gallery_details_type.setText(title)
|
||||||
|
|
||||||
|
content.forEach { tag ->
|
||||||
|
gallery_details_tags.addView(
|
||||||
|
Chip(context).apply {
|
||||||
|
chipIcon = when(tag.area) {
|
||||||
|
"male" -> {
|
||||||
|
setChipBackgroundColorResource(R.color.material_blue_700)
|
||||||
|
setTextColor(ContextCompat.getColor(context, android.R.color.white))
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.ic_gender_male_white)
|
||||||
|
}
|
||||||
|
"female" -> {
|
||||||
|
setChipBackgroundColorResource(R.color.material_pink_600)
|
||||||
|
setTextColor(ContextCompat.getColor(context, android.R.color.white))
|
||||||
|
ContextCompat.getDrawable(context, R.drawable.ic_gender_female_white)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
text = when (tag.area) {
|
||||||
|
"language" -> languages[tag.tag]
|
||||||
|
else -> tag.tag.wordCapitalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
onChipClickedHandler.forEach { handler ->
|
||||||
|
handler.invoke(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
gallery_details_contents.addView(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
gallery_contents.addView(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addThumbnails(gallery: Gallery) {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
|
||||||
|
inflater.inflate(R.layout.gallery_details, gallery_contents, false).apply {
|
||||||
|
gallery_details.setText(R.string.gallery_thumbnails)
|
||||||
|
|
||||||
|
RecyclerView(context).apply {
|
||||||
|
layoutManager = GridLayoutManager(context, 3)
|
||||||
|
adapter = ThumbnailAdapter(glide, gallery.thumbnails)
|
||||||
|
}.let {
|
||||||
|
gallery_details_contents.addView(it, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
gallery_contents.addView(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addRelated(gallery: Gallery) {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val galleries = ArrayList<Pair<GalleryBlock, Deferred<String>>>()
|
||||||
|
|
||||||
|
val adapter = GalleryBlockAdapter(glide, galleries).apply {
|
||||||
|
onChipClickedHandler.add { tag ->
|
||||||
|
this@GalleryDialog.onChipClickedHandler.forEach { handler ->
|
||||||
|
handler.invoke(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
gallery.related.forEachIndexed { i, galleryID ->
|
||||||
|
async(Dispatchers.IO) {
|
||||||
|
getGalleryBlock(galleryID)
|
||||||
|
}.let {
|
||||||
|
val galleryBlock = it.await() ?: return@let
|
||||||
|
|
||||||
|
galleries.add(Pair(galleryBlock, GlobalScope.async { galleryBlock.thumbnails.first() }))
|
||||||
|
adapter.notifyItemInserted(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inflater.inflate(R.layout.gallery_details, gallery_contents, false).apply {
|
||||||
|
gallery_details.setText(R.string.gallery_related)
|
||||||
|
|
||||||
|
RecyclerView(context).apply {
|
||||||
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
this.adapter = adapter
|
||||||
|
|
||||||
|
ItemClickSupport.addTo(this)
|
||||||
|
.setOnItemClickListener { _, position, _ ->
|
||||||
|
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||||
|
putExtra("galleryID", galleries[position].first.id)
|
||||||
|
})
|
||||||
|
(context.applicationContext as Pupil).histories.add(galleries[position].first.id)
|
||||||
|
}
|
||||||
|
.setOnItemLongClickListener { _, position, _ ->
|
||||||
|
GalleryDialog(context, galleries[position].first.id).apply {
|
||||||
|
onChipClickedHandler.add { tag ->
|
||||||
|
this@GalleryDialog.onChipClickedHandler.forEach { it.invoke(tag) }
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
gallery_details_contents.addView(it, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
|
||||||
|
}
|
||||||
|
}.let {
|
||||||
|
gallery_contents.addView(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
package xyz.quaver.pupil.ui
|
package xyz.quaver.pupil.ui
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.andrognito.patternlockview.PatternLockView
|
import com.andrognito.patternlockview.PatternLockView
|
||||||
@@ -35,7 +36,18 @@ class LockActivity : AppCompatActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_lock)
|
setContentView(R.layout.activity_lock)
|
||||||
|
|
||||||
val lockManager = LockManager(this)
|
val lockManager = try {
|
||||||
|
LockManager(this)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
AlertDialog.Builder(this).apply {
|
||||||
|
setTitle(R.string.warning)
|
||||||
|
setMessage(R.string.lock_corrupted)
|
||||||
|
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val mode = intent.getStringExtra("mode")
|
val mode = intent.getStringExtra("mode")
|
||||||
|
|
||||||
@@ -46,9 +58,10 @@ class LockActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
when(mode) {
|
when(mode) {
|
||||||
null -> {
|
null -> {
|
||||||
if (lockManager.empty()) {
|
if (lockManager.isEmpty()) {
|
||||||
setResult(RESULT_OK)
|
setResult(RESULT_OK)
|
||||||
finish()
|
finish()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"add_lock" -> {
|
"add_lock" -> {
|
||||||
|
|||||||
@@ -25,14 +25,16 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.text.*
|
import android.text.*
|
||||||
import android.text.style.AlignmentSpan
|
import android.text.style.AlignmentSpan
|
||||||
import android.view.*
|
import android.view.KeyEvent
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
@@ -42,7 +44,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
@@ -50,11 +51,11 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
|||||||
import com.arlib.floatingsearchview.FloatingSearchView
|
import com.arlib.floatingsearchview.FloatingSearchView
|
||||||
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
||||||
import com.arlib.floatingsearchview.util.view.SearchInputView
|
import com.arlib.floatingsearchview.util.view.SearchInputView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.android.synthetic.main.activity_main_content.*
|
import kotlinx.android.synthetic.main.activity_main_content.*
|
||||||
import kotlinx.android.synthetic.main.dialog_galleryblock.view.*
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@@ -126,6 +127,20 @@ class MainActivity : AppCompatActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
val lockManager = try {
|
||||||
|
LockManager(this)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.app.AlertDialog.Builder(this).apply {
|
||||||
|
setTitle(R.string.warning)
|
||||||
|
setMessage(R.string.lock_corrupted)
|
||||||
|
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockManager.isNotEmpty())
|
||||||
startActivityForResult(Intent(this, LockActivity::class.java), REQUEST_LOCK)
|
startActivityForResult(Intent(this, LockActivity::class.java), REQUEST_LOCK)
|
||||||
|
|
||||||
checkPermissions()
|
checkPermissions()
|
||||||
@@ -191,10 +206,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
|
val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
|
||||||
|
|
||||||
return when(keyCode) {
|
return when(keyCode) {
|
||||||
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||||
if (currentPage < maxPage) {
|
if (currentPage > 0) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
currentPage++
|
currentPage--
|
||||||
|
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
@@ -205,10 +220,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
KeyEvent.KEYCODE_VOLUME_UP -> {
|
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||||
if (currentPage > 0) {
|
if (currentPage < maxPage) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
currentPage--
|
currentPage++
|
||||||
|
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
@@ -243,6 +258,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun checkUpdate() {
|
private fun checkUpdate() {
|
||||||
|
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val ignoreUpdateUntil = preferences.getLong("ignore_update_until", 0)
|
||||||
|
|
||||||
|
if (ignoreUpdateUntil > System.currentTimeMillis())
|
||||||
|
return
|
||||||
|
|
||||||
fun extractReleaseNote(update: JsonObject, locale: String) : String {
|
fun extractReleaseNote(update: JsonObject, locale: String) : String {
|
||||||
val markdown = update["body"]!!.content
|
val markdown = update["body"]!!.content
|
||||||
|
|
||||||
@@ -297,6 +318,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val msg = extractReleaseNote(update, Locale.getDefault().language)
|
val msg = extractReleaseNote(update, Locale.getDefault().language)
|
||||||
setMessage(Markwon.create(context).toMarkdown(msg))
|
setMessage(Markwon.create(context).toMarkdown(msg))
|
||||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||||
|
if (!this@MainActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
AlertDialog.Builder(this@MainActivity).apply {
|
||||||
|
setTitle(R.string.warning)
|
||||||
|
setMessage(R.string.update_no_permission)
|
||||||
|
setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||||
|
}.show()
|
||||||
|
|
||||||
|
return@setPositiveButton
|
||||||
|
}
|
||||||
|
|
||||||
val request = DownloadManager.Request(Uri.parse(url)).apply {
|
val request = DownloadManager.Request(Uri.parse(url)).apply {
|
||||||
setDescription(getString(R.string.update_notification_description))
|
setDescription(getString(R.string.update_notification_description))
|
||||||
setTitle(getString(R.string.app_name))
|
setTitle(getString(R.string.app_name))
|
||||||
@@ -308,6 +339,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
registerReceiver(object: BroadcastReceiver() {
|
registerReceiver(object: BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
try {
|
||||||
val install = Intent(Intent.ACTION_VIEW).apply {
|
val install = Intent(Intent.ACTION_VIEW).apply {
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
setDataAndType(manager.getUriForDownloadedFile(id), manager.getMimeTypeForDownloadedFile(id))
|
setDataAndType(manager.getUriForDownloadedFile(id), manager.getMimeTypeForDownloadedFile(id))
|
||||||
@@ -315,10 +347,21 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
startActivity(install)
|
startActivity(install)
|
||||||
unregisterReceiver(this)
|
unregisterReceiver(this)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
AlertDialog.Builder(this@MainActivity).apply {
|
||||||
|
setTitle(R.string.update_failed)
|
||||||
|
setMessage(R.string.update_failed_message)
|
||||||
|
setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
}, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||||
}
|
}
|
||||||
setNegativeButton(android.R.string.no) { _, _ ->}
|
setNegativeButton(R.string.ignore_update) { _, _ ->
|
||||||
|
preferences.edit()
|
||||||
|
.putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
@@ -328,7 +371,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPermissions() {
|
private fun checkPermissions() {
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
|
if (!hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE))
|
||||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 13489)
|
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 13489)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +448,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
|
||||||
}
|
}
|
||||||
R.id.main_drawer_kakaotalk -> {
|
R.id.main_drawer_kakaotalk -> {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.kakaotalk))))
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.discord))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,7 +478,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
fetchGalleries(query, sortMode)
|
|
||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -461,6 +503,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
intent.putExtra("galleryID", gallery.id)
|
intent.putExtra("galleryID", gallery.id)
|
||||||
|
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
|
||||||
|
histories.add(gallery.id)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Snackbar.make(main_layout,
|
Snackbar.make(main_layout,
|
||||||
R.string.main_open_gallery_by_id_error, Snackbar.LENGTH_LONG).show()
|
R.string.main_open_gallery_by_id_error, Snackbar.LENGTH_LONG).show()
|
||||||
@@ -479,7 +523,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
with(main_recyclerview) {
|
with(main_recyclerview) {
|
||||||
adapter = GalleryBlockAdapter(galleries).apply {
|
adapter = GalleryBlockAdapter(Glide.with(this@MainActivity), galleries).apply {
|
||||||
onChipClickedHandler.add {
|
onChipClickedHandler.add {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
query = it.toQuery()
|
query = it.toQuery()
|
||||||
@@ -491,10 +535,66 @@ class MainActivity : AppCompatActivity() {
|
|||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onDownloadClickedHandler = { position ->
|
||||||
|
val galleryID = galleries[position].first.id
|
||||||
|
|
||||||
|
if (!completeFlag.get(galleryID, false)) {
|
||||||
|
val downloader = GalleryDownloader.get(galleryID)
|
||||||
|
|
||||||
|
if (downloader == null)
|
||||||
|
GalleryDownloader(context, galleryID, true).start()
|
||||||
|
else {
|
||||||
|
downloader.cancel()
|
||||||
|
downloader.clearNotification()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeAllItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteClickedHandler = { position ->
|
||||||
|
val galleryID = galleries[position].first.id
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
with(GalleryDownloader[galleryID]) {
|
||||||
|
this?.cancelAndJoin()
|
||||||
|
this?.clearNotification()
|
||||||
|
}
|
||||||
|
val cache = File(cacheDir, "imageCache/${galleryID}")
|
||||||
|
val data = getCachedGallery(context, galleryID)
|
||||||
|
cache.deleteRecursively()
|
||||||
|
data.deleteRecursively()
|
||||||
|
|
||||||
|
downloads.remove(galleryID)
|
||||||
|
|
||||||
|
if (this@MainActivity.mode == Mode.DOWNLOAD) {
|
||||||
|
runOnUiThread {
|
||||||
|
cancelFetch()
|
||||||
|
clearGalleries()
|
||||||
|
fetchGalleries(query, sortMode)
|
||||||
|
loadBlocks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
histories.remove(galleryID)
|
||||||
|
|
||||||
|
if (this@MainActivity.mode == Mode.HISTORY) {
|
||||||
|
runOnUiThread {
|
||||||
|
cancelFetch()
|
||||||
|
clearGalleries()
|
||||||
|
fetchGalleries(query, sortMode)
|
||||||
|
loadBlocks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completeFlag.put(galleryID, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeAllItems()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ItemClickSupport.addTo(this)
|
ItemClickSupport.addTo(this)
|
||||||
.setOnItemClickListener { _, position, v ->
|
.setOnItemClickListener { _, position, v ->
|
||||||
|
|
||||||
if (v !is CardView)
|
if (v !is CardView)
|
||||||
return@setOnItemClickListener
|
return@setOnItemClickListener
|
||||||
|
|
||||||
@@ -506,66 +606,27 @@ class MainActivity : AppCompatActivity() {
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
|
||||||
histories.add(gallery.id)
|
histories.add(gallery.id)
|
||||||
}.setOnItemLongClickListener { recyclerView, position, v ->
|
}.setOnItemLongClickListener { _, position, v ->
|
||||||
|
|
||||||
if (v !is CardView)
|
if (v !is CardView)
|
||||||
return@setOnItemLongClickListener true
|
return@setOnItemLongClickListener true
|
||||||
|
|
||||||
val gallery = galleries[position].first
|
val galleryID = galleries[position].first.id
|
||||||
val view = LayoutInflater.from(this@MainActivity)
|
|
||||||
.inflate(R.layout.dialog_galleryblock, recyclerView, false)
|
|
||||||
|
|
||||||
val dialog = AlertDialog.Builder(this@MainActivity).apply {
|
GalleryDialog(this@MainActivity, galleryID).apply {
|
||||||
setView(view)
|
onChipClickedHandler.add {
|
||||||
}.create()
|
|
||||||
|
|
||||||
with(view.main_dialog_download) {
|
|
||||||
text = when(GalleryDownloader.get(gallery.id)) {
|
|
||||||
null -> getString(R.string.reader_fab_download)
|
|
||||||
else -> getString(R.string.reader_fab_download_cancel)
|
|
||||||
}
|
|
||||||
isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(gallery.id, false)
|
|
||||||
setOnClickListener {
|
|
||||||
val downloader = GalleryDownloader.get(gallery.id)
|
|
||||||
if (downloader == null)
|
|
||||||
GalleryDownloader(context, gallery.id, true).start()
|
|
||||||
else {
|
|
||||||
downloader.cancel()
|
|
||||||
downloader.clearNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
view.main_dialog_delete.setOnClickListener {
|
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
|
||||||
with(GalleryDownloader[gallery.id]) {
|
|
||||||
this?.cancelAndJoin()
|
|
||||||
this?.clearNotification()
|
|
||||||
}
|
|
||||||
val cache = File(cacheDir, "imageCache/${gallery.id}")
|
|
||||||
val data = getCachedGallery(context, gallery.id)
|
|
||||||
cache.deleteRecursively()
|
|
||||||
data.deleteRecursively()
|
|
||||||
|
|
||||||
downloads.remove(gallery.id)
|
|
||||||
|
|
||||||
if (mode == Mode.DOWNLOAD) {
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
|
query = it.toQuery()
|
||||||
|
currentPage = 0
|
||||||
|
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
fetchGalleries(query, sortMode)
|
fetchGalleries(query, sortMode)
|
||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
|
dismiss()
|
||||||
}
|
}
|
||||||
|
}.show()
|
||||||
(adapter as GalleryBlockAdapter).completeFlag.put(gallery.id, false)
|
|
||||||
}
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -721,6 +782,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val text = next.findViewById<TextView>(R.id.text_next).apply {
|
val text = next.findViewById<TextView>(R.id.text_next).apply {
|
||||||
text = getString(R.string.main_move, currentPage+2)
|
text = getString(R.string.main_move, currentPage+2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absDist < 360) {
|
if (absDist < 360) {
|
||||||
next.layoutParams.height = (absDist/2).roundToInt()
|
next.layoutParams.height = (absDist/2).roundToInt()
|
||||||
icon.layoutParams.height = (absDist/2).roundToInt()
|
icon.layoutParams.height = (absDist/2).roundToInt()
|
||||||
@@ -728,8 +790,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
text.layoutParams.width = absDist.roundToInt()
|
text.layoutParams.width = absDist.roundToInt()
|
||||||
|
|
||||||
target = -1
|
target = -1
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
next.layoutParams.height = 180
|
next.layoutParams.height = 180
|
||||||
icon.layoutParams.height = 180
|
icon.layoutParams.height = 180
|
||||||
icon.rotation = 0f
|
icon.rotation = 0f
|
||||||
@@ -792,7 +853,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
s ?: return
|
s ?: return
|
||||||
|
|
||||||
if (s.any { it.isUpperCase() })
|
if (s.any { it.isUpperCase() })
|
||||||
s.replace(0, s.length, s.toString().toLowerCase())
|
s.replace(0, s.length, s.toString().toLowerCase(Locale.getDefault()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -900,11 +961,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
else
|
else
|
||||||
setImageResource(R.drawable.ic_star_empty)
|
setImageResource(R.drawable.ic_star_empty)
|
||||||
|
|
||||||
|
visibility = View.VISIBLE
|
||||||
rotation = 0f
|
rotation = 0f
|
||||||
isEnabled = true
|
isEnabled = true
|
||||||
|
|
||||||
setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500))
|
|
||||||
|
|
||||||
isClickable = true
|
isClickable = true
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
val favorites = Tags(json.parse(serializer, favoritesFile.readText()))
|
val favorites = Tags(json.parse(serializer, favoritesFile.readText()))
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ import com.andrognito.patternlockview.utils.PatternLockUtils
|
|||||||
import kotlinx.android.synthetic.main.fragment_pattern_lock.*
|
import kotlinx.android.synthetic.main.fragment_pattern_lock.*
|
||||||
import kotlinx.android.synthetic.main.fragment_pattern_lock.view.*
|
import kotlinx.android.synthetic.main.fragment_pattern_lock.view.*
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.util.hash
|
|
||||||
import xyz.quaver.pupil.util.hashWithSalt
|
|
||||||
|
|
||||||
class PatternLockFragment : Fragment(), PatternLockViewListener {
|
class PatternLockFragment : Fragment(), PatternLockViewListener {
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,11 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.ui
|
package xyz.quaver.pupil.ui
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
@@ -31,6 +33,7 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.activity_reader.*
|
import kotlinx.android.synthetic.main.activity_reader.*
|
||||||
@@ -46,6 +49,7 @@ import xyz.quaver.pupil.adapters.ReaderAdapter
|
|||||||
import xyz.quaver.pupil.util.GalleryDownloader
|
import xyz.quaver.pupil.util.GalleryDownloader
|
||||||
import xyz.quaver.pupil.util.Histories
|
import xyz.quaver.pupil.util.Histories
|
||||||
import xyz.quaver.pupil.util.ItemClickSupport
|
import xyz.quaver.pupil.util.ItemClickSupport
|
||||||
|
import xyz.quaver.pupil.util.hasPermission
|
||||||
|
|
||||||
class ReaderActivity : AppCompatActivity() {
|
class ReaderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -217,6 +221,23 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
|
//currentPage is 1-based
|
||||||
|
return when(keyCode) {
|
||||||
|
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||||
|
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(currentPage-2, 0)
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||||
|
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(currentPage, 0)
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> super.onKeyDown(keyCode, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initDownloader() {
|
private fun initDownloader() {
|
||||||
var d: GalleryDownloader? = GalleryDownloader.get(galleryID)
|
var d: GalleryDownloader? = GalleryDownloader.get(galleryID)
|
||||||
|
|
||||||
@@ -228,16 +249,16 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
title = it.title
|
title = it.title
|
||||||
with(reader_download_progressbar) {
|
with(reader_download_progressbar) {
|
||||||
max = it.readerItems.size
|
max = it.galleryInfo.size
|
||||||
progress = 0
|
progress = 0
|
||||||
}
|
}
|
||||||
with(reader_progressbar) {
|
with(reader_progressbar) {
|
||||||
max = it.readerItems.size
|
max = it.galleryInfo.size
|
||||||
progress = 0
|
progress = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
gallerySize = it.readerItems.size
|
gallerySize = it.galleryInfo.size
|
||||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.readerItems.size}"
|
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.galleryInfo.size}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onProgressHandler = {
|
onProgressHandler = {
|
||||||
@@ -259,7 +280,12 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onErrorHandler = {
|
onErrorHandler = {
|
||||||
Snackbar.make(reader_layout, it.message ?: it.javaClass.name, Snackbar.LENGTH_INDEFINITE).show()
|
Snackbar
|
||||||
|
.make(reader_layout, it.message ?: it.javaClass.name, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
.setAction(R.string.reader_help) {
|
||||||
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.error_help))))
|
||||||
|
}
|
||||||
|
.show()
|
||||||
downloader.download = false
|
downloader.download = false
|
||||||
}
|
}
|
||||||
onCompleteHandler = {
|
onCompleteHandler = {
|
||||||
@@ -307,7 +333,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
with(reader_recyclerview) {
|
with(reader_recyclerview) {
|
||||||
adapter = ReaderAdapter(images)
|
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID, images)
|
||||||
|
|
||||||
addOnScrollListener(object: RecyclerView.OnScrollListener() {
|
addOnScrollListener(object: RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
@@ -337,7 +363,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
scrollMode(false)
|
scrollMode(false)
|
||||||
fullscreen(true)
|
fullscreen(true)
|
||||||
} else {
|
} else {
|
||||||
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPosition(currentPage)
|
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPosition(currentPage) //Moves to next page because currentPage is 1-based indexing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,6 +371,17 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
with(reader_fab_download) {
|
with(reader_fab_download) {
|
||||||
setImageResource(R.drawable.ic_download)
|
setImageResource(R.drawable.ic_download)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
|
||||||
|
if (!this@ReaderActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
AlertDialog.Builder(this@ReaderActivity).apply {
|
||||||
|
setTitle(R.string.warning)
|
||||||
|
setMessage(R.string.update_no_permission)
|
||||||
|
setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||||
|
}.show()
|
||||||
|
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
downloader.download = !downloader.download
|
downloader.download = !downloader.download
|
||||||
|
|
||||||
if (!downloader.download)
|
if (!downloader.download)
|
||||||
|
|||||||
@@ -31,10 +31,16 @@ import android.widget.LinearLayout
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.dialog_default_query.view.*
|
import kotlinx.android.synthetic.main.dialog_default_query.view.*
|
||||||
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.parseList
|
||||||
import xyz.quaver.pupil.Pupil
|
import xyz.quaver.pupil.Pupil
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.types.Tags
|
import xyz.quaver.pupil.types.Tags
|
||||||
@@ -42,10 +48,13 @@ import xyz.quaver.pupil.util.Lock
|
|||||||
import xyz.quaver.pupil.util.LockManager
|
import xyz.quaver.pupil.util.LockManager
|
||||||
import xyz.quaver.pupil.util.getDownloadDirectory
|
import xyz.quaver.pupil.util.getDownloadDirectory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class SettingsActivity : AppCompatActivity() {
|
class SettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
val REQUEST_LOCK = 38238
|
val REQUEST_LOCK = 38238
|
||||||
|
val REQUEST_RESTORE = 16546
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -153,7 +162,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
with(findPreference<Preference>("delete_downloads")) {
|
with(findPreference<Preference>("delete_downloads")) {
|
||||||
this!!
|
this!!
|
||||||
|
|
||||||
val dir = getDownloadDirectory(context)!!
|
val dir = getDownloadDirectory(context)
|
||||||
|
|
||||||
summary = getDirSize(dir)
|
summary = getDirSize(dir)
|
||||||
|
|
||||||
@@ -277,7 +286,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
s ?: return
|
s ?: return
|
||||||
|
|
||||||
if (s.any { it.isUpperCase() })
|
if (s.any { it.isUpperCase() })
|
||||||
s.replace(0, s.length, s.toString().toLowerCase())
|
s.replace(0, s.length, s.toString().toLowerCase(Locale.getDefault()))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -338,6 +347,50 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
with(findPreference<Preference>("dark_mode")) {
|
||||||
|
this!!
|
||||||
|
|
||||||
|
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
|
AppCompatDelegate.setDefaultNightMode(when (newValue as Boolean) {
|
||||||
|
true -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
|
false -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
|
})
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with(findPreference<Preference>("backup")) {
|
||||||
|
this!!
|
||||||
|
|
||||||
|
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
File(ContextCompat.getDataDir(context), "favorites.json").copyTo(
|
||||||
|
File(getDownloadDirectory(context), "favorites.json"),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
Snackbar.make(this@SettingsFragment.listView, R.string.settings_backup_snackbar, Snackbar.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with(findPreference<Preference>("restore")) {
|
||||||
|
this!!
|
||||||
|
|
||||||
|
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "*/*"
|
||||||
|
}
|
||||||
|
|
||||||
|
activity?.startActivityForResult(intent, (activity as SettingsActivity).REQUEST_RESTORE)
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,6 +454,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
when(requestCode) {
|
when(requestCode) {
|
||||||
REQUEST_LOCK -> {
|
REQUEST_LOCK -> {
|
||||||
@@ -412,6 +466,33 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
.commitAllowingStateLoss()
|
.commitAllowingStateLoss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
REQUEST_RESTORE -> {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
val uri = data?.data ?: return
|
||||||
|
|
||||||
|
try {
|
||||||
|
val json = contentResolver.openInputStream(uri).use { inputStream ->
|
||||||
|
inputStream!!
|
||||||
|
|
||||||
|
inputStream.readBytes().toString(Charset.defaultCharset())
|
||||||
|
}
|
||||||
|
|
||||||
|
(application as Pupil).favorites.addAll(Json.parseList<Int>(json).also {
|
||||||
|
Snackbar.make(
|
||||||
|
window.decorView,
|
||||||
|
getString(R.string.settings_restore_successful, it.size),
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
})
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Snackbar.make(
|
||||||
|
window.decorView,
|
||||||
|
R.string.settings_restore_failed,
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> super.onActivityResult(requestCode, resultCode, data)
|
else -> super.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,14 +27,16 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.app.TaskStackBuilder
|
import androidx.core.app.TaskStackBuilder
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.crashlytics.android.Crashlytics
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.io.IOException
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonConfiguration
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
import xyz.quaver.hitomi.Reader
|
import xyz.quaver.hitomi.Reader
|
||||||
import xyz.quaver.hitomi.getReader
|
import xyz.quaver.hitomi.getReader
|
||||||
import xyz.quaver.hitomi.getReferer
|
import xyz.quaver.hitomi.getReferer
|
||||||
|
import xyz.quaver.hitomi.urlFromUrlFromHash
|
||||||
import xyz.quaver.hiyobi.cookie
|
import xyz.quaver.hiyobi.cookie
|
||||||
|
import xyz.quaver.hiyobi.createImgList
|
||||||
import xyz.quaver.hiyobi.user_agent
|
import xyz.quaver.hiyobi.user_agent
|
||||||
import xyz.quaver.pupil.Pupil
|
import xyz.quaver.pupil.Pupil
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
@@ -62,7 +64,8 @@ class GalleryDownloader(
|
|||||||
field = true
|
field = true
|
||||||
notificationManager.notify(galleryID, notificationBuilder.build())
|
notificationManager.notify(galleryID, notificationBuilder.build())
|
||||||
|
|
||||||
val data = getCachedGallery(this, galleryID)
|
if (reader?.isActive == false && downloadJob?.isActive != true) {
|
||||||
|
val data = File(getDownloadDirectory(this), galleryID.toString())
|
||||||
val cache = File(cacheDir, "imageCache/$galleryID")
|
val cache = File(cacheDir, "imageCache/$galleryID")
|
||||||
|
|
||||||
if (File(cache, "images").exists() && !data.exists()) {
|
if (File(cache, "images").exists() && !data.exists()) {
|
||||||
@@ -70,8 +73,8 @@ class GalleryDownloader(
|
|||||||
cache.deleteRecursively()
|
cache.deleteRecursively()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader?.isActive == false && downloadJob?.isActive != true)
|
|
||||||
field = false
|
field = false
|
||||||
|
}
|
||||||
|
|
||||||
downloads.add(galleryID)
|
downloads.add(galleryID)
|
||||||
} else {
|
} else {
|
||||||
@@ -81,7 +84,7 @@ class GalleryDownloader(
|
|||||||
onNotifyChangedHandler?.invoke(value)
|
onNotifyChangedHandler?.invoke(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val reader: Deferred<Reader>?
|
private val reader: Deferred<Reader?>?
|
||||||
private var downloadJob: Job? = null
|
private var downloadJob: Job? = null
|
||||||
|
|
||||||
private lateinit var notificationBuilder: NotificationCompat.Builder
|
private lateinit var notificationBuilder: NotificationCompat.Builder
|
||||||
@@ -110,14 +113,17 @@ class GalleryDownloader(
|
|||||||
//Check cache
|
//Check cache
|
||||||
val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "reader.json")
|
val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "reader.json")
|
||||||
|
|
||||||
|
try {
|
||||||
|
json.parse(serializer, cache.readText())
|
||||||
|
} catch(e: Exception) {
|
||||||
|
cache.delete()
|
||||||
|
}
|
||||||
|
|
||||||
if (cache.exists()) {
|
if (cache.exists()) {
|
||||||
val cached = json.parse(serializer, cache.readText())
|
val cached = json.parse(serializer, cache.readText())
|
||||||
|
|
||||||
if (cached.readerItems.isNotEmpty()) {
|
if (cached.galleryInfo.isNotEmpty()) {
|
||||||
useHiyobi = when {
|
useHiyobi = cached.code == Reader.Code.HIYOBI
|
||||||
cached.readerItems[0].url.contains("hitomi.la") -> false
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
|
|
||||||
onReaderLoadedHandler?.invoke(cached)
|
onReaderLoadedHandler?.invoke(cached)
|
||||||
|
|
||||||
@@ -128,22 +134,19 @@ class GalleryDownloader(
|
|||||||
//Cache doesn't exist. Load from internet
|
//Cache doesn't exist. Load from internet
|
||||||
val reader = when {
|
val reader = when {
|
||||||
useHiyobi -> {
|
useHiyobi -> {
|
||||||
xyz.quaver.hiyobi.getReader(galleryID).let {
|
try {
|
||||||
when {
|
xyz.quaver.hiyobi.getReader(galleryID)
|
||||||
it.readerItems.isEmpty() -> {
|
} catch(e: Exception) {
|
||||||
useHiyobi = false
|
useHiyobi = false
|
||||||
getReader(galleryID)
|
getReader(galleryID)
|
||||||
}
|
}
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
getReader(galleryID)
|
getReader(galleryID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.readerItems.isNotEmpty()) {
|
if (reader.galleryInfo.isNotEmpty()) {
|
||||||
//Save cache
|
//Save cache
|
||||||
if (cache.parentFile?.exists() == false)
|
if (cache.parentFile?.exists() == false)
|
||||||
cache.parentFile!!.mkdirs()
|
cache.parentFile!!.mkdirs()
|
||||||
@@ -153,7 +156,9 @@ class GalleryDownloader(
|
|||||||
|
|
||||||
reader
|
reader
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Reader("", listOf())
|
Crashlytics.logException(e)
|
||||||
|
onErrorHandler?.invoke(e)
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,27 +167,32 @@ class GalleryDownloader(
|
|||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
downloadJob = CoroutineScope(Dispatchers.Default).launch {
|
downloadJob = CoroutineScope(Dispatchers.Default).launch {
|
||||||
val reader = reader!!.await()
|
val reader = reader!!.await() ?: return@launch
|
||||||
|
|
||||||
if (reader.readerItems.isEmpty()) {
|
notificationBuilder.setContentTitle(reader.title)
|
||||||
onErrorHandler?.invoke(IOException(getString(R.string.unable_to_connect)))
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val list = ArrayList<String>()
|
val list = ArrayList<String>()
|
||||||
|
|
||||||
onReaderLoadedHandler?.invoke(reader)
|
onReaderLoadedHandler?.invoke(reader)
|
||||||
|
|
||||||
notificationBuilder
|
notificationBuilder
|
||||||
.setProgress(reader.readerItems.size, 0, false)
|
.setProgress(reader.galleryInfo.size, 0, false)
|
||||||
.setContentText("0/${reader.readerItems.size}")
|
.setContentText("0/${reader.galleryInfo.size}")
|
||||||
|
|
||||||
reader.readerItems.chunked(4).forEachIndexed { chunkIndex, chunked ->
|
reader.galleryInfo.chunked(4).forEachIndexed { chunkIndex, chunked ->
|
||||||
chunked.mapIndexed { i, it ->
|
chunked.mapIndexed { i, galleryInfo ->
|
||||||
val index = chunkIndex*4+i
|
val index = chunkIndex*4+i
|
||||||
|
|
||||||
async(Dispatchers.IO) {
|
async(Dispatchers.IO) {
|
||||||
val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url
|
val url = when(useHiyobi) {
|
||||||
|
true -> createImgList(galleryID, reader)[index].path
|
||||||
|
false -> when {
|
||||||
|
(!galleryInfo.hash.isNullOrBlank()) and (galleryInfo.haswebp == 1) ->
|
||||||
|
urlFromUrlFromHash(galleryID, galleryInfo, "webp")
|
||||||
|
else ->
|
||||||
|
urlFromUrlFromHash(galleryID, galleryInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val name = "$index".padStart(4, '0')
|
val name = "$index".padStart(4, '0')
|
||||||
val ext = url.split('.').last()
|
val ext = url.split('.').last()
|
||||||
@@ -216,7 +226,7 @@ class GalleryDownloader(
|
|||||||
notificationManager.notify(galleryID, notificationBuilder.build())
|
notificationManager.notify(galleryID, notificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.absolutePath
|
"images/$name.$ext"
|
||||||
}
|
}
|
||||||
}.forEach {
|
}.forEach {
|
||||||
list.add(it.await())
|
list.add(it.await())
|
||||||
@@ -226,8 +236,8 @@ class GalleryDownloader(
|
|||||||
onProgressHandler?.invoke(index)
|
onProgressHandler?.invoke(index)
|
||||||
|
|
||||||
notificationBuilder
|
notificationBuilder
|
||||||
.setProgress(reader.readerItems.size, index, false)
|
.setProgress(reader.galleryInfo.size, index, false)
|
||||||
.setContentText("$index/${reader.readerItems.size}")
|
.setContentText("$index/${reader.galleryInfo.size}")
|
||||||
|
|
||||||
if (download)
|
if (download)
|
||||||
notificationManager.notify(galleryID, notificationBuilder.build())
|
notificationManager.notify(galleryID, notificationBuilder.build())
|
||||||
@@ -307,16 +317,11 @@ class GalleryDownloader(
|
|||||||
notificationBuilder = NotificationCompat.Builder(this, "download").apply {
|
notificationBuilder = NotificationCompat.Builder(this, "download").apply {
|
||||||
setContentTitle(getString(R.string.reader_loading))
|
setContentTitle(getString(R.string.reader_loading))
|
||||||
setContentText(getString(R.string.reader_notification_text))
|
setContentText(getString(R.string.reader_notification_text))
|
||||||
setSmallIcon(R.drawable.ic_download)
|
setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
setContentIntent(pendingIntent)
|
setContentIntent(pendingIntent)
|
||||||
setProgress(0, 0, true)
|
setProgress(0, 0, true)
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
priority = NotificationCompat.PRIORITY_LOW
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
|
||||||
while (reader == null) ;
|
|
||||||
notificationBuilder.setContentTitle(reader.await().title)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,6 @@ package xyz.quaver.pupil.util
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.MediaStore
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
fun getCachedGallery(context: Context, galleryID: Int): File {
|
fun getCachedGallery(context: Context, galleryID: Int): File {
|
||||||
@@ -35,9 +33,9 @@ fun getCachedGallery(context: Context, galleryID: Int): File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun getDownloadDirectory(context: Context): File? {
|
fun getDownloadDirectory(context: Context): File {
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
context.getExternalFilesDir("Pupil")
|
context.getExternalFilesDir("Pupil")!!
|
||||||
else
|
else
|
||||||
File(Environment.getExternalStorageDirectory(), "Pupil")
|
File(Environment.getExternalStorageDirectory(), "Pupil")
|
||||||
}
|
}
|
||||||
@@ -112,10 +112,12 @@ class LockManager(base: Context): ContextWrapper(base) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun empty(): Boolean {
|
fun isEmpty(): Boolean {
|
||||||
return locks.isNullOrEmpty()
|
return locks.isNullOrEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isNotEmpty(): Boolean = !isEmpty()
|
||||||
|
|
||||||
fun contains(type: Lock.Type): Boolean {
|
fun contains(type: Lock.Type): Boolean {
|
||||||
return locks?.any { it.type == type } ?: false
|
return locks?.any { it.type == type } ?: false
|
||||||
}
|
}
|
||||||
|
|||||||
35
app/src/main/java/xyz/quaver/pupil/util/misc.kt
Normal file
35
app/src/main/java/xyz/quaver/pupil/util/misc.kt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Pupil, Hitomi.la viewer for Android
|
||||||
|
* Copyright (C) 2019 tom5079
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package xyz.quaver.pupil.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
|
fun Context.hasPermission(permission: String) =
|
||||||
|
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||||
|
|
||||||
|
fun String.wordCapitalize() : String {
|
||||||
|
val result = ArrayList<String>()
|
||||||
|
|
||||||
|
for (word in this.split(" "))
|
||||||
|
result.add(word.capitalize())
|
||||||
|
|
||||||
|
return result.joinToString(" ")
|
||||||
|
}
|
||||||
@@ -18,8 +18,14 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.util
|
package xyz.quaver.pupil.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
|
import kotlinx.serialization.internal.EnumSerializer
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
|
import xyz.quaver.availableInHiyobi
|
||||||
|
import xyz.quaver.hitomi.Reader
|
||||||
import xyz.quaver.pupil.BuildConfig
|
import xyz.quaver.pupil.BuildConfig
|
||||||
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
fun getReleases(url: String) : JsonArray {
|
fun getReleases(url: String) : JsonArray {
|
||||||
@@ -40,19 +46,74 @@ fun checkUpdate(url: String) : JsonObject? {
|
|||||||
|
|
||||||
return releases.firstOrNull {
|
return releases.firstOrNull {
|
||||||
if (BuildConfig.PRERELEASE) {
|
if (BuildConfig.PRERELEASE) {
|
||||||
BuildConfig.VERSION_NAME != it.jsonObject["tag_name"]?.content
|
true
|
||||||
} else {
|
} else {
|
||||||
it.jsonObject["prerelease"]?.boolean == false &&
|
it.jsonObject["prerelease"]?.boolean == false
|
||||||
BuildConfig.VERSION_NAME != (it.jsonObject["tag_name"]?.content ?: "")
|
}
|
||||||
|
}?.let {
|
||||||
|
if (it.jsonObject["tag_name"]?.content == BuildConfig.VERSION_NAME)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
it.jsonObject
|
||||||
}
|
}
|
||||||
}?.jsonObject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getApkUrl(releases: JsonObject) : Pair<String?, String?>? {
|
fun getApkUrl(releases: JsonObject) : Pair<String?, String?>? {
|
||||||
releases["assets"]?.jsonArray?.forEach {
|
return releases["assets"]?.jsonArray?.firstOrNull {
|
||||||
if (Regex("Pupil-v(\\d+\\.)+\\d+\\.apk").matches(it.jsonObject["name"]?.content ?: ""))
|
Regex("Pupil-v.+\\.apk").matches(it.jsonObject["name"]?.content ?: "")
|
||||||
return Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content)
|
}.let {
|
||||||
|
if (it == null)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOldReaderGalleries(context: Context) : List<File> {
|
||||||
|
val oldGallery = mutableListOf<File>()
|
||||||
|
|
||||||
|
listOf(
|
||||||
|
getDownloadDirectory(context),
|
||||||
|
File(context.cacheDir, "imageCache")
|
||||||
|
).forEach { root ->
|
||||||
|
root.listFiles()?.forEach { gallery ->
|
||||||
|
File(gallery, "reader.json").let { readerFile ->
|
||||||
|
if (!readerFile.exists())
|
||||||
|
return@let
|
||||||
|
|
||||||
|
Json(JsonConfiguration.Stable).parseJson(readerFile.readText()).jsonObject.let { reader ->
|
||||||
|
if (!reader.contains("code"))
|
||||||
|
oldGallery.add(gallery)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldGallery
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseExperimental(InternalSerializationApi::class)
|
||||||
|
fun updateOldReaderGalleries(context: Context) {
|
||||||
|
|
||||||
|
val json = Json(JsonConfiguration.Stable)
|
||||||
|
|
||||||
|
getOldReaderGalleries(context).forEach { gallery ->
|
||||||
|
val reader = json.parseJson(File(gallery, "reader.json").apply {
|
||||||
|
if (!exists())
|
||||||
|
return@forEach
|
||||||
|
}.readText())
|
||||||
|
.jsonObject.toMutableMap()
|
||||||
|
|
||||||
|
val codeSerializer = EnumSerializer(Reader.Code::class)
|
||||||
|
|
||||||
|
reader["code"] = when {
|
||||||
|
(File(gallery, "images").list()?.
|
||||||
|
all { !it.endsWith("webp") } ?: return@forEach) &&
|
||||||
|
availableInHiyobi(gallery.name.toInt()) -> json.toJson(codeSerializer, Reader.Code.HIYOBI)
|
||||||
|
else -> json.toJson(codeSerializer, Reader.Code.HITOMI)
|
||||||
|
}
|
||||||
|
|
||||||
|
File(gallery, "reader.json").writeText(JsonObject(reader).toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#FFFFFF">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M7,14L5,14v5h5v-2L7,17v-3zM5,10h2L7,7h3L10,5L5,5v5zM17,17h-3v2h5v-5h-2v3zM14,5v2h3v3h2L19,5h-5z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
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>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M21,5v6.59l-3,-3.01 -4,4.01 -4,-4 -4,4 -3,-3.01L3,5c0,-1.1 0.9,-2 2,-2h14c1.1,0 2,0.9 2,2zM18,11.42l3,3.01L21,19c0,1.1 -0.9,2 -2,2L5,21c-1.1,0 -2,-0.9 -2,-2v-6.58l3,2.99 4,-4 4,4 4,-3.99z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#FFFFFF">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#FFFFFF">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
|
|
||||||
</vector>
|
|
||||||
8
app/src/main/res/drawable/arrow_right.xml
Normal file
8
app/src/main/res/drawable/arrow_right.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<!-- drawable/arrow_right.xml -->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#fff" android:pathData="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" />
|
||||||
|
</vector>
|
||||||
@@ -13,14 +13,14 @@
|
|||||||
<path
|
<path
|
||||||
android:name="path"
|
android:name="path"
|
||||||
android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z"
|
android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z"
|
||||||
android:fillColor="#000"/>
|
android:fillColor="@color/material_orange_500"/>
|
||||||
<clip-path
|
<clip-path
|
||||||
android:name="clip"
|
android:name="clip"
|
||||||
android:pathData="M 2 21 L 2 21 L 22 21 L 22 21 Z"/>
|
android:pathData="M 2 21 L 2 21 L 22 21 L 22 21 Z"/>
|
||||||
<path
|
<path
|
||||||
android:name="path_1"
|
android:name="path_1"
|
||||||
android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z"
|
android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z"
|
||||||
android:fillColor="#000"/>
|
android:fillColor="@color/material_orange_500"/>
|
||||||
</vector>
|
</vector>
|
||||||
</aapt:attr>
|
</aapt:attr>
|
||||||
<target android:name="clip">
|
<target android:name="clip">
|
||||||
|
|||||||
@@ -1,3 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<animated-vector
|
<animated-vector
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<vector android:height="24dp" android:width="24dp"
|
<vector android:height="24dp" android:width="24dp"
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<path android:fillColor="#000" android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z"/>
|
<path android:fillColor="@color/material_orange_500" android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<vector android:height="24dp" android:width="24dp"
|
<vector android:height="24dp" android:width="24dp"
|
||||||
android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<path android:fillColor="#000" android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z"/>
|
<path android:fillColor="@color/material_orange_500" android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z"/>
|
||||||
</vector>
|
</vector>
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@@ -11,7 +29,29 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@id/lock_button_layout"/>
|
app:layout_constraintBottom_toTopOf="@id/lock_fingerprint_layout"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/lock_fingerprint_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/lock_content"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/lock_button_layout">
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/lock_fingerprint"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:srcCompat="@drawable/fingerprint"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
app:backgroundTint="@color/dark_gray"
|
||||||
|
app:fabSize="mini"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/lock_button_layout"
|
android:id="@+id/lock_button_layout"
|
||||||
@@ -19,7 +59,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginBottom="32dp"
|
android:layout_marginBottom="32dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/lock_content"
|
app:layout_constraintTop_toBottomOf="@id/lock_fingerprint_layout"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:gravity="center">
|
android:gravity="center">
|
||||||
|
|
||||||
@@ -41,16 +81,6 @@
|
|||||||
app:backgroundTint="@color/dark_gray"
|
app:backgroundTint="@color/dark_gray"
|
||||||
app:fabSize="mini"/>
|
app:fabSize="mini"/>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/lock_fingerprint"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:srcCompat="@drawable/fingerprint"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
app:backgroundTint="@color/dark_gray"
|
|
||||||
app:fabSize="mini"/>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/lock_password"
|
android:id="@+id/lock_password"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<androidx.drawerlayout.widget.DrawerLayout
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/main_layout"
|
android:id="@+id/main_layout"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
@@ -86,6 +104,7 @@
|
|||||||
android:id="@+id/main_searchview"
|
android:id="@+id/main_searchview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
app:floatingSearch_backgroundColor="?attr/colorSurface"
|
||||||
app:floatingSearch_searchBarMarginLeft="8dp"
|
app:floatingSearch_searchBarMarginLeft="8dp"
|
||||||
app:floatingSearch_searchBarMarginRight="8dp"
|
app:floatingSearch_searchBarMarginRight="8dp"
|
||||||
app:floatingSearch_searchBarMarginTop="8dp"
|
app:floatingSearch_searchBarMarginTop="8dp"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/reader_layout"
|
android:id="@+id/reader_layout"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -1,23 +1,132 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/gallery_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:padding="16dp">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<Button
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/main_dialog_download"
|
android:id="@+id/gallery_toolbar"
|
||||||
style="?borderlessButtonStyle"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||||
|
|
||||||
<Button
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/main_dialog_delete"
|
|
||||||
style="?borderlessButtonStyle"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/main_dialog_delete"
|
android:padding="8dp">
|
||||||
app:layout_constraintTop_toBottomOf="@id/main_dialog_download"/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<ImageView
|
||||||
|
android:id="@+id/gallery_cover"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/gallery_title"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gallery_title"
|
||||||
|
style="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/gallery_cover"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginStart="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:id="@+id/gallery_artist"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/gallery_title"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/gallery_cover"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginStart="8dp"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/gallery_padding"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/gallery_artist"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/gallery_type"/>
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/gallery_type"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/gallery_cover"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginStart="8dp"/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/gallery_contents"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"/>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/gallery_progressbar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/gallery_fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
app:layout_anchor="@id/gallery_toolbar"
|
||||||
|
app:layout_anchorGravity="bottom|end"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
40
app/src/main/res/layout/gallery_details.xml
Normal file
40
app/src/main/res/layout/gallery_details.xml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gallery_details"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
|
android:textColor="@color/colorAccent"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/gallery_details_contents"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
40
app/src/main/res/layout/item_gallery_details.xml
Normal file
40
app/src/main/res/layout/item_gallery_details.xml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||||
|
android:id="@+id/gallery_details_type"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="8dp"/>
|
||||||
|
|
||||||
|
<com.google.android.material.chip.ChipGroup
|
||||||
|
android:id="@+id/gallery_details_tags"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:chipSpacingVertical="8dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
23
app/src/main/res/layout/item_gallery_thumbnails.xml
Normal file
23
app/src/main/res/layout/item_gallery_thumbnails.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="3"/>
|
||||||
@@ -1,24 +1,82 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
android:paddingStart="0dp"
|
|
||||||
android:paddingLeft="0dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
app:cardCornerRadius="8dp"
|
app:cardCornerRadius="8dp"
|
||||||
|
android:clipChildren="true">
|
||||||
|
|
||||||
|
<com.daimajia.swipe.SwipeLayout
|
||||||
|
android:id="@+id/galleryblock_swipe_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:drag_edge="right"
|
||||||
|
app:show_mode="pull_out">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/galleryblock_secondary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/galleryblock_download"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:minWidth="70dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@android:color/holo_blue_dark"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:text="@string/main_download"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/galleryblock_delete"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:minWidth="70dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@android:color/holo_red_dark"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:text="@string/main_delete"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/galleryblock_primary"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
android:foreground="?attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:clickable="true">
|
android:clickable="true">
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
@@ -69,8 +127,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
|
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/galleryblock_title"/>
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_title"/>
|
||||||
@@ -81,8 +137,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/galleryblock_artist"
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_artist"
|
||||||
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail"
|
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail"
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
@@ -93,8 +147,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/galleryblock_series"
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_series"
|
||||||
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail" />
|
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail" />
|
||||||
|
|
||||||
@@ -105,8 +157,6 @@
|
|||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/galleryblock_type"
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_type"
|
||||||
app:layout_constraintBottom_toTopOf="@id/galleryblock_padding"
|
app:layout_constraintBottom_toTopOf="@id/galleryblock_padding"
|
||||||
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail" />
|
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail" />
|
||||||
@@ -127,6 +177,7 @@
|
|||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
app:chipSpacing="2dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/galleryblock_padding"
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_padding"
|
||||||
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
|
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
@@ -163,11 +214,12 @@
|
|||||||
android:contentDescription="@string/app_name"
|
android:contentDescription="@string/app_name"
|
||||||
android:layout_width="32dp"
|
android:layout_width="32dp"
|
||||||
android:layout_height="32dp"
|
android:layout_height="32dp"
|
||||||
app:srcCompat="@drawable/ic_star_empty"
|
app:srcCompat="@drawable/ic_star_empty"/>
|
||||||
app:backgroundTint="@color/material_orange_500"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.daimajia.swipe.SwipeLayout>
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
|
||||||
android:layout_width="match_parent" android:layout_height="wrap_content">
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:contentDescription="@string/reader_imageview_description"
|
android:contentDescription="@string/reader_imageview_description"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<com.google.android.material.chip.Chip
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
app:chipIconSize="16dp"
|
|
||||||
app:chipStartPadding="8dp"
|
|
||||||
app:chipCornerRadius="100dp"/>
|
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<group android:checkableBehavior="single">
|
<group android:checkableBehavior="single">
|
||||||
@@ -35,7 +53,7 @@
|
|||||||
android:title="@string/main_drawer_group_contact_email"
|
android:title="@string/main_drawer_group_contact_email"
|
||||||
android:icon="@drawable/ic_email"/>
|
android:icon="@drawable/ic_email"/>
|
||||||
<item android:id="@+id/main_drawer_kakaotalk"
|
<item android:id="@+id/main_drawer_kakaotalk"
|
||||||
android:title="@string/main_drawer_grouop_contact_kakaotalk"
|
android:title="@string/main_drawer_grouop_contact_discord"
|
||||||
android:icon="@drawable/ic_message"/>
|
android:icon="@drawable/ic_message"/>
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
35
app/src/main/res/menu/gallery.xml
Normal file
35
app/src/main/res/menu/gallery.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/gallery_favorite"
|
||||||
|
android:icon="@drawable/ic_star_empty"
|
||||||
|
android:title=""
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/gallery_download"
|
||||||
|
android:icon="@drawable/ic_download"
|
||||||
|
android:title=""
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item android:id="@+id/reader_menu_favorite"
|
<item android:id="@+id/reader_menu_favorite"
|
||||||
android:title=""
|
android:title=""
|
||||||
android:iconTint="@color/material_orange_500"
|
|
||||||
android:icon="@drawable/avd_star"
|
android:icon="@drawable/avd_star"
|
||||||
app:showAsAction="always"/>
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
|
|||||||
@@ -66,11 +66,11 @@
|
|||||||
<string name="main_open_gallery_by_id">ギャラリー番号で見る</string>
|
<string name="main_open_gallery_by_id">ギャラリー番号で見る</string>
|
||||||
<string name="main_open_gallery_by_id_error">エラーが発生しました</string>
|
<string name="main_open_gallery_by_id_error">エラーが発生しました</string>
|
||||||
<string name="settings_storage">ストレージ</string>
|
<string name="settings_storage">ストレージ</string>
|
||||||
<string name="main_drawer_grouop_contact_kakaotalk">カカオトーク</string>
|
<string name="main_drawer_grouop_contact_discord">ディスコード</string>
|
||||||
<string name="settings_app_lock">アプリロック</string>
|
<string name="settings_app_lock">アプリロック</string>
|
||||||
<string name="settings_app_lock_type">アップロックの種類</string>
|
<string name="settings_app_lock_type">アップロックの種類</string>
|
||||||
<string name="settings_app_version_title">バージョン</string>
|
<string name="settings_app_version_title">バージョン</string>
|
||||||
<string name="settings_lock_biomatrics">生体認識</string>
|
<string name="settings_lock_biometrics">生体認識</string>
|
||||||
<string name="settings_lock_confirm">ロック確認のためもう一回入力してください。</string>
|
<string name="settings_lock_confirm">ロック確認のためもう一回入力してください。</string>
|
||||||
<string name="settings_lock_enabled">有効</string>
|
<string name="settings_lock_enabled">有効</string>
|
||||||
<string name="settings_lock_fingerprint">指紋</string>
|
<string name="settings_lock_fingerprint">指紋</string>
|
||||||
@@ -83,4 +83,31 @@
|
|||||||
<string name="main_menu_sort">ソート</string>
|
<string name="main_menu_sort">ソート</string>
|
||||||
<string name="main_menu_sort_newest">投稿日時順</string>
|
<string name="main_menu_sort_newest">投稿日時順</string>
|
||||||
<string name="main_menu_sort_popular">人気順</string>
|
<string name="main_menu_sort_popular">人気順</string>
|
||||||
|
<string name="update_failed">アップデートに失敗しました</string>
|
||||||
|
<string name="update_failed_message">マニュアルインストールが必要です。APKファイルは</string>
|
||||||
|
<string name="ignore_update">無視</string>
|
||||||
|
<string name="lock_corrupted">ロックファイルが破損されています。Pupilを再再インストールしてください。</string>
|
||||||
|
<string name="update_no_permission">権限がないため自動アップデートを行えません。ホームページで直接ダウンロードしてください。</string>
|
||||||
|
<string name="settings_dark_mode_title">ダークモード</string>
|
||||||
|
<string name="settings_dark_mode_summary">夜にシコりたい方々へ</string>
|
||||||
|
<string name="gallery_details">ギャラリー情報</string>
|
||||||
|
<string name="gallery_artists">アーティスト</string>
|
||||||
|
<string name="gallery_characters">キャラクター</string>
|
||||||
|
<string name="gallery_groups">グループ</string>
|
||||||
|
<string name="gallery_language">言語</string>
|
||||||
|
<string name="gallery_series">シリーズ</string>
|
||||||
|
<string name="gallery_tags">タグ</string>
|
||||||
|
<string name="gallery_thumbnails">サムネイル</string>
|
||||||
|
<string name="gallery_related">おすすめ</string>
|
||||||
|
<string name="settings_nomedia_summary">イメージをギャラリーから見えなくする</string>
|
||||||
|
<string name="settings_nomedia_title">イメージを隠す</string>
|
||||||
|
<string name="reader_help">ヘルプ</string>
|
||||||
|
<string name="main_delete">削除</string>
|
||||||
|
<string name="main_download">ダウンロード</string>
|
||||||
|
<string name="settings_backup_title">お気に入りバックアップ</string>
|
||||||
|
<string name="settings_restore_title">お気に入り復元</string>
|
||||||
|
<string name="settings_backup_snackbar">バックアップファイルを作成しました</string>
|
||||||
|
<string name="settings_backup_checkout">確認</string>
|
||||||
|
<string name="settings_restore_failed">復元に失敗しました</string>
|
||||||
|
<string name="settings_restore_successful">%1$d項目を復元しました</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -66,11 +66,11 @@
|
|||||||
<string name="main_open_gallery_by_id">갤러리 번호로 열기</string>
|
<string name="main_open_gallery_by_id">갤러리 번호로 열기</string>
|
||||||
<string name="main_open_gallery_by_id_error">갤러리를 찾지 못했습니다</string>
|
<string name="main_open_gallery_by_id_error">갤러리를 찾지 못했습니다</string>
|
||||||
<string name="settings_storage">저장 공간</string>
|
<string name="settings_storage">저장 공간</string>
|
||||||
<string name="main_drawer_grouop_contact_kakaotalk">카카오톡 오픈채팅방</string>
|
<string name="main_drawer_grouop_contact_discord">디스코드</string>
|
||||||
<string name="settings_app_lock">앱 잠금</string>
|
<string name="settings_app_lock">앱 잠금</string>
|
||||||
<string name="settings_app_lock_type">앱 잠금 종류</string>
|
<string name="settings_app_lock_type">앱 잠금 종류</string>
|
||||||
<string name="settings_app_version_title">앱 버전</string>
|
<string name="settings_app_version_title">앱 버전</string>
|
||||||
<string name="settings_lock_biomatrics">생체 인식</string>
|
<string name="settings_lock_biometrics">생체 인식</string>
|
||||||
<string name="settings_lock_confirm">잠금 확인을 위해 한번 더 입력해주세요</string>
|
<string name="settings_lock_confirm">잠금 확인을 위해 한번 더 입력해주세요</string>
|
||||||
<string name="settings_lock_enabled">사용 중</string>
|
<string name="settings_lock_enabled">사용 중</string>
|
||||||
<string name="settings_lock_fingerprint">지문</string>
|
<string name="settings_lock_fingerprint">지문</string>
|
||||||
@@ -83,4 +83,31 @@
|
|||||||
<string name="main_menu_sort">정렬</string>
|
<string name="main_menu_sort">정렬</string>
|
||||||
<string name="main_menu_sort_popular">인기순</string>
|
<string name="main_menu_sort_popular">인기순</string>
|
||||||
<string name="main_menu_sort_newest">시간순</string>
|
<string name="main_menu_sort_newest">시간순</string>
|
||||||
|
<string name="update_failed">"업데이트 "</string>
|
||||||
|
<string name="update_failed_message">수동 업데이트가 필요합니다. APK 파일은 다운로드 폴더에 있습니다.</string>
|
||||||
|
<string name="ignore_update">무시</string>
|
||||||
|
<string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string>
|
||||||
|
<string name="update_no_permission">권한이 부여되어 있지 않아 자동 업데이트를 진행할 수 없습니다. 홈페이지에서 직접 다운로드 받으시기 바랍니다.</string>
|
||||||
|
<string name="settings_dark_mode_title">다크 모드</string>
|
||||||
|
<string name="settings_dark_mode_summary">딥 다크한 모오드</string>
|
||||||
|
<string name="gallery_details">갤러리 정보</string>
|
||||||
|
<string name="gallery_artists">작가</string>
|
||||||
|
<string name="gallery_characters">캐릭터</string>
|
||||||
|
<string name="gallery_groups">그룹</string>
|
||||||
|
<string name="gallery_language">언어</string>
|
||||||
|
<string name="gallery_series">시리즈</string>
|
||||||
|
<string name="gallery_tags">태그</string>
|
||||||
|
<string name="gallery_related">관련 갤러리</string>
|
||||||
|
<string name="gallery_thumbnails">미리보기</string>
|
||||||
|
<string name="settings_nomedia_summary">갤러리에서 이미지 검색이 되지 않도록 합니다</string>
|
||||||
|
<string name="settings_nomedia_title">이미지 숨기기</string>
|
||||||
|
<string name="reader_help">도움말</string>
|
||||||
|
<string name="main_delete">삭제</string>
|
||||||
|
<string name="main_download">다운로드</string>
|
||||||
|
<string name="settings_backup_title">즐겨찾기 백업</string>
|
||||||
|
<string name="settings_restore_title">즐겨찾기 복원</string>
|
||||||
|
<string name="settings_backup_snackbar">백업 파일을 생성하였습니다</string>
|
||||||
|
<string name="settings_backup_checkout">확인</string>
|
||||||
|
<string name="settings_restore_failed">복원에 실패했습니다</string>
|
||||||
|
<string name="settings_restore_successful">%1$d개 항목을 복원했습니다</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -8,4 +8,6 @@
|
|||||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
<dimen name="nav_header_vertical_spacing">8dp</dimen>
|
<dimen name="nav_header_vertical_spacing">8dp</dimen>
|
||||||
<dimen name="nav_header_height">176dp</dimen>
|
<dimen name="nav_header_height">176dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="thumbnail_margin">8dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -7,9 +7,10 @@
|
|||||||
<string name="home_page" translatable="false">http://bit.ly/2EZDClw</string>
|
<string name="home_page" translatable="false">http://bit.ly/2EZDClw</string>
|
||||||
<string name="update" translatable="false">http://bit.ly/2ZlOjXJ</string>
|
<string name="update" translatable="false">http://bit.ly/2ZlOjXJ</string>
|
||||||
<string name="help" translatable="false">http://bit.ly/2Z7lNZE</string>
|
<string name="help" translatable="false">http://bit.ly/2Z7lNZE</string>
|
||||||
<string name="github" translatable="false">https://github.com/tom5079/Pupil-issue/issues/new/choose</string>
|
<string name="github" translatable="false">https://github.com/tom5079/Pupil/</string>
|
||||||
<string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string>
|
<string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string>
|
||||||
<string name="kakaotalk" translatable="false">https://open.kakao.com/o/gvNrncsb</string>
|
<string name="discord" translatable="false">https://discord.gg/Stj4b5v</string>
|
||||||
|
<string name="error_help" translatable="false">http://bit.ly/2KYYhto</string>
|
||||||
|
|
||||||
<string name="main_settings" translatable="false">Settings</string>
|
<string name="main_settings" translatable="false">Settings</string>
|
||||||
<string name="galleryblock_thumbnail_description" translatable="false">Thumbnail</string>
|
<string name="galleryblock_thumbnail_description" translatable="false">Thumbnail</string>
|
||||||
@@ -26,11 +27,18 @@
|
|||||||
<string name="https_block_alert_title">(Korean only)</string>
|
<string name="https_block_alert_title">(Korean only)</string>
|
||||||
<string name="https_block_alert">(Korean only)</string>
|
<string name="https_block_alert">(Korean only)</string>
|
||||||
|
|
||||||
|
<string name="update_failed">Update failed</string>
|
||||||
|
<string name="update_failed_message">Please install manually. APK file is in the Downloads folder.</string>
|
||||||
|
<string name="update_no_permission">Cannot auto update because permission is denied. Please download manually from the webpage.</string>
|
||||||
|
<string name="ignore_update">Ignore</string>
|
||||||
|
|
||||||
<string name="channel_download">Download</string>
|
<string name="channel_download">Download</string>
|
||||||
<string name="channel_download_description">Shows download status</string>
|
<string name="channel_download_description">Shows download status</string>
|
||||||
|
|
||||||
<string name="unable_to_connect">Unable to connect to hitomi.la</string>
|
<string name="unable_to_connect">Unable to connect to hitomi.la</string>
|
||||||
|
|
||||||
|
<string name="lock_corrupted">Lock file corrupted! Please re-install Pupil</string>
|
||||||
|
|
||||||
<string name="main_search">Search</string>
|
<string name="main_search">Search</string>
|
||||||
<string name="main_no_result">No result</string>
|
<string name="main_no_result">No result</string>
|
||||||
|
|
||||||
@@ -43,7 +51,7 @@
|
|||||||
<string name="main_drawer_group_contact_homepage">Visit homepage</string>
|
<string name="main_drawer_group_contact_homepage">Visit homepage</string>
|
||||||
<string name="main_drawer_group_contact_github">Visit github</string>
|
<string name="main_drawer_group_contact_github">Visit github</string>
|
||||||
<string name="main_drawer_group_contact_email">Email me!</string>
|
<string name="main_drawer_group_contact_email">Email me!</string>
|
||||||
<string name="main_drawer_grouop_contact_kakaotalk">Kakaotalk</string>
|
<string name="main_drawer_grouop_contact_discord">Discord</string>
|
||||||
|
|
||||||
<string name="main_menu_sort">Sort</string>
|
<string name="main_menu_sort">Sort</string>
|
||||||
<string name="main_menu_sort_newest">Newest</string>
|
<string name="main_menu_sort_newest">Newest</string>
|
||||||
@@ -63,6 +71,9 @@
|
|||||||
<string name="main_export_open_folder">Open Folder</string>
|
<string name="main_export_open_folder">Open Folder</string>
|
||||||
<string name="main_export_error">Error occurred during export</string>
|
<string name="main_export_error">Error occurred during export</string>
|
||||||
|
|
||||||
|
<string name="main_download">DOWNLOAD</string>
|
||||||
|
<string name="main_delete">DELETE</string>
|
||||||
|
|
||||||
<string name="update_title">Update available</string>
|
<string name="update_title">Update available</string>
|
||||||
<string name="update_download_started">Download started</string>
|
<string name="update_download_started">Download started</string>
|
||||||
<string name="update_notification_description">Downloading apk…</string>
|
<string name="update_notification_description">Downloading apk…</string>
|
||||||
@@ -71,6 +82,16 @@
|
|||||||
<string name="search_hint">Search galleries</string>
|
<string name="search_hint">Search galleries</string>
|
||||||
<string name="search_hint_with_page">Search galleries</string>
|
<string name="search_hint_with_page">Search galleries</string>
|
||||||
|
|
||||||
|
<string name="gallery_details">Details</string>
|
||||||
|
<string name="gallery_thumbnails">Thumbnails</string>
|
||||||
|
<string name="gallery_related">Related Galleries</string>
|
||||||
|
<string name="gallery_artists">Artists</string>
|
||||||
|
<string name="gallery_groups">Groups</string>
|
||||||
|
<string name="gallery_language">Language</string>
|
||||||
|
<string name="gallery_series">Series</string>
|
||||||
|
<string name="gallery_characters">Characters</string>
|
||||||
|
<string name="gallery_tags">Tags</string>
|
||||||
|
|
||||||
<string name="galleryblock_series">Series: %1$s</string>
|
<string name="galleryblock_series">Series: %1$s</string>
|
||||||
<string name="galleryblock_type">Type: %1$s</string>
|
<string name="galleryblock_type">Type: %1$s</string>
|
||||||
<string name="galleryblock_language">Language: %1$s</string>
|
<string name="galleryblock_language">Language: %1$s</string>
|
||||||
@@ -84,6 +105,8 @@
|
|||||||
<string name="reader_notification_complete">Download complete</string>
|
<string name="reader_notification_complete">Download complete</string>
|
||||||
<string name="reader_notification_error">Download error</string>
|
<string name="reader_notification_error">Download error</string>
|
||||||
|
|
||||||
|
<string name="reader_help">Help</string>
|
||||||
|
|
||||||
<string name="settings_title">Settings</string>
|
<string name="settings_title">Settings</string>
|
||||||
<string name="settings_app_version_title">App version</string>
|
<string name="settings_app_version_title">App version</string>
|
||||||
<string name="settings_search_title">Search Settings</string>
|
<string name="settings_search_title">Search Settings</string>
|
||||||
@@ -105,12 +128,22 @@
|
|||||||
<string name="settings_use_hiyobi_summary">Load images from hiyobi.me to improve loading speed (if available)</string>
|
<string name="settings_use_hiyobi_summary">Load images from hiyobi.me to improve loading speed (if available)</string>
|
||||||
<string name="settings_security_mode_title">Enable security mode</string>
|
<string name="settings_security_mode_title">Enable security mode</string>
|
||||||
<string name="settings_security_mode_summary">Enable security mode to make the screen invisible on recent app window</string>
|
<string name="settings_security_mode_summary">Enable security mode to make the screen invisible on recent app window</string>
|
||||||
|
<string name="settings_dark_mode_title">Dark mode</string>
|
||||||
|
<string name="settings_dark_mode_summary">Protect yourself against light attacks!</string>
|
||||||
|
<string name="settings_nomedia_title">Hide image from gallery</string>
|
||||||
|
<string name="settings_nomedia_summary">Hides image from gallery</string>
|
||||||
|
<string name="settings_backup_title">Backup favorites</string>
|
||||||
|
<string name="settings_backup_snackbar">Backup file created</string>
|
||||||
|
<string name="settings_backup_checkout">Check out</string>
|
||||||
|
<string name="settings_restore_title">Restore favorites</string>
|
||||||
|
<string name="settings_restore_failed">Restore failed</string>
|
||||||
|
<string name="settings_restore_successful">%1$d entries restored</string>
|
||||||
|
|
||||||
<string name="settings_lock_none">None</string>
|
<string name="settings_lock_none">None</string>
|
||||||
<string name="settings_lock_pattern">Pattern</string>
|
<string name="settings_lock_pattern">Pattern</string>
|
||||||
<string name="settings_lock_pin" translatable="false">PIN</string>
|
<string name="settings_lock_pin" translatable="false">PIN</string>
|
||||||
<string name="settings_lock_password">Password</string>
|
<string name="settings_lock_password">Password</string>
|
||||||
<string name="settings_lock_biomatrics">Biomatrics</string>
|
<string name="settings_lock_biometrics">Biometrics</string>
|
||||||
<string name="settings_lock_fingerprint">Fingerprint</string>
|
<string name="settings_lock_fingerprint">Fingerprint</string>
|
||||||
<string name="settings_lock_enabled">Enabled</string>
|
<string name="settings_lock_enabled">Enabled</string>
|
||||||
<string name="settings_lock_confirm">Input same lock once more to confirm Lock</string>
|
<string name="settings_lock_confirm">Input same lock once more to confirm Lock</string>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="NoActionBarAppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
|
<style name="NoActionBarAppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
|||||||
22
app/src/main/res/xml/file_paths.xml
Normal file
22
app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
|
~ Copyright (C) 2019 tom5079
|
||||||
|
~
|
||||||
|
~ 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 <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<external-path name="external" path="/"/>
|
||||||
|
</paths>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
app:key="lock_password"/>
|
app:key="lock_password"/>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:title="@string/settings_lock_biomatrics">
|
app:title="@string/settings_lock_biometrics">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:title="@string/settings_lock_fingerprint"
|
app:title="@string/settings_lock_fingerprint"
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
<Preference
|
<Preference
|
||||||
app:key="default_query"
|
app:key="default_query"
|
||||||
app:title="@string/settings_default_query"
|
app:title="@string/settings_default_query"
|
||||||
app:defaultValue=""
|
|
||||||
app:useSimpleSummaryProvider="true"/>
|
app:useSimpleSummaryProvider="true"/>
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
@@ -65,6 +64,24 @@
|
|||||||
app:summary="@string/settings_security_mode_summary"
|
app:summary="@string/settings_security_mode_summary"
|
||||||
app:defaultValue="true"/>
|
app:defaultValue="true"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
app:key="dark_mode"
|
||||||
|
app:title="@string/settings_dark_mode_title"
|
||||||
|
app:summary="@string/settings_dark_mode_summary"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
app:key="nomedia"
|
||||||
|
app:title="@string/settings_nomedia_title"
|
||||||
|
app:summary="@string/settings_nomedia_title"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:key="backup"
|
||||||
|
app:title="@string/settings_backup_title"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:key="restore"
|
||||||
|
app:title="@string/settings_restore_title"/>
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen>
|
</androidx.preference.PreferenceScreen>
|
||||||
|
|||||||
@@ -20,22 +20,19 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
*
|
*
|
||||||
* See [testing documentation](http://d.android.com/tools/testing).
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
class ExampleUnitTest {
|
class ExampleUnitTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test() {
|
fun test() {
|
||||||
val current = "0.1"
|
|
||||||
val latest = "0.2"
|
|
||||||
|
|
||||||
print(current < latest)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
12
build.gradle
12
build.gradle
@@ -1,24 +1,22 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.41'
|
ext.kotlin_version = '1.3.61'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
maven { url 'https://maven.fabric.io/public' }
|
||||||
url 'https://maven.fabric.io/public'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
classpath 'com.google.gms:google-services:4.3.0'
|
classpath 'com.google.gms:google-services:4.3.3'
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
classpath 'io.fabric.tools:gradle:1.29.0'
|
classpath 'io.fabric.tools:gradle:1.29.0'
|
||||||
classpath 'com.google.firebase:perf-plugin:1.2.1'
|
classpath 'com.google.firebase:perf-plugin:1.3.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Thu Apr 25 10:57:40 KST 2019
|
#Fri Aug 23 08:21:15 KST 2019
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ dependencies {
|
|||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0"
|
||||||
implementation 'org.jsoup:jsoup:1.11.3'
|
implementation 'org.jsoup:jsoup:1.11.3'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,6 @@ dependencies {
|
|||||||
sourceCompatibility = "7"
|
sourceCompatibility = "7"
|
||||||
targetCompatibility = "7"
|
targetCompatibility = "7"
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.31'
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|||||||
26
libpupil/src/main/java/xyz/quaver/Utils.kt
Normal file
26
libpupil/src/main/java/xyz/quaver/Utils.kt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 tom5079
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package xyz.quaver
|
||||||
|
|
||||||
|
fun availableInHiyobi(galleryID: Int) : Boolean {
|
||||||
|
return try {
|
||||||
|
xyz.quaver.hiyobi.getReader(galleryID)
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,11 +16,25 @@
|
|||||||
|
|
||||||
package xyz.quaver.hitomi
|
package xyz.quaver.hitomi
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
|
import kotlinx.serialization.list
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
const val protocol = "https:"
|
const val protocol = "https:"
|
||||||
|
|
||||||
|
fun getGalleryInfo(galleryID: Int): List<GalleryInfo> {
|
||||||
|
return Json(JsonConfiguration.Stable).parse(
|
||||||
|
GalleryInfo.serializer().list,
|
||||||
|
Regex("""\[.+]""").find(
|
||||||
|
URL("$protocol//$domain/galleries/$galleryID.js").readText()
|
||||||
|
)?.value ?: "[]"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
//common.js
|
//common.js
|
||||||
var adapose = false
|
var adapose = false
|
||||||
const val numberOfFrontends = 2
|
const val numberOfFrontends = 3
|
||||||
const val domain = "ltn.hitomi.la"
|
const val domain = "ltn.hitomi.la"
|
||||||
const val galleryblockdir = "galleryblock"
|
const val galleryblockdir = "galleryblock"
|
||||||
const val nozomiextension = ".nozomi"
|
const val nozomiextension = ".nozomi"
|
||||||
@@ -37,20 +51,22 @@ fun subdomainFromGalleryID(g: Int) : String {
|
|||||||
fun subdomainFromURL(url: String, base: String? = null) : String {
|
fun subdomainFromURL(url: String, base: String? = null) : String {
|
||||||
var retval = "a"
|
var retval = "a"
|
||||||
|
|
||||||
if (base != null)
|
if (!base.isNullOrBlank())
|
||||||
retval = base
|
retval = base
|
||||||
|
|
||||||
val r = Regex("""/\d*(\d)/""")
|
val r = Regex("""/galleries/\d*(\d)/""")
|
||||||
val m = r.find(url)
|
var m = r.find(url)
|
||||||
|
var b = 10
|
||||||
|
|
||||||
m ?: return retval
|
if (m == null) {
|
||||||
|
b = 16
|
||||||
|
val r2 = Regex("""/[0-9a-f]/([0-9a-f]{2})/""")
|
||||||
|
m = r2.find(url)
|
||||||
|
if (m == null)
|
||||||
|
return retval
|
||||||
|
}
|
||||||
|
|
||||||
var g = m.groups[1]!!.value.toIntOrNull()
|
val g = m.groupValues[1].toIntOrNull(b) ?: return retval
|
||||||
|
|
||||||
g ?: return retval
|
|
||||||
|
|
||||||
if (g == 1)
|
|
||||||
g = 0
|
|
||||||
|
|
||||||
retval = subdomainFromGalleryID(g) + retval
|
retval = subdomainFromGalleryID(g) + retval
|
||||||
|
|
||||||
@@ -58,5 +74,25 @@ fun subdomainFromURL(url: String, base: String? = null) : String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun urlFromURL(url: String, base: String? = null) : String {
|
fun urlFromURL(url: String, base: String? = null) : String {
|
||||||
return url.replace(Regex("//..?\\.hitomi\\.la/"), "//${subdomainFromURL(url, base)}.hitomi.la/")
|
return url.replace(Regex("""//..?\.hitomi\.la/"""), "//${subdomainFromURL(url, base)}.hitomi.la/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fullPathFromHash(hash: String?) : String? {
|
||||||
|
return when {
|
||||||
|
(hash?.length ?: 0) < 3 -> hash
|
||||||
|
else -> hash!!.replace(Regex("^.*(..)(.)$"), "$2/$1/$hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun urlFromHash(galleryID: Int, image: GalleryInfo, webp: String? = null) : String {
|
||||||
|
val ext = webp ?: image.name.split('.').last()
|
||||||
|
return when {
|
||||||
|
image.hash.isNullOrBlank() ->
|
||||||
|
"$protocol//a.hitomi.la/galleries/$galleryID/${image.name}"
|
||||||
|
else ->
|
||||||
|
"$protocol//a.hitomi.la/${webp?:"images"}/${fullPathFromHash(image.hash)}.$ext"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun urlFromUrlFromHash(galleryID: Int, image: GalleryInfo, webp: String? = null) =
|
||||||
|
urlFromURL(urlFromHash(galleryID, image, webp))
|
||||||
@@ -17,12 +17,12 @@
|
|||||||
package xyz.quaver.hitomi
|
package xyz.quaver.hitomi
|
||||||
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.net.URL
|
import java.net.URLDecoder
|
||||||
|
|
||||||
data class Gallery(
|
data class Gallery(
|
||||||
val related: List<Int>,
|
val related: List<Int>,
|
||||||
val langList: List<Pair<String, String>>,
|
val langList: List<Pair<String, String>>,
|
||||||
val cover: URL,
|
val cover: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val artists: List<String>,
|
val artists: List<String>,
|
||||||
val groups: List<String>,
|
val groups: List<String>,
|
||||||
@@ -31,10 +31,11 @@ data class Gallery(
|
|||||||
val series: List<String>,
|
val series: List<String>,
|
||||||
val characters: List<String>,
|
val characters: List<String>,
|
||||||
val tags: List<String>,
|
val tags: List<String>,
|
||||||
val thumbnails: List<URL>
|
val thumbnails: List<String>
|
||||||
)
|
)
|
||||||
fun getGallery(galleryID: Int) : Gallery {
|
fun getGallery(galleryID: Int) : Gallery {
|
||||||
val url = "https://hitomi.la/galleries/$galleryID.html"
|
val url = Jsoup.connect("https://hitomi.la/galleries/$galleryID.html").get()
|
||||||
|
.select("a").attr("href")
|
||||||
|
|
||||||
val doc = Jsoup.connect(url).get()
|
val doc = Jsoup.connect(url).get()
|
||||||
|
|
||||||
@@ -45,10 +46,10 @@ fun getGallery(galleryID: Int) : Gallery {
|
|||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
val langList = doc.select("#lang-list a").map {
|
val langList = doc.select("#lang-list a").map {
|
||||||
Pair(it.text(), it.attr("href").replace(".html", ""))
|
Pair(it.text(), "$protocol//hitomi.la${it.attr("href")}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val cover = URL(protocol + doc.selectFirst(".cover img").attr("src"))
|
val cover = protocol + doc.selectFirst(".cover img").attr("src")
|
||||||
val title = doc.selectFirst(".gallery h1 a").text()
|
val title = doc.selectFirst(".gallery h1 a").text()
|
||||||
val artists = doc.select(".gallery h2 a").map { it.text() }
|
val artists = doc.select(".gallery h2 a").map { it.text() }
|
||||||
val groups = doc.select(".gallery-info a[href~=^/group/]").map { it.text() }
|
val groups = doc.select(".gallery-info a[href~=^/group/]").map { it.text() }
|
||||||
@@ -63,15 +64,13 @@ fun getGallery(galleryID: Int) : Gallery {
|
|||||||
val characters = doc.select(".gallery-info a[href~=^/character/]").map { it.text() }
|
val characters = doc.select(".gallery-info a[href~=^/character/]").map { it.text() }
|
||||||
|
|
||||||
val tags = doc.select(".gallery-info a[href~=^/tag/]").map {
|
val tags = doc.select(".gallery-info a[href~=^/tag/]").map {
|
||||||
val href = it.attr("href")
|
val href = URLDecoder.decode(it.attr("href"), "UTF-8")
|
||||||
href.slice(5 until href.indexOf('-'))
|
href.slice(5 until href.indexOf('-'))
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbnails = Regex("'(//tn.hitomi.la/smalltn/\\d+/\\d+.+)',")
|
val thumbnails = getGalleryInfo(galleryID).map {
|
||||||
.findAll(doc.select("script").last().html())
|
"$protocol//tn.hitomi.la/smalltn/$galleryID/${it.name}.jpg"
|
||||||
.map {
|
}
|
||||||
URL(protocol + it.groups[1]!!.value)
|
|
||||||
}.toList()
|
|
||||||
|
|
||||||
return Gallery(related, langList, cover, title, artists, groups, type, language, series, characters, tags, thumbnails)
|
return Gallery(related, langList, cover, title, artists, groups, type, language, series, characters, tags, thumbnails)
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,6 @@ package xyz.quaver.hitomi
|
|||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import sun.rmi.runtime.Log
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
@@ -69,6 +68,7 @@ fun fetchNozomi(area: String? = null, tag: String = "index", language: String =
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class GalleryBlock(
|
data class GalleryBlock(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
val galleryUrl: String,
|
||||||
val thumbnails: List<String>,
|
val thumbnails: List<String>,
|
||||||
val title: String,
|
val title: String,
|
||||||
val artists: List<String>,
|
val artists: List<String>,
|
||||||
@@ -83,6 +83,8 @@ fun getGalleryBlock(galleryID: Int) : GalleryBlock? {
|
|||||||
try {
|
try {
|
||||||
val doc = Jsoup.connect(url).get()
|
val doc = Jsoup.connect(url).get()
|
||||||
|
|
||||||
|
val galleryUrl = doc.selectFirst(".lillie").attr("href")
|
||||||
|
|
||||||
val thumbnails = doc.select("img").map { protocol + it.attr("data-src") }
|
val thumbnails = doc.select("img").map { protocol + it.attr("data-src") }
|
||||||
|
|
||||||
val title = doc.selectFirst("h1.lillie > a").text()
|
val title = doc.selectFirst("h1.lillie > a").text()
|
||||||
@@ -100,7 +102,7 @@ fun getGalleryBlock(galleryID: Int) : GalleryBlock? {
|
|||||||
href.slice(5 until href.indexOf("-all"))
|
href.slice(5 until href.indexOf("-all"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return GalleryBlock(galleryID, thumbnails, title, artists, series, type, language, relatedTags)
|
return GalleryBlock(galleryID, galleryUrl, thumbnails, title, artists, series, type, language, relatedTags)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,72 +17,34 @@
|
|||||||
package xyz.quaver.hitomi
|
package xyz.quaver.hitomi
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.JsonConfiguration
|
|
||||||
import kotlinx.serialization.list
|
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import xyz.quaver.hiyobi.HiyobiReader
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html"
|
fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html"
|
||||||
fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp"
|
fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp"
|
||||||
|
|
||||||
fun webpReaderFromReader(reader: Reader) : Reader {
|
|
||||||
if (reader is HiyobiReader)
|
|
||||||
return reader
|
|
||||||
|
|
||||||
return Reader(reader.title, reader.readerItems.map {
|
|
||||||
ReaderItem(
|
|
||||||
if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url,
|
|
||||||
it.galleryInfo
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class GalleryInfo(
|
data class GalleryInfo(
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val haswebp: Int,
|
val hash: String? = null,
|
||||||
|
val haswebp: Int = 0,
|
||||||
val name: String,
|
val name: String,
|
||||||
val height: Int
|
val height: Int
|
||||||
)
|
)
|
||||||
@Serializable
|
|
||||||
data class ReaderItem(
|
|
||||||
val url: String,
|
|
||||||
val galleryInfo: GalleryInfo?
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
open class Reader(val title: String, val readerItems: List<ReaderItem>)
|
data class Reader(val code: Code, val title: String, val galleryInfo: List<GalleryInfo>) {
|
||||||
|
enum class Code {
|
||||||
|
HITOMI,
|
||||||
|
HIYOBI,
|
||||||
|
SORALA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Set header `Referer` to reader url to avoid 403 error
|
//Set header `Referer` to reader url to avoid 403 error
|
||||||
fun getReader(galleryID: Int) : Reader {
|
fun getReader(galleryID: Int) : Reader {
|
||||||
val readerUrl = "https://hitomi.la/reader/$galleryID.html"
|
val readerUrl = "https://hitomi.la/reader/$galleryID.html"
|
||||||
val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js"
|
|
||||||
|
|
||||||
val doc = Jsoup.connect(readerUrl).get()
|
val doc = Jsoup.connect(readerUrl).get()
|
||||||
|
|
||||||
val title = doc.title()
|
return Reader(Reader.Code.HITOMI, doc.title(), getGalleryInfo(galleryID))
|
||||||
|
|
||||||
val images = doc.select(".img-url").map {
|
|
||||||
protocol + urlFromURL(it.text())
|
|
||||||
}
|
|
||||||
|
|
||||||
val galleryInfo = ArrayList<GalleryInfo?>()
|
|
||||||
|
|
||||||
galleryInfo.addAll(
|
|
||||||
Json(JsonConfiguration.Stable).parse(
|
|
||||||
GalleryInfo.serializer().list,
|
|
||||||
Regex("""\[.+]""").find(
|
|
||||||
URL(galleryInfoUrl).readText()
|
|
||||||
)?.value ?: "[]"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (images.size > galleryInfo.size)
|
|
||||||
galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size))
|
|
||||||
|
|
||||||
return Reader(title, (images zip galleryInfo).map {
|
|
||||||
ReaderItem(it.first, it.second)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
@@ -57,30 +57,33 @@ fun doSearch(query: String, sortByPopularity: Boolean = false) : List<Int> {
|
|||||||
var results = when {
|
var results = when {
|
||||||
sortByPopularity -> getGalleryIDsFromNozomi(null, "popular", "all")
|
sortByPopularity -> getGalleryIDsFromNozomi(null, "popular", "all")
|
||||||
positiveTerms.isEmpty() -> getGalleryIDsFromNozomi(null, "index", "all")
|
positiveTerms.isEmpty() -> getGalleryIDsFromNozomi(null, "index", "all")
|
||||||
else -> getGalleryIDsForQuery(positiveTerms.poll())
|
else -> listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
@Synchronized fun filterPositive(newResults: List<Int>) {
|
@Synchronized fun filterPositive(newResults: List<Int>) {
|
||||||
results = results.filter { newResults.binarySearch(it) >= 0 }
|
results = when {
|
||||||
|
results.isEmpty() -> newResults
|
||||||
|
else -> newResults.sorted().let { sorted ->
|
||||||
|
results.filter { sorted.binarySearch(it) >= 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized fun filterNegative(newResults: List<Int>) {
|
@Synchronized fun filterNegative(newResults: List<Int>) {
|
||||||
results = results.filter { newResults.binarySearch(it) < 0 }
|
results = newResults.sorted().let { sorted ->
|
||||||
|
results.filter { sorted.binarySearch(it) < 0 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//positive results
|
//positive results
|
||||||
positiveResults.forEach {
|
positiveResults.forEach {
|
||||||
val result = it.await()
|
filterPositive(it.await())
|
||||||
|
|
||||||
filterPositive(result.sorted())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//negative results
|
//negative results
|
||||||
negativeResults.forEach {
|
negativeResults.forEach {
|
||||||
val result = it.await()
|
filterNegative(it.await())
|
||||||
|
|
||||||
filterNegative(result.sorted())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package xyz.quaver.hitomi
|
package xyz.quaver.hitomi
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
@@ -175,13 +174,10 @@ fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : List
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
with (URL(nozomiAddress).openConnection() as HttpsURLConnection) {
|
val bytes = URL(nozomiAddress).readBytes()
|
||||||
requestMethod = "GET"
|
|
||||||
|
|
||||||
val nozomi = ArrayList<Int>()
|
val nozomi = ArrayList<Int>()
|
||||||
|
|
||||||
val bytes = inputStream.readBytes()
|
|
||||||
|
|
||||||
val arrayBuffer = ByteBuffer
|
val arrayBuffer = ByteBuffer
|
||||||
.wrap(bytes)
|
.wrap(bytes)
|
||||||
.order(ByteOrder.BIG_ENDIAN)
|
.order(ByteOrder.BIG_ENDIAN)
|
||||||
@@ -190,7 +186,6 @@ fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : List
|
|||||||
nozomi.add(arrayBuffer.int)
|
nozomi.add(arrayBuffer.int)
|
||||||
|
|
||||||
return nozomi
|
return nozomi
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,17 @@ package xyz.quaver.hiyobi
|
|||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonConfiguration
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
import kotlinx.serialization.json.content
|
import kotlinx.serialization.list
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
import xyz.quaver.hitomi.GalleryInfo
|
||||||
import xyz.quaver.hitomi.Reader
|
import xyz.quaver.hitomi.Reader
|
||||||
import xyz.quaver.hitomi.ReaderItem
|
import xyz.quaver.hitomi.protocol
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
const val hiyobi = "xn--9w3b15m8vo.asia"
|
const val hiyobi = "hiyobi.me"
|
||||||
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
|
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
|
||||||
|
|
||||||
class HiyobiReader(title: String, readerItems: List<ReaderItem>) : Reader(title, readerItems)
|
|
||||||
|
|
||||||
var cookie: String = ""
|
var cookie: String = ""
|
||||||
get() {
|
get() {
|
||||||
if (field.isEmpty())
|
if (field.isEmpty())
|
||||||
@@ -38,6 +37,12 @@ var cookie: String = ""
|
|||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Images(
|
||||||
|
val path: String,
|
||||||
|
val no: Int,
|
||||||
|
val name: String
|
||||||
|
)
|
||||||
|
|
||||||
fun renewCookie() : String {
|
fun renewCookie() : String {
|
||||||
val url = "https://$hiyobi/"
|
val url = "https://$hiyobi/"
|
||||||
|
|
||||||
@@ -59,7 +64,8 @@ fun getReader(galleryID: Int) : Reader {
|
|||||||
|
|
||||||
val title = Jsoup.connect(reader).get().title()
|
val title = Jsoup.connect(reader).get().title()
|
||||||
|
|
||||||
val json = Json(JsonConfiguration.Stable).parseJson(
|
val galleryInfo = Json(JsonConfiguration.Stable).parse(
|
||||||
|
GalleryInfo.serializer().list,
|
||||||
with(URL(url).openConnection() as HttpsURLConnection) {
|
with(URL(url).openConnection() as HttpsURLConnection) {
|
||||||
setRequestProperty("User-Agent", user_agent)
|
setRequestProperty("User-Agent", user_agent)
|
||||||
setRequestProperty("Cookie", cookie)
|
setRequestProperty("Cookie", cookie)
|
||||||
@@ -70,8 +76,8 @@ fun getReader(galleryID: Int) : Reader {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return Reader(title, json.jsonArray.map {
|
return Reader(Reader.Code.HIYOBI, title, galleryInfo)
|
||||||
val name = it.jsonObject["name"]!!.content
|
|
||||||
ReaderItem("https://$hiyobi/data/$galleryID/$name", null)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createImgList(galleryID: Int, reader: Reader) =
|
||||||
|
reader.galleryInfo.map { Images("$protocol//$hiyobi/data/$galleryID/${it.name}", galleryID, it.name) }
|
||||||
@@ -18,12 +18,17 @@
|
|||||||
|
|
||||||
package xyz.quaver.hitomi
|
package xyz.quaver.hitomi
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import xyz.quaver.availableInHiyobi
|
||||||
|
|
||||||
class UnitTest {
|
class UnitTest {
|
||||||
@Test
|
@Test
|
||||||
fun test() {
|
fun test() {
|
||||||
|
assertEquals(
|
||||||
|
"6/2d/c26014fc6153ef717932d85f4d26c75195560fb2ce1da60b431ef376501642d6",
|
||||||
|
fullPathFromHash("c26014fc6153ef717932d85f4d26c75195560fb2ce1da60b431ef376501642d6")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -63,20 +68,36 @@ class UnitTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_getGallery() {
|
fun test_getGallery() {
|
||||||
val gallery = getGallery(1405267)
|
val gallery = getGallery(1510566)
|
||||||
|
|
||||||
print(gallery)
|
print(gallery)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_getReader() {
|
fun test_getReader() {
|
||||||
val reader = getReader(1404693)
|
val reader = getReader(1442740)
|
||||||
|
|
||||||
print(reader)
|
print(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_hiyobi() {
|
fun test_hiyobi() {
|
||||||
|
xyz.quaver.hiyobi.getReader(1510567)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_urlFromUrlFromHash() {
|
||||||
|
val url = urlFromUrlFromHash(1531795, GalleryInfo(
|
||||||
|
212, "719d46a7556be0d0021c5105878507129b5b3308b02cf67f18901b69dbb3b5ef", 1, "00.jpg", 300
|
||||||
|
), "webp")
|
||||||
|
|
||||||
|
print(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_availableInHiyobi() {
|
||||||
|
val result = availableInHiyobi(1272781)
|
||||||
|
|
||||||
|
print(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user