Compare commits
52 Commits
5.1.2
...
5.1.7-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65fafa8f2c | ||
|
|
edef2a3eae | ||
|
|
338241d26f | ||
|
|
17b031e1b7 | ||
|
|
2abfe1a061 | ||
|
|
7510202f55 | ||
|
|
f07f624fcf | ||
|
|
48ff2f328f | ||
|
|
9ae2423a40 | ||
|
|
2bc3c78c75 | ||
|
|
18e9fe75fb | ||
|
|
880a741a44 | ||
|
|
2c6ddcc64b | ||
|
|
8f2e757b77 | ||
|
|
ff177955b3 | ||
|
|
8bb8066a98 | ||
|
|
2747ddbf65 | ||
|
|
b939e9424d | ||
|
|
fb9dea5d1e | ||
|
|
da4d5d711b | ||
|
|
331cbec5f1 | ||
|
|
7f02284285 | ||
|
|
ac2c3a6d97 | ||
|
|
c3bc80fec6 | ||
|
|
09779a0710 | ||
|
|
e82c6ef866 | ||
|
|
861ae9be64 | ||
|
|
96108bc1ec | ||
|
|
016f217db0 | ||
|
|
0688294f18 | ||
|
|
9ad008255d | ||
|
|
4c5a862dd6 | ||
|
|
b165a2308f | ||
|
|
8757b08cd2 | ||
|
|
3800543fba | ||
|
|
02ef60c818 | ||
|
|
88f3b30266 | ||
|
|
9203dc0112 | ||
|
|
4c683bec68 | ||
|
|
0cfd1eb453 | ||
|
|
19744dab37 | ||
|
|
12d58e5aa7 | ||
|
|
e46dd37a26 | ||
|
|
49c3ebc36b | ||
|
|
11e9bc2235 | ||
|
|
3029b3bf0e | ||
|
|
9a6c6f67ce | ||
|
|
a6ed0baef2 | ||
|
|
d3b43d80da | ||
|
|
46d4316d49 | ||
|
|
ade2864351 | ||
|
|
365fc56e9d |
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="1.8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2
.idea/copyright/profiles_settings.xml
generated
2
.idea/copyright/profiles_settings.xml
generated
@@ -1,7 +1,7 @@
|
|||||||
<component name="CopyrightManager">
|
<component name="CopyrightManager">
|
||||||
<settings>
|
<settings>
|
||||||
<module2copyright>
|
<module2copyright>
|
||||||
<element module="Pupil" copyright="GPL" />
|
<element module="Project Files" copyright="GPL" />
|
||||||
</module2copyright>
|
</module2copyright>
|
||||||
</settings>
|
</settings>
|
||||||
</component>
|
</component>
|
||||||
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -14,6 +14,7 @@
|
|||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
<option name="useQualifiedModuleNames" value="true" />
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="AndroidLintNewerVersionAvailable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
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_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
*Pupil, Hitomi.la viewer for Android*
|
*Pupil, Hitomi.la viewer for Android*
|
||||||
|
|
||||||

|

|
||||||
[](https://github.com/tom5079/Pupil/releases/download/5.1.1-hotfix3/Pupil-v5.1.1-hotfix3.apk)
|
[](https://github.com/tom5079/Pupil/releases/download/5.1.6-hotfix7/Pupil-v5.1.6-hotfix7.apk)
|
||||||
[](https://discord.gg/Stj4b5v)
|
[](https://discord.gg/Stj4b5v)
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
@@ -21,3 +21,6 @@ or Build app yourself
|
|||||||
# Contribution
|
# Contribution
|
||||||
|
|
||||||
Any kind of contribution is appriciated. Feel free to leave PR!
|
Any kind of contribution is appriciated. Feel free to leave PR!
|
||||||
|
|
||||||
|
## Tag Translation
|
||||||
|
Head over to [tags branch](https://github.com/tom5079/Pupil/tree/tags)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ apply plugin: "kotlin-android-extensions"
|
|||||||
apply plugin: "kotlinx-serialization"
|
apply plugin: "kotlinx-serialization"
|
||||||
apply plugin: "com.google.android.gms.oss-licenses-plugin"
|
apply plugin: "com.google.android.gms.oss-licenses-plugin"
|
||||||
|
|
||||||
if (file("google-services.json").exists() && file("src/debug/google-services.json").exists()) {
|
if (file("google-services.json").exists()) {
|
||||||
logger.lifecycle("Firebase Enabled")
|
logger.lifecycle("Firebase Enabled")
|
||||||
apply plugin: "com.google.gms.google-services"
|
apply plugin: "com.google.gms.google-services"
|
||||||
apply plugin: "com.google.firebase.crashlytics"
|
apply plugin: "com.google.firebase.crashlytics"
|
||||||
@@ -37,8 +37,8 @@ android {
|
|||||||
applicationId "xyz.quaver.pupil"
|
applicationId "xyz.quaver.pupil"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 61
|
versionCode 64
|
||||||
versionName "5.1.2"
|
versionName "5.1.7-beta1"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,6 @@ android {
|
|||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
versionNameSuffix "-DEBUG"
|
versionNameSuffix "-DEBUG"
|
||||||
|
|
||||||
buildConfigField("Boolean", "CENSOR", "false")
|
|
||||||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
||||||
|
|
||||||
ext.enableCrashlytics = false
|
ext.enableCrashlytics = false
|
||||||
@@ -61,7 +60,6 @@ android {
|
|||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
|
|
||||||
buildConfigField("Boolean", "CENSOR", "false")
|
|
||||||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,27 +77,27 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
|
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0-RC2"
|
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||||
implementation "androidx.activity:activity-ktx:1.2.0-alpha08"
|
implementation "androidx.activity:activity-ktx:1.2.0-beta01"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.3.0-alpha08"
|
implementation "androidx.fragment:fragment-ktx:1.3.0-beta01"
|
||||||
implementation "androidx.preference:preference:1.1.1"
|
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.0.1"
|
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||||
|
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||||
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
||||||
implementation "androidx.biometric:biometric:1.0.1"
|
implementation "androidx.biometric:biometric:1.0.1"
|
||||||
implementation "androidx.work:work-runtime-ktx:2.4.0"
|
implementation "androidx.work:work-runtime-ktx:2.4.0"
|
||||||
|
|
||||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||||
|
|
||||||
implementation "com.google.android.material:material:1.3.0-alpha02"
|
implementation "com.google.android.material:material:1.3.0-alpha04"
|
||||||
|
|
||||||
implementation "com.google.firebase:firebase-core:17.5.0"
|
implementation "com.google.firebase:firebase-core:18.0.0"
|
||||||
implementation "com.google.firebase:firebase-analytics:17.5.0"
|
implementation "com.google.firebase:firebase-analytics:18.0.0"
|
||||||
implementation "com.google.firebase:firebase-crashlytics:17.2.1"
|
implementation "com.google.firebase:firebase-crashlytics:17.3.0"
|
||||||
implementation "com.google.firebase:firebase-perf:19.0.8"
|
implementation "com.google.firebase:firebase-perf:19.0.10"
|
||||||
|
|
||||||
implementation "com.google.android.gms:play-services-oss-licenses:17.0.0"
|
implementation "com.google.android.gms:play-services-oss-licenses:17.0.0"
|
||||||
implementation "com.google.android.gms:play-services-mlkit-face-detection:16.1.1"
|
implementation "com.google.android.gms:play-services-mlkit-face-detection:16.1.1"
|
||||||
@@ -125,11 +123,11 @@ dependencies {
|
|||||||
|
|
||||||
implementation "ru.noties.markwon:core:3.1.0"
|
implementation "ru.noties.markwon:core:3.1.0"
|
||||||
|
|
||||||
implementation "xyz.quaver:libpupil:1.7.2"
|
implementation "xyz.quaver:libpupil:1.8.16"
|
||||||
implementation "xyz.quaver:documentfilex:0.3"
|
implementation "xyz.quaver:documentfilex:0.4-alpha02"
|
||||||
implementation "xyz.quaver:floatingsearchview:1.0.7"
|
implementation "xyz.quaver:floatingsearchview:1.0.7"
|
||||||
|
|
||||||
testImplementation "junit:junit:4.13"
|
testImplementation "junit:junit:4.13.1"
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.2"
|
androidTestImplementation "androidx.test.ext:junit:1.1.2"
|
||||||
androidTestImplementation "androidx.test:rules:1.3.0"
|
androidTestImplementation "androidx.test:rules:1.3.0"
|
||||||
androidTestImplementation "androidx.test:runner:1.3.0"
|
androidTestImplementation "androidx.test:runner:1.3.0"
|
||||||
|
|||||||
16
app/proguard-rules.pro
vendored
16
app/proguard-rules.pro
vendored
@@ -22,21 +22,6 @@
|
|||||||
|
|
||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
|
|
||||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
|
||||||
-keep class * extends com.bumptech.glide.module.AppGlideModule {
|
|
||||||
<init>(...);
|
|
||||||
}
|
|
||||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
|
||||||
**[] $VALUES;
|
|
||||||
public *;
|
|
||||||
}
|
|
||||||
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
|
||||||
*** rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
|
||||||
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl
|
|
||||||
|
|
||||||
-keepattributes *Annotation*, InnerClasses
|
-keepattributes *Annotation*, InnerClasses
|
||||||
-dontnote kotlinx.serialization.SerializationKt
|
-dontnote kotlinx.serialization.SerializationKt
|
||||||
-keep,includedescriptorclasses class xyz.quaver.**$$serializer { *; } # <-- change package name to your app's
|
-keep,includedescriptorclasses class xyz.quaver.**$$serializer { *; } # <-- change package name to your app's
|
||||||
@@ -48,4 +33,3 @@
|
|||||||
}
|
}
|
||||||
-keep class xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment
|
-keep class xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment
|
||||||
-keep class xyz.quaver.pupil.ui.fragment.ManageStorageFragment
|
-keep class xyz.quaver.pupil.ui.fragment.ManageStorageFragment
|
||||||
-keep class xyz.quaver.pupil.util.Preferences
|
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"artifactType": {
|
"artifactType": {
|
||||||
"type": "APK",
|
"type": "APK",
|
||||||
"kind": "Directory"
|
"kind": "Directory"
|
||||||
},
|
},
|
||||||
"applicationId": "xyz.quaver.pupil",
|
"applicationId": "xyz.quaver.pupil",
|
||||||
"variantName": "release",
|
"variantName": "processReleaseResources",
|
||||||
"elements": [
|
"elements": [
|
||||||
{
|
{
|
||||||
"type": "SINGLE",
|
"type": "SINGLE",
|
||||||
"filters": [],
|
"filters": [],
|
||||||
"properties": [],
|
"versionCode": 64,
|
||||||
"versionCode": 61,
|
"versionName": "5.1.7-beta1",
|
||||||
"versionName": "5.1.2",
|
|
||||||
"enabled": true,
|
|
||||||
"outputFile": "app-release.apk"
|
"outputFile": "app-release.apk"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,10 +6,11 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<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" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="21"/>
|
<uses-permission-sdk-23 android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="21" />
|
<uses-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
|
|||||||
@@ -18,7 +18,10 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
import android.app.*
|
import android.app.Application
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|||||||
@@ -18,14 +18,15 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.adapters
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
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.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.cardview.widget.CardView
|
import android.widget.Toast
|
||||||
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
|
||||||
@@ -35,6 +36,7 @@ import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
|
|||||||
import com.daimajia.swipe.interfaces.SwipeAdapterInterface
|
import com.daimajia.swipe.interfaces.SwipeAdapterInterface
|
||||||
import com.github.piasy.biv.loader.ImageLoader
|
import com.github.piasy.biv.loader.ImageLoader
|
||||||
import kotlinx.android.synthetic.main.item_galleryblock.view.*
|
import kotlinx.android.synthetic.main.item_galleryblock.view.*
|
||||||
|
import kotlinx.android.synthetic.main.view_progress_card.view.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import xyz.quaver.hitomi.getGallery
|
import xyz.quaver.hitomi.getGallery
|
||||||
import xyz.quaver.hitomi.getReader
|
import xyz.quaver.hitomi.getReader
|
||||||
@@ -43,6 +45,7 @@ import xyz.quaver.pupil.R
|
|||||||
import xyz.quaver.pupil.favoriteTags
|
import xyz.quaver.pupil.favoriteTags
|
||||||
import xyz.quaver.pupil.favorites
|
import xyz.quaver.pupil.favorites
|
||||||
import xyz.quaver.pupil.types.Tag
|
import xyz.quaver.pupil.types.Tag
|
||||||
|
import xyz.quaver.pupil.ui.view.ProgressCard
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.Preferences
|
||||||
import xyz.quaver.pupil.util.downloader.Cache
|
import xyz.quaver.pupil.util.downloader.Cache
|
||||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
import xyz.quaver.pupil.util.downloader.DownloadManager
|
||||||
@@ -51,12 +54,6 @@ import java.io.File
|
|||||||
|
|
||||||
class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
|
class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
|
||||||
|
|
||||||
enum class ViewType {
|
|
||||||
NEXT,
|
|
||||||
GALLERY,
|
|
||||||
PREV
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateAll = true
|
var updateAll = true
|
||||||
var thin: Boolean = Preferences["thin"]
|
var thin: Boolean = Preferences["thin"]
|
||||||
|
|
||||||
@@ -72,60 +69,40 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateProgress(context: Context) {
|
private fun updateProgress(context: Context) = CoroutineScope(Dispatchers.Main).launch {
|
||||||
val cache = Cache.getInstance(context, galleryID)
|
with(view.galleryblock_card) {
|
||||||
|
val imageList = Cache.getInstance(context, galleryID).metadata.imageList
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
if (imageList == null) {
|
||||||
if (cache.metadata.reader == null) {
|
max = 0
|
||||||
view.galleryblock_progressbar_layout.visibility = View.GONE
|
return@with
|
||||||
view.galleryblock_progress_complete.visibility = View.INVISIBLE
|
|
||||||
return@launch
|
|
||||||
}
|
}
|
||||||
|
|
||||||
with(view.galleryblock_progressbar) {
|
|
||||||
val imageList = cache.metadata.imageList!!
|
|
||||||
|
|
||||||
progress = imageList.count { it != null }
|
progress = imageList.count { it != null }
|
||||||
max = imageList.size
|
max = imageList.size
|
||||||
|
|
||||||
with(view.galleryblock_progressbar_layout) {
|
view.galleryblock_id.setOnClickListener {
|
||||||
if (visibility == View.GONE)
|
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
|
||||||
visibility = View.VISIBLE
|
ClipData.newPlainText("gallery_id", galleryID.toString())
|
||||||
|
)
|
||||||
|
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!imageList.contains(null)) {
|
type = if (!imageList.contains(null)) {
|
||||||
val downloadManager = DownloadManager.getInstance(context)
|
val downloadManager = DownloadManager.getInstance(context)
|
||||||
|
|
||||||
if (completeFlag.get(galleryID, false)) {
|
if (downloadManager.getDownloadFolder(galleryID) == null)
|
||||||
with(view.galleryblock_progress_complete) {
|
ProgressCard.Type.CACHE
|
||||||
setImageResource(
|
else
|
||||||
if (downloadManager.getDownloadFolder(galleryID) != null)
|
ProgressCard.Type.DOWNLOAD
|
||||||
R.drawable.ic_progressbar
|
|
||||||
else R.drawable.ic_progressbar_cache
|
|
||||||
)
|
|
||||||
visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
with(view.galleryblock_progress_complete) {
|
|
||||||
setImageDrawable(AnimatedVectorDrawableCompat.create(context,
|
|
||||||
if (downloadManager.getDownloadFolder(galleryID) != null)
|
|
||||||
R.drawable.ic_progressbar_complete
|
|
||||||
else R.drawable.ic_progressbar_complete_cache
|
|
||||||
).apply {
|
|
||||||
this?.start()
|
|
||||||
})
|
|
||||||
visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
completeFlag.put(galleryID, true)
|
|
||||||
}
|
|
||||||
} else
|
} else
|
||||||
view.galleryblock_progress_complete.visibility = View.INVISIBLE
|
ProgressCard.Type.LOADING
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(galleryID: Int) {
|
fun bind(galleryID: Int) {
|
||||||
this.galleryID = galleryID
|
this.galleryID = galleryID
|
||||||
|
updateProgress(view.context)
|
||||||
|
|
||||||
val cache = Cache.getInstance(view.context, galleryID)
|
val cache = Cache.getInstance(view.context, galleryID)
|
||||||
|
|
||||||
@@ -228,6 +205,8 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
|
|||||||
}
|
}
|
||||||
|
|
||||||
tags.clear()
|
tags.clear()
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
tags.addAll(
|
tags.addAll(
|
||||||
galleryBlock.relatedTags.sortedBy {
|
galleryBlock.relatedTags.sortedBy {
|
||||||
val tag = Tag.parse(it)
|
val tag = Tag.parse(it)
|
||||||
@@ -245,8 +224,11 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
galleryblock_id.text = galleryBlock.id.toString()
|
galleryblock_id.text = galleryBlock.id.toString()
|
||||||
galleryblock_pagecount.text = "-"
|
galleryblock_pagecount.text = "-"
|
||||||
@@ -294,80 +276,38 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class NextViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)
|
|
||||||
class PrevViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)
|
|
||||||
|
|
||||||
class ViewHolderFactory {
|
|
||||||
companion object {
|
|
||||||
fun getLayoutID(type: Int): Int {
|
|
||||||
return when(ViewType.values()[type]) {
|
|
||||||
ViewType.NEXT -> R.layout.item_next
|
|
||||||
ViewType.PREV -> R.layout.item_prev
|
|
||||||
ViewType.GALLERY -> R.layout.item_galleryblock
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val completeFlag = SparseBooleanArray()
|
|
||||||
|
|
||||||
val onChipClickedHandler = ArrayList<((Tag) -> Unit)>()
|
val onChipClickedHandler = ArrayList<((Tag) -> Unit)>()
|
||||||
var onDownloadClickedHandler: ((Int) -> Unit)? = null
|
var onDownloadClickedHandler: ((Int) -> Unit)? = null
|
||||||
var onDeleteClickedHandler: ((Int) -> Unit)? = null
|
var onDeleteClickedHandler: ((Int) -> Unit)? = null
|
||||||
|
|
||||||
var showNext = false
|
|
||||||
var showPrev = false
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return GalleryViewHolder(
|
||||||
fun getViewHolder(type: Int, view: View): RecyclerView.ViewHolder {
|
LayoutInflater.from(parent.context).inflate(R.layout.item_galleryblock, parent, false)
|
||||||
return when(ViewType.values()[type]) {
|
|
||||||
ViewType.NEXT -> NextViewHolder(view as LinearLayout)
|
|
||||||
ViewType.PREV -> PrevViewHolder(view as LinearLayout)
|
|
||||||
ViewType.GALLERY -> GalleryViewHolder(view as CardView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getViewHolder(
|
|
||||||
viewType,
|
|
||||||
LayoutInflater.from(parent.context).inflate(
|
|
||||||
ViewHolderFactory.getLayoutID(viewType),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
if (holder is GalleryViewHolder) {
|
if (holder is GalleryViewHolder) {
|
||||||
val galleryID = galleries[position-(if (showPrev) 1 else 0)]
|
val galleryID = galleries[position]
|
||||||
|
|
||||||
holder.bind(galleryID)
|
holder.bind(galleryID)
|
||||||
|
|
||||||
with(holder.view.galleryblock_primary) {
|
holder.view.galleryblock_card.download.setOnClickListener {
|
||||||
setOnClickListener {
|
|
||||||
holder.view.performClick()
|
|
||||||
}
|
|
||||||
setOnLongClickListener {
|
|
||||||
holder.view.performLongClick()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.view.galleryblock_download.setOnClickListener {
|
|
||||||
onDownloadClickedHandler?.invoke(position)
|
onDownloadClickedHandler?.invoke(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.view.galleryblock_delete.setOnClickListener {
|
holder.view.galleryblock_card.delete.setOnClickListener {
|
||||||
onDeleteClickedHandler?.invoke(position)
|
onDeleteClickedHandler?.invoke(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
mItemManger.bindView(holder.view, position)
|
mItemManger.bindView(holder.view, position)
|
||||||
|
|
||||||
holder.view.galleryblock_swipe_layout.addSwipeListener(object: SwipeLayout.SwipeListener {
|
holder.view.galleryblock_card.swipe_layout.addSwipeListener(object: SwipeLayout.SwipeListener {
|
||||||
override fun onStartOpen(layout: SwipeLayout?) {
|
override fun onStartOpen(layout: SwipeLayout?) {
|
||||||
mItemManger.closeAllExcept(layout)
|
mItemManger.closeAllExcept(layout)
|
||||||
|
|
||||||
holder.view.galleryblock_download.text =
|
holder.view.galleryblock_card.download.text =
|
||||||
if (DownloadManager.getInstance(holder.view.context).isDownloading(galleryID))
|
if (DownloadManager.getInstance(holder.view.context).isDownloading(galleryID))
|
||||||
holder.view.context.getString(android.R.string.cancel)
|
holder.view.context.getString(android.R.string.cancel)
|
||||||
else
|
else
|
||||||
@@ -383,18 +323,7 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount() =
|
override fun getItemCount() = galleries.size
|
||||||
galleries.size +
|
|
||||||
(if (showNext) 1 else 0) +
|
|
||||||
(if (showPrev) 1 else 0)
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getSwipeLayoutResourceId(position: Int) = R.id.swipe_layout
|
||||||
return when {
|
|
||||||
showPrev && position == 0 -> ViewType.PREV
|
|
||||||
showNext && position == galleries.size+(if (showPrev) 1 else 0) -> ViewType.NEXT
|
|
||||||
else -> ViewType.GALLERY
|
|
||||||
}.ordinal
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSwipeLayoutResourceId(position: Int) = R.id.galleryblock_swipe_layout
|
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
package xyz.quaver.pupil.adapters
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.DiscretePathEffect
|
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ 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.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -37,10 +36,7 @@ import okhttp3.Callback
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import okio.*
|
import okio.*
|
||||||
import xyz.quaver.pupil.PupilInterceptor
|
import xyz.quaver.pupil.*
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.client
|
|
||||||
import xyz.quaver.pupil.interceptors
|
|
||||||
import xyz.quaver.pupil.ui.ReaderActivity
|
import xyz.quaver.pupil.ui.ReaderActivity
|
||||||
import xyz.quaver.pupil.util.cleanCache
|
import xyz.quaver.pupil.util.cleanCache
|
||||||
import xyz.quaver.pupil.util.downloader.Cache
|
import xyz.quaver.pupil.util.downloader.Cache
|
||||||
@@ -91,7 +87,7 @@ class DownloadService : Service() {
|
|||||||
PendingIntent.FLAG_UPDATE_CURRENT),
|
PendingIntent.FLAG_UPDATE_CURRENT),
|
||||||
).build()
|
).build()
|
||||||
|
|
||||||
notification.put(galleryID, NotificationCompat.Builder(this, "download").apply {
|
notification[galleryID] = 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_notification)
|
setSmallIcon(R.drawable.ic_notification)
|
||||||
@@ -99,7 +95,7 @@ class DownloadService : Service() {
|
|||||||
addAction(action)
|
addAction(action)
|
||||||
setProgress(0, 0, true)
|
setProgress(0, 0, true)
|
||||||
setOngoing(true)
|
setOngoing(true)
|
||||||
})
|
}
|
||||||
|
|
||||||
notify(galleryID)
|
notify(galleryID)
|
||||||
}
|
}
|
||||||
@@ -124,7 +120,7 @@ class DownloadService : Service() {
|
|||||||
.setProgress(max, progress, false)
|
.setProgress(max, progress, false)
|
||||||
.setContentText("$progress/$max")
|
.setContentText("$progress/$max")
|
||||||
|
|
||||||
if (DownloadManager.getInstance(this).getDownloadFolder(galleryID) != null)
|
if (DownloadManager.getInstance(this).getDownloadFolder(galleryID) != null || galleryID == priority)
|
||||||
notification.let { notificationManager.notify(galleryID, it.build()) }
|
notification.let { notificationManager.notify(galleryID, it.build()) }
|
||||||
else
|
else
|
||||||
notificationManager.cancel(galleryID)
|
notificationManager.cancel(galleryID)
|
||||||
@@ -199,12 +195,15 @@ class DownloadService : Service() {
|
|||||||
* Float.POSITIVE_INFINITY -> Download completed
|
* Float.POSITIVE_INFINITY -> Download completed
|
||||||
*/
|
*/
|
||||||
val progress = ConcurrentHashMap<Int, MutableList<Float>>()
|
val progress = ConcurrentHashMap<Int, MutableList<Float>>()
|
||||||
|
var priority = 0
|
||||||
|
|
||||||
fun isCompleted(galleryID: Int) = progress[galleryID]?.toList()?.all { it == Float.POSITIVE_INFINITY } == true
|
fun isCompleted(galleryID: Int) = progress[galleryID]?.toList()?.all { it == Float.POSITIVE_INFINITY } == true
|
||||||
|
|
||||||
private val callback = object: Callback {
|
private val callback = object: Callback {
|
||||||
|
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
|
||||||
if (e.message?.contains("cancel", true) == false) {
|
if (e.message?.contains("cancel", true) == false) {
|
||||||
val galleryID = (call.request().tag() as Tag).galleryID
|
val galleryID = (call.request().tag() as Tag).galleryID
|
||||||
|
|
||||||
@@ -237,6 +236,7 @@ class DownloadService : Service() {
|
|||||||
startId?.let { stopSelf(it) }
|
startId?.let { stopSelf(it) }
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
cancel(galleryID)
|
cancel(galleryID)
|
||||||
download(galleryID)
|
download(galleryID)
|
||||||
}
|
}
|
||||||
@@ -290,16 +290,16 @@ class DownloadService : Service() {
|
|||||||
fun delete(galleryID: Int, startId: Int? = null) = CoroutineScope(Dispatchers.IO).launch {
|
fun delete(galleryID: Int, startId: Int? = null) = CoroutineScope(Dispatchers.IO).launch {
|
||||||
cancel(galleryID)
|
cancel(galleryID)
|
||||||
DownloadManager.getInstance(this@DownloadService).deleteDownloadFolder(galleryID)
|
DownloadManager.getInstance(this@DownloadService).deleteDownloadFolder(galleryID)
|
||||||
Cache.delete(galleryID)
|
Cache.delete(this@DownloadService, galleryID)
|
||||||
|
|
||||||
startId?.let { stopSelf(it) }
|
startId?.let { stopSelf(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun download(galleryID: Int, priority: Boolean = false, startId: Int? = null): Job = CoroutineScope(Dispatchers.IO).launch {
|
fun download(galleryID: Int, priority: Boolean = false, startId: Int? = null): Job = CoroutineScope(Dispatchers.IO).launch {
|
||||||
cleanCache(this@DownloadService)
|
if (DownloadManager.getInstance(this@DownloadService).isDownloading(galleryID))
|
||||||
|
return@launch
|
||||||
|
|
||||||
if (progress.containsKey(galleryID))
|
cleanCache(this@DownloadService)
|
||||||
cancel(galleryID)
|
|
||||||
|
|
||||||
val cache = Cache.getInstance(this@DownloadService, galleryID)
|
val cache = Cache.getInstance(this@DownloadService, galleryID)
|
||||||
|
|
||||||
@@ -314,6 +314,8 @@ class DownloadService : Service() {
|
|||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
histories.add(galleryID)
|
||||||
|
|
||||||
progress[galleryID] = MutableList(reader.galleryInfo.files.size) { 0F }
|
progress[galleryID] = MutableList(reader.galleryInfo.files.size) { 0F }
|
||||||
|
|
||||||
cache.metadata.imageList?.let {
|
cache.metadata.imageList?.let {
|
||||||
|
|||||||
@@ -22,13 +22,18 @@ import kotlinx.android.parcel.IgnoredOnParcel
|
|||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||||
import xyz.quaver.hitomi.Suggestion
|
import xyz.quaver.hitomi.Suggestion
|
||||||
|
import xyz.quaver.pupil.util.translations
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion {
|
data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion {
|
||||||
constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n)
|
constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n)
|
||||||
|
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
override val body = s
|
override val body =
|
||||||
|
if (translations[s] != null)
|
||||||
|
"${translations[s]} ($s)"
|
||||||
|
else
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|||||||
@@ -26,15 +26,16 @@ import android.text.InputType
|
|||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.*
|
import android.view.animation.DecelerateInterpolator
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.core.text.util.LinkifyCompat
|
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
@@ -54,6 +55,8 @@ import xyz.quaver.pupil.services.DownloadService
|
|||||||
import xyz.quaver.pupil.types.*
|
import xyz.quaver.pupil.types.*
|
||||||
import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment
|
import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment
|
||||||
import xyz.quaver.pupil.ui.dialog.GalleryDialog
|
import xyz.quaver.pupil.ui.dialog.GalleryDialog
|
||||||
|
import xyz.quaver.pupil.ui.view.MainView
|
||||||
|
import xyz.quaver.pupil.ui.view.ProgressCard
|
||||||
import xyz.quaver.pupil.util.ItemClickSupport
|
import xyz.quaver.pupil.util.ItemClickSupport
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.Preferences
|
||||||
import xyz.quaver.pupil.util.checkUpdate
|
import xyz.quaver.pupil.util.checkUpdate
|
||||||
@@ -61,10 +64,7 @@ import xyz.quaver.pupil.util.downloader.Cache
|
|||||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
import xyz.quaver.pupil.util.downloader.DownloadManager
|
||||||
import xyz.quaver.pupil.util.restore
|
import xyz.quaver.pupil.util.restore
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.math.abs
|
import kotlin.math.*
|
||||||
import kotlin.math.ceil
|
|
||||||
import kotlin.math.min
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class MainActivity :
|
class MainActivity :
|
||||||
BaseActivity(),
|
BaseActivity(),
|
||||||
@@ -188,22 +188,22 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
var prevP1 = 0
|
main_recyclerview.addOnScrollListener(object: RecyclerView.OnScrollListener() {
|
||||||
main_appbar_layout.addOnOffsetChangedListener(
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
AppBarLayout.OnOffsetChangedListener { _, p1 ->
|
// -height of the search view < translationY < 0
|
||||||
main_searchview.translationY = p1.toFloat()
|
main_searchview.translationY =
|
||||||
main_recyclerview.scrollBy(0, prevP1 - p1)
|
min(
|
||||||
|
max(
|
||||||
|
main_searchview.translationY - dy,
|
||||||
|
-main_searchview.findViewById<CardView>(R.id.search_query_section).height.toFloat()
|
||||||
|
), 0F)
|
||||||
|
|
||||||
with(main_fab) {
|
if (dy > 0)
|
||||||
if (prevP1 > p1)
|
main_fab.hideMenuButton(true)
|
||||||
hideMenuButton(true)
|
else if (dy < 0)
|
||||||
else if (prevP1 < p1)
|
main_fab.showMenuButton(true)
|
||||||
showMenuButton(true)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
prevP1 = p1
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
Linkify.addLinks(main_noresult, Pattern.compile(getString(R.string.https_text)), null, null, { _, _ -> getString(R.string.https) })
|
Linkify.addLinks(main_noresult, Pattern.compile(getString(R.string.https_text)), null, null, { _, _ -> getString(R.string.https) })
|
||||||
|
|
||||||
@@ -308,6 +308,44 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
with(main_view) {
|
||||||
|
setOnPageTurnListener(object: MainView.OnPageTurnListener {
|
||||||
|
override fun onPrev(page: Int) {
|
||||||
|
currentPage--
|
||||||
|
|
||||||
|
// disable pageturn until the contents are loaded
|
||||||
|
setCurrentPage(1, false)
|
||||||
|
|
||||||
|
ViewCompat.animate(main_searchview)
|
||||||
|
.setDuration(100)
|
||||||
|
.setInterpolator(DecelerateInterpolator())
|
||||||
|
.translationY(0F)
|
||||||
|
|
||||||
|
cancelFetch()
|
||||||
|
clearGalleries()
|
||||||
|
fetchGalleries(query, sortMode)
|
||||||
|
loadBlocks()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNext(page: Int) {
|
||||||
|
currentPage++
|
||||||
|
|
||||||
|
// disable pageturn until the contents are loaded
|
||||||
|
setCurrentPage(1, false)
|
||||||
|
|
||||||
|
ViewCompat.animate(main_searchview)
|
||||||
|
.setDuration(100)
|
||||||
|
.setInterpolator(DecelerateInterpolator())
|
||||||
|
.translationY(0F)
|
||||||
|
|
||||||
|
cancelFetch()
|
||||||
|
clearGalleries()
|
||||||
|
fetchGalleries(query, sortMode)
|
||||||
|
loadBlocks()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
setupSearchBar()
|
setupSearchBar()
|
||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
fetchGalleries(query, sortMode)
|
fetchGalleries(query, sortMode)
|
||||||
@@ -357,14 +395,12 @@ class MainActivity :
|
|||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
completeFlag.put(galleryID, false)
|
|
||||||
|
|
||||||
closeAllItems()
|
closeAllItems()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ItemClickSupport.addTo(this).apply {
|
ItemClickSupport.addTo(this).apply {
|
||||||
onItemClickListener = listener@{ _, position, v ->
|
onItemClickListener = listener@{ _, position, v ->
|
||||||
if (v !is CardView)
|
if (v !is ProgressCard)
|
||||||
return@listener
|
return@listener
|
||||||
|
|
||||||
val intent = Intent(this@MainActivity, ReaderActivity::class.java)
|
val intent = Intent(this@MainActivity, ReaderActivity::class.java)
|
||||||
@@ -375,10 +411,10 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
onItemLongClickListener = listener@{ _, position, v ->
|
onItemLongClickListener = listener@{ _, position, v ->
|
||||||
if (v !is CardView)
|
if (v !is ProgressCard)
|
||||||
return@listener false
|
return@listener false
|
||||||
|
|
||||||
val galleryID = galleries[position]
|
val galleryID = galleries.getOrNull(position) ?: return@listener true
|
||||||
|
|
||||||
GalleryDialog(this@MainActivity, galleryID).apply {
|
GalleryDialog(this@MainActivity, galleryID).apply {
|
||||||
onChipClickedHandler.add {
|
onChipClickedHandler.add {
|
||||||
@@ -398,207 +434,6 @@ class MainActivity :
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var origin = 0f
|
|
||||||
var target = -1
|
|
||||||
val perPage = Preferences["per_page", "25"].toInt()
|
|
||||||
setOnTouchListener { _, event ->
|
|
||||||
when(event.action) {
|
|
||||||
MotionEvent.ACTION_UP -> {
|
|
||||||
origin = 0f
|
|
||||||
|
|
||||||
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
|
||||||
if(showPrev) {
|
|
||||||
showPrev = false
|
|
||||||
|
|
||||||
val prev = main_recyclerview.layoutManager?.getChildAt(0)
|
|
||||||
|
|
||||||
if (prev is LinearLayout) {
|
|
||||||
val icon = prev.findViewById<ImageView>(R.id.icon_prev)
|
|
||||||
prev.layoutParams.height = 1
|
|
||||||
icon.layoutParams.height = 1
|
|
||||||
icon.rotation = 180f
|
|
||||||
}
|
|
||||||
|
|
||||||
prev?.requestLayout()
|
|
||||||
|
|
||||||
notifyItemRemoved(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(showNext) {
|
|
||||||
showNext = false
|
|
||||||
|
|
||||||
val next = main_recyclerview.layoutManager?.let {
|
|
||||||
getChildAt(childCount-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next is LinearLayout) {
|
|
||||||
val icon = next.findViewById<ImageView>(R.id.icon_next)
|
|
||||||
next.layoutParams.height = 1
|
|
||||||
icon.layoutParams.height = 1
|
|
||||||
icon.rotation = 0f
|
|
||||||
}
|
|
||||||
|
|
||||||
next?.requestLayout()
|
|
||||||
|
|
||||||
notifyItemRemoved(itemCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target != -1) {
|
|
||||||
currentPage = target
|
|
||||||
|
|
||||||
runOnUiThread {
|
|
||||||
cancelFetch()
|
|
||||||
clearGalleries()
|
|
||||||
loadBlocks()
|
|
||||||
}
|
|
||||||
|
|
||||||
target = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MotionEvent.ACTION_DOWN -> origin = event.y
|
|
||||||
MotionEvent.ACTION_MOVE -> {
|
|
||||||
if (origin == 0f)
|
|
||||||
origin = event.y
|
|
||||||
|
|
||||||
val dist = event.y - origin
|
|
||||||
|
|
||||||
when {
|
|
||||||
!canScrollVertically(-1) -> {
|
|
||||||
//TOP
|
|
||||||
|
|
||||||
//Scrolling UP
|
|
||||||
if (dist > 0 && currentPage != 0) {
|
|
||||||
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
|
||||||
if(!showPrev) {
|
|
||||||
showPrev = true
|
|
||||||
notifyItemInserted(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val prev = main_recyclerview.layoutManager?.getChildAt(0)
|
|
||||||
|
|
||||||
if (prev is LinearLayout) {
|
|
||||||
val icon = prev.findViewById<ImageView>(R.id.icon_prev)
|
|
||||||
val text = prev.findViewById<TextView>(R.id.text_prev).apply {
|
|
||||||
text = getString(R.string.main_move, currentPage)
|
|
||||||
}
|
|
||||||
if (dist < 360) {
|
|
||||||
prev.layoutParams.height = (dist/2).roundToInt()
|
|
||||||
icon.layoutParams.height = (dist/2).roundToInt()
|
|
||||||
icon.rotation = dist+180
|
|
||||||
text.layoutParams.width = dist.roundToInt()
|
|
||||||
|
|
||||||
target = -1
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
prev.layoutParams.height = 180
|
|
||||||
icon.layoutParams.height = 180
|
|
||||||
icon.rotation = 180f
|
|
||||||
text.layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
|
|
||||||
target = currentPage-1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prev?.requestLayout()
|
|
||||||
|
|
||||||
return@setOnTouchListener true
|
|
||||||
} else {
|
|
||||||
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
|
||||||
if(showPrev) {
|
|
||||||
showPrev = false
|
|
||||||
|
|
||||||
val prev = main_recyclerview.layoutManager?.getChildAt(0)
|
|
||||||
|
|
||||||
if (prev is LinearLayout) {
|
|
||||||
val icon = prev.findViewById<ImageView>(R.id.icon_prev)
|
|
||||||
prev.layoutParams.height = 1
|
|
||||||
icon.layoutParams.height = 1
|
|
||||||
icon.rotation = 180f
|
|
||||||
}
|
|
||||||
|
|
||||||
prev?.requestLayout()
|
|
||||||
|
|
||||||
notifyItemRemoved(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
!canScrollVertically(1) -> {
|
|
||||||
//BOTTOM
|
|
||||||
|
|
||||||
//Scrolling DOWN
|
|
||||||
if (dist < 0 && currentPage != ceil(totalItems.toDouble()/perPage).roundToInt()-1) {
|
|
||||||
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
|
||||||
if(!showNext) {
|
|
||||||
showNext = true
|
|
||||||
notifyItemInserted(itemCount-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val next = main_recyclerview.layoutManager?.let {
|
|
||||||
getChildAt(childCount-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
val absDist = abs(dist)
|
|
||||||
|
|
||||||
if (next is LinearLayout) {
|
|
||||||
val icon = next.findViewById<ImageView>(R.id.icon_next)
|
|
||||||
val text = next.findViewById<TextView>(R.id.text_next).apply {
|
|
||||||
text = getString(R.string.main_move, currentPage+2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (absDist < 360) {
|
|
||||||
next.layoutParams.height = (absDist/2).roundToInt()
|
|
||||||
icon.layoutParams.height = (absDist/2).roundToInt()
|
|
||||||
icon.rotation = -absDist
|
|
||||||
text.layoutParams.width = absDist.roundToInt()
|
|
||||||
|
|
||||||
target = -1
|
|
||||||
} else {
|
|
||||||
next.layoutParams.height = 180
|
|
||||||
icon.layoutParams.height = 180
|
|
||||||
icon.rotation = 0f
|
|
||||||
text.layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
|
|
||||||
target = currentPage+1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next?.requestLayout()
|
|
||||||
|
|
||||||
return@setOnTouchListener true
|
|
||||||
} else {
|
|
||||||
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
|
||||||
if(showNext) {
|
|
||||||
showNext = false
|
|
||||||
|
|
||||||
val next = main_recyclerview.layoutManager?.let {
|
|
||||||
getChildAt(childCount-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next is LinearLayout) {
|
|
||||||
val icon = next.findViewById<ImageView>(R.id.icon_next)
|
|
||||||
next.layoutParams.height = 1
|
|
||||||
icon.layoutParams.height = 1
|
|
||||||
icon.rotation = 180f
|
|
||||||
}
|
|
||||||
|
|
||||||
next?.requestLayout()
|
|
||||||
|
|
||||||
notifyItemRemoved(itemCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,17 +662,15 @@ class MainActivity :
|
|||||||
loadingJob?.cancel()
|
loadingJob?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearGalleries() {
|
private fun clearGalleries() = CoroutineScope(Dispatchers.Main).launch {
|
||||||
galleries.clear()
|
galleries.clear()
|
||||||
|
|
||||||
with(main_recyclerview.adapter as GalleryBlockAdapter?) {
|
with(main_recyclerview.adapter as GalleryBlockAdapter?) {
|
||||||
this ?: return@with
|
this ?: return@with
|
||||||
|
|
||||||
this.completeFlag.clear()
|
|
||||||
this.notifyDataSetChanged()
|
this.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
main_appbar_layout.setExpanded(true)
|
|
||||||
main_noresult.visibility = View.INVISIBLE
|
main_noresult.visibility = View.INVISIBLE
|
||||||
main_progressbar.show()
|
main_progressbar.show()
|
||||||
}
|
}
|
||||||
@@ -932,7 +765,7 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}.toList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,6 +791,10 @@ class MainActivity :
|
|||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
main_view.setCurrentPage(currentPage + 1, galleryIDs.size > (currentPage+1)*perPage)
|
||||||
|
}
|
||||||
|
|
||||||
galleryIDs.slice(currentPage*perPage until min(currentPage*perPage+perPage, galleryIDs.size)).chunked(5).let { chunks ->
|
galleryIDs.slice(currentPage*perPage until min(currentPage*perPage+perPage, galleryIDs.size)).chunked(5).let { chunks ->
|
||||||
for (chunk in chunks)
|
for (chunk in chunks)
|
||||||
chunk.map { galleryID ->
|
chunk.map { galleryID ->
|
||||||
|
|||||||
@@ -33,11 +33,9 @@ import android.view.animation.Animation
|
|||||||
import android.view.animation.AnticipateInterpolator
|
import android.view.animation.AnticipateInterpolator
|
||||||
import android.view.animation.OvershootInterpolator
|
import android.view.animation.OvershootInterpolator
|
||||||
import android.view.animation.TranslateAnimation
|
import android.view.animation.TranslateAnimation
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.PagerSnapHelper
|
import androidx.recyclerview.widget.PagerSnapHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@@ -59,7 +57,6 @@ import xyz.quaver.Code
|
|||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.adapters.ReaderAdapter
|
import xyz.quaver.pupil.adapters.ReaderAdapter
|
||||||
import xyz.quaver.pupil.favorites
|
import xyz.quaver.pupil.favorites
|
||||||
import xyz.quaver.pupil.histories
|
|
||||||
import xyz.quaver.pupil.services.DownloadService
|
import xyz.quaver.pupil.services.DownloadService
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.Preferences
|
||||||
import xyz.quaver.pupil.util.camera
|
import xyz.quaver.pupil.util.camera
|
||||||
@@ -67,8 +64,6 @@ import xyz.quaver.pupil.util.closeCamera
|
|||||||
import xyz.quaver.pupil.util.downloader.Cache
|
import xyz.quaver.pupil.util.downloader.Cache
|
||||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
import xyz.quaver.pupil.util.downloader.DownloadManager
|
||||||
import xyz.quaver.pupil.util.startCamera
|
import xyz.quaver.pupil.util.startCamera
|
||||||
import java.util.*
|
|
||||||
import kotlin.concurrent.schedule
|
|
||||||
|
|
||||||
class ReaderActivity : BaseActivity() {
|
class ReaderActivity : BaseActivity() {
|
||||||
|
|
||||||
@@ -88,6 +83,8 @@ class ReaderActivity : BaseActivity() {
|
|||||||
private val conn = object: ServiceConnection {
|
private val conn = object: ServiceConnection {
|
||||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||||
downloader = (service as DownloadService.Binder).service.also {
|
downloader = (service as DownloadService.Binder).service.also {
|
||||||
|
it.priority = 0
|
||||||
|
|
||||||
if (!it.progress.containsKey(galleryID))
|
if (!it.progress.containsKey(galleryID))
|
||||||
DownloadService.download(this@ReaderActivity, galleryID, true)
|
DownloadService.download(this@ReaderActivity, galleryID, true)
|
||||||
}
|
}
|
||||||
@@ -230,14 +227,16 @@ class ReaderActivity : BaseActivity() {
|
|||||||
if (downloader != null)
|
if (downloader != null)
|
||||||
unbindService(conn)
|
unbindService(conn)
|
||||||
|
|
||||||
if (!DownloadManager.getInstance(this).isDownloading(galleryID))
|
downloader?.priority = galleryID
|
||||||
DownloadService.cancel(this, galleryID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|
||||||
update = false
|
update = false
|
||||||
|
|
||||||
|
if (!DownloadManager.getInstance(this).isDownloading(galleryID))
|
||||||
|
DownloadService.cancel(this, galleryID)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
@@ -259,7 +258,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
//currentPage is 1-based
|
//currentPage is 1-based
|
||||||
return when(keyCode) {
|
return when(keyCode) {
|
||||||
KeyEvent.KEYCODE_VOLUME_UP -> {
|
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||||
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(currentPage-2, 0)
|
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-2, 0)
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -292,8 +291,6 @@ class ReaderActivity : BaseActivity() {
|
|||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
histories.add(galleryID)
|
|
||||||
|
|
||||||
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
||||||
reader_download_progressbar.progress =
|
reader_download_progressbar.progress =
|
||||||
downloader.progress[galleryID]?.count { it.isInfinite() } ?: 0
|
downloader.progress[galleryID]?.count { it.isInfinite() } ?: 0
|
||||||
@@ -342,7 +339,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
scrollMode(false)
|
scrollMode(false)
|
||||||
fullscreen(true)
|
fullscreen(true)
|
||||||
} else {
|
} else {
|
||||||
(reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPosition(currentPage) //Moves to next page because currentPage is 1-based indexing
|
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage, 0) //Moves to next page because currentPage is 1-based indexing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,7 +359,6 @@ class ReaderActivity : BaseActivity() {
|
|||||||
return
|
return
|
||||||
currentPage = layoutManager.findFirstVisibleItemPosition()+1
|
currentPage = layoutManager.findFirstVisibleItemPosition()+1
|
||||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${recyclerView.adapter!!.itemCount}"
|
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${recyclerView.adapter!!.itemCount}"
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -458,7 +454,11 @@ class ReaderActivity : BaseActivity() {
|
|||||||
reader_recyclerview.layoutManager = LinearLayoutManager(this)
|
reader_recyclerview.layoutManager = LinearLayoutManager(this)
|
||||||
} else {
|
} else {
|
||||||
snapHelper.attachToRecyclerView(reader_recyclerview)
|
snapHelper.attachToRecyclerView(reader_recyclerview)
|
||||||
reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, Preferences["rtl", false])
|
reader_recyclerview.layoutManager = object: LinearLayoutManager(this, HORIZONTAL, Preferences["rtl", false]) {
|
||||||
|
override fun calculateExtraLayoutSpace(state: RecyclerView.State, extraLayoutSpace: IntArray) {
|
||||||
|
extraLayoutSpace.fill(600)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0)
|
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0)
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import xyz.quaver.pupil.R
|
|||||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||||
import xyz.quaver.pupil.adapters.ThumbnailPageAdapter
|
import xyz.quaver.pupil.adapters.ThumbnailPageAdapter
|
||||||
import xyz.quaver.pupil.favoriteTags
|
import xyz.quaver.pupil.favoriteTags
|
||||||
import xyz.quaver.pupil.histories
|
|
||||||
import xyz.quaver.pupil.types.Tag
|
import xyz.quaver.pupil.types.Tag
|
||||||
import xyz.quaver.pupil.ui.ReaderActivity
|
import xyz.quaver.pupil.ui.ReaderActivity
|
||||||
import xyz.quaver.pupil.ui.view.TagChip
|
import xyz.quaver.pupil.ui.view.TagChip
|
||||||
@@ -77,7 +76,6 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
|
|||||||
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||||
putExtra("galleryID", galleryID)
|
putExtra("galleryID", galleryID)
|
||||||
})
|
})
|
||||||
histories.add(galleryID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +236,6 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
|
|||||||
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||||
putExtra("galleryID", galleries[position])
|
putExtra("galleryID", galleries[position])
|
||||||
})
|
})
|
||||||
histories.add(galleries[position])
|
|
||||||
}
|
}
|
||||||
onItemLongClickListener = { _, position, _ ->
|
onItemLongClickListener = { _, position, _ ->
|
||||||
GalleryDialog(context, galleries[position]).apply {
|
GalleryDialog(context, galleries[position]).apply {
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ import android.os.Bundle
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.preference.Preference
|
import androidx.preference.*
|
||||||
import androidx.preference.PreferenceCategory
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
|
||||||
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import xyz.quaver.io.FileX
|
import xyz.quaver.io.FileX
|
||||||
import xyz.quaver.io.util.getChild
|
import xyz.quaver.io.util.getChild
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
@@ -37,6 +37,7 @@ import xyz.quaver.pupil.ui.SettingsActivity
|
|||||||
import xyz.quaver.pupil.ui.dialog.*
|
import xyz.quaver.pupil.ui.dialog.*
|
||||||
import xyz.quaver.pupil.util.*
|
import xyz.quaver.pupil.util.*
|
||||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
import xyz.quaver.pupil.util.downloader.DownloadManager
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class SettingsFragment :
|
class SettingsFragment :
|
||||||
PreferenceFragmentCompat(),
|
PreferenceFragmentCompat(),
|
||||||
@@ -109,7 +110,7 @@ class SettingsFragment :
|
|||||||
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
|
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
|
||||||
ClipData.newPlainText("user_id", Preferences.get<String>("user_id"))
|
ClipData.newPlainText("user_id", Preferences.get<String>("user_id"))
|
||||||
)
|
)
|
||||||
Toast.makeText(context, R.string.settings_user_id_toast, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
@@ -123,6 +124,9 @@ class SettingsFragment :
|
|||||||
this ?: return false
|
this ?: return false
|
||||||
|
|
||||||
when (key) {
|
when (key) {
|
||||||
|
"tag_translation" -> {
|
||||||
|
updateTranslations()
|
||||||
|
}
|
||||||
"nomedia" -> {
|
"nomedia" -> {
|
||||||
val create = (newValue as? Boolean) ?: return false
|
val create = (newValue as? Boolean) ?: return false
|
||||||
|
|
||||||
@@ -243,6 +247,27 @@ class SettingsFragment :
|
|||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
|
"tag_translation" -> {
|
||||||
|
this as ListPreference
|
||||||
|
|
||||||
|
isEnabled = false
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
kotlin.runCatching {
|
||||||
|
val languages = getAvailableLanguages().distinct().toTypedArray()
|
||||||
|
|
||||||
|
entries = languages.map { Locale(it).let { loc -> loc.getDisplayLanguage(loc) } }.toTypedArray()
|
||||||
|
entryValues = languages
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreferenceChangeListener = this@SettingsFragment
|
||||||
|
|
||||||
|
}
|
||||||
"mirrors" -> {
|
"mirrors" -> {
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import android.content.Context
|
|||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.PorterDuffColorFilter
|
import android.graphics.PorterDuffColorFilter
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
import android.os.Parcelable
|
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@@ -38,8 +37,6 @@ import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
|||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||||
import xyz.quaver.floatingsearchview.FloatingSearchView
|
import xyz.quaver.floatingsearchview.FloatingSearchView
|
||||||
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||||
import xyz.quaver.floatingsearchview.util.MenuPopupHelper
|
|
||||||
import xyz.quaver.floatingsearchview.util.view.MenuView
|
|
||||||
import xyz.quaver.floatingsearchview.util.view.SearchInputView
|
import xyz.quaver.floatingsearchview.util.view.SearchInputView
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.favoriteTags
|
import xyz.quaver.pupil.favoriteTags
|
||||||
@@ -57,7 +54,7 @@ class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: Attr
|
|||||||
var onFavoriteHistorySwitchClickListener: (() -> Unit)? = null
|
var onFavoriteHistorySwitchClickListener: (() -> Unit)? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
searchInputView.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
|
searchInputView.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI or searchInputView.imeOptions
|
||||||
|
|
||||||
searchInputView.addTextChangedListener(this)
|
searchInputView.addTextChangedListener(this)
|
||||||
onSearchListener = this
|
onSearchListener = this
|
||||||
@@ -164,9 +161,7 @@ class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: Attr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.t == -1) {
|
if (item.t > 0) {
|
||||||
textView?.text = item.s
|
|
||||||
} else {
|
|
||||||
(suggestionView as? LinearLayout)?.let {
|
(suggestionView as? LinearLayout)?.let {
|
||||||
val count = it.findViewById<TextView>(R.id.count)
|
val count = it.findViewById<TextView>(R.id.count)
|
||||||
if (count == null)
|
if (count == null)
|
||||||
|
|||||||
462
app/src/main/java/xyz/quaver/pupil/ui/view/MainView.java
Normal file
462
app/src/main/java/xyz/quaver/pupil/ui/view/MainView.java
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
/*
|
||||||
|
* Pupil, Hitomi.la viewer for Android
|
||||||
|
* Copyright (C) 2020 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.view;
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.DecelerateInterpolator;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.view.NestedScrollingChild;
|
||||||
|
import androidx.core.view.NestedScrollingChildHelper;
|
||||||
|
import androidx.core.view.NestedScrollingParent;
|
||||||
|
import androidx.core.view.NestedScrollingParentHelper;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.widget.TextViewCompat;
|
||||||
|
|
||||||
|
import xyz.quaver.pupil.R;
|
||||||
|
|
||||||
|
@SuppressWarnings("NullableProblems")
|
||||||
|
public class MainView extends ViewGroup implements NestedScrollingChild, NestedScrollingParent {
|
||||||
|
|
||||||
|
private static final int PAGE_TURN_LAYOUT_SIZE = 48;
|
||||||
|
private static final int PAGE_TURN_ANIM_DURATION = 500;
|
||||||
|
private static final int PREV_OFFSET = 64;
|
||||||
|
private static final int RIPPLE_GIVE = 4;
|
||||||
|
|
||||||
|
private final float adjustedPageTurnLayoutSize;
|
||||||
|
private final float adjustedPrevOffset;
|
||||||
|
private final float adjustedRippleGive;
|
||||||
|
|
||||||
|
final private NestedScrollingParentHelper mNestedScrollingParentHelper;
|
||||||
|
final private NestedScrollingChildHelper mNestedScrollingChildHelper;
|
||||||
|
|
||||||
|
final private Vibrator mVibrator;
|
||||||
|
|
||||||
|
private View mTarget;
|
||||||
|
|
||||||
|
private TextView mPrev;
|
||||||
|
private TextView mNext;
|
||||||
|
|
||||||
|
private final Paint mRipplePaint = new Paint();
|
||||||
|
private final Rect mRippleBound = new Rect();
|
||||||
|
|
||||||
|
private int mRippleSize = 0;
|
||||||
|
private final int mRippleTargetSize;
|
||||||
|
private final ValueAnimator mRippleAnimator = new ValueAnimator();
|
||||||
|
|
||||||
|
private int mCurrentOverScroll = 0;
|
||||||
|
|
||||||
|
private int mCurrentPage = 1;
|
||||||
|
private boolean mShowPrev;
|
||||||
|
private boolean mShowNext;
|
||||||
|
|
||||||
|
private OnPageTurnListener mOnPageTurnListener;
|
||||||
|
|
||||||
|
public MainView(@NonNull Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MainView(@NonNull Context context, AttributeSet attr) {
|
||||||
|
this(context, attr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MainView(@NonNull Context context, AttributeSet attr, int defStyle) {
|
||||||
|
super(context, attr, defStyle);
|
||||||
|
|
||||||
|
setWillNotDraw(false);
|
||||||
|
|
||||||
|
DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||||
|
|
||||||
|
adjustedPageTurnLayoutSize = PAGE_TURN_LAYOUT_SIZE * metrics.density;
|
||||||
|
adjustedPrevOffset = PREV_OFFSET * metrics.density;
|
||||||
|
adjustedRippleGive = RIPPLE_GIVE * metrics.density;
|
||||||
|
|
||||||
|
mRippleTargetSize = metrics.widthPixels;
|
||||||
|
|
||||||
|
mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
|
||||||
|
mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
|
||||||
|
|
||||||
|
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
|
||||||
|
mRippleAnimator.addUpdateListener(animation -> {
|
||||||
|
mRippleSize = (int) animation.getAnimatedValue();
|
||||||
|
invalidate();
|
||||||
|
});
|
||||||
|
mRippleAnimator.setDuration(PAGE_TURN_ANIM_DURATION);
|
||||||
|
|
||||||
|
initPageTurnView();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentPage(int currentPage, boolean showNext) {
|
||||||
|
mCurrentPage = currentPage;
|
||||||
|
|
||||||
|
mShowPrev = currentPage > 1;
|
||||||
|
mShowNext = showNext;
|
||||||
|
|
||||||
|
mPrev.setText(getContext().getString(R.string.main_move_to_page, mCurrentPage-1));
|
||||||
|
mNext.setText(getContext().getString(R.string.main_move_to_page, mCurrentPage+1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnPageTurnListener(OnPageTurnListener listener) {
|
||||||
|
mOnPageTurnListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPageTurnView() {
|
||||||
|
TextView prev = new TextView(getContext());
|
||||||
|
TextView next = new TextView(getContext());
|
||||||
|
|
||||||
|
prev.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
next.setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
|
||||||
|
prev.setCompoundDrawablesWithIntrinsicBounds(R.drawable.navigate_prev, 0, 0, 0);
|
||||||
|
next.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.navigate_next, 0);
|
||||||
|
|
||||||
|
TextViewCompat.setCompoundDrawableTintList(prev, AppCompatResources.getColorStateList(getContext(), R.color.colorAccent));
|
||||||
|
TextViewCompat.setCompoundDrawableTintList(next, AppCompatResources.getColorStateList(getContext(), R.color.colorAccent));
|
||||||
|
|
||||||
|
prev.setVisibility(View.INVISIBLE);
|
||||||
|
next.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
|
mPrev = prev;
|
||||||
|
mNext = next;
|
||||||
|
|
||||||
|
addView(mPrev);
|
||||||
|
addView(mNext);
|
||||||
|
|
||||||
|
setCurrentPage(1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureTarget() {
|
||||||
|
if (mTarget == null) {
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
View child = getChildAt(i);
|
||||||
|
|
||||||
|
if (!child.equals(mNext) && !child.equals(mPrev)) {
|
||||||
|
mTarget = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
final int width = getMeasuredWidth();
|
||||||
|
final int height = getMeasuredHeight();
|
||||||
|
|
||||||
|
if (getChildCount() == 0)
|
||||||
|
return;
|
||||||
|
if (mTarget == null)
|
||||||
|
ensureTarget();
|
||||||
|
if (mTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mTarget.layout(
|
||||||
|
getPaddingLeft(),
|
||||||
|
getPaddingTop(),
|
||||||
|
width - getPaddingRight(),
|
||||||
|
height - getPaddingBottom()
|
||||||
|
);
|
||||||
|
|
||||||
|
final int prevWidth = mPrev.getMeasuredWidth();
|
||||||
|
mPrev.layout(
|
||||||
|
width / 2 - prevWidth / 2,
|
||||||
|
getPaddingTop() + (int) adjustedPrevOffset,
|
||||||
|
width / 2 + prevWidth / 2,
|
||||||
|
getPaddingTop() + (int) adjustedPrevOffset + mPrev.getMeasuredHeight()
|
||||||
|
);
|
||||||
|
|
||||||
|
final int nextWidth = mNext.getMeasuredWidth();
|
||||||
|
mNext.layout(
|
||||||
|
width / 2 - nextWidth / 2,
|
||||||
|
height - getPaddingBottom() - mNext.getMeasuredHeight(),
|
||||||
|
width / 2 + nextWidth / 2,
|
||||||
|
height - getPaddingBottom()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
if (mTarget == null)
|
||||||
|
ensureTarget();
|
||||||
|
if (mTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mTarget.measure(
|
||||||
|
MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY)
|
||||||
|
);
|
||||||
|
|
||||||
|
mPrev.measure(
|
||||||
|
MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST),
|
||||||
|
MeasureSpec.makeMeasureSpec((int) adjustedPageTurnLayoutSize, MeasureSpec.EXACTLY)
|
||||||
|
);
|
||||||
|
|
||||||
|
mNext.measure(
|
||||||
|
MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST),
|
||||||
|
MeasureSpec.makeMeasureSpec((int) adjustedPageTurnLayoutSize, MeasureSpec.EXACTLY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
if (mCurrentOverScroll == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mCurrentOverScroll > 0) {
|
||||||
|
mRippleBound.set(
|
||||||
|
getPaddingLeft(),
|
||||||
|
(int) (getPaddingTop() - adjustedRippleGive),
|
||||||
|
getMeasuredWidth() - getPaddingRight(),
|
||||||
|
(int) (getPaddingTop() + adjustedPrevOffset + mPrev.getMeasuredHeight() + adjustedRippleGive)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCurrentOverScroll < 0) {
|
||||||
|
final int height = getMeasuredHeight();
|
||||||
|
mRippleBound.set(
|
||||||
|
getPaddingLeft(),
|
||||||
|
(int) (height - getPaddingBottom() - mNext.getMeasuredHeight() - adjustedRippleGive),
|
||||||
|
getMeasuredWidth() - getPaddingRight(),
|
||||||
|
height - getPaddingBottom()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
mRipplePaint.reset();
|
||||||
|
mRipplePaint.setStyle(Paint.Style.FILL);
|
||||||
|
|
||||||
|
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||||
|
|
||||||
|
switch (currentNightMode) {
|
||||||
|
case Configuration.UI_MODE_NIGHT_YES:
|
||||||
|
mRipplePaint.setColor(ContextCompat.getColor(getContext(), R.color.material_light_blue_700));
|
||||||
|
break;
|
||||||
|
case Configuration.UI_MODE_NIGHT_NO:
|
||||||
|
mRipplePaint.setColor(ContextCompat.getColor(getContext(), R.color.material_light_blue_300));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawCircle(
|
||||||
|
(mRippleBound.left + mRippleBound.right) / 2F,
|
||||||
|
mCurrentOverScroll > 0 ? mRippleBound.bottom : mRippleBound.top,
|
||||||
|
mRippleSize,
|
||||||
|
mRipplePaint
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onOverscroll(int overscroll) {
|
||||||
|
if (mTarget == null)
|
||||||
|
ensureTarget();
|
||||||
|
if (mTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCurrentOverScroll = overscroll;
|
||||||
|
|
||||||
|
if (overscroll > 0) {
|
||||||
|
mPrev.setVisibility(View.VISIBLE);
|
||||||
|
mNext.setVisibility(View.INVISIBLE);
|
||||||
|
} else if (overscroll < 0) {
|
||||||
|
mPrev.setVisibility(View.INVISIBLE);
|
||||||
|
mNext.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
mPrev.setVisibility(View.INVISIBLE);
|
||||||
|
mNext.setVisibility(View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(overscroll) >= adjustedPageTurnLayoutSize) {
|
||||||
|
if (!mRippleAnimator.isStarted() && mRippleSize != mRippleTargetSize) {
|
||||||
|
mVibrator.vibrate(10);
|
||||||
|
|
||||||
|
mRippleAnimator.setIntValues(mRippleSize, mRippleTargetSize);
|
||||||
|
mRippleAnimator.start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!mRippleAnimator.isStarted() && mRippleSize != 0) {
|
||||||
|
mRippleAnimator.setIntValues(mRippleSize, 0);
|
||||||
|
mRippleAnimator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float clippedOverScrollTop = (overscroll > 0 ? 1 : -1) * Math.min(Math.abs(overscroll), adjustedPageTurnLayoutSize);
|
||||||
|
mTarget.setTranslationY(clippedOverScrollTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onOverscrollEnd(int overscroll) {
|
||||||
|
if (mTarget == null)
|
||||||
|
ensureTarget();
|
||||||
|
if (mTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mRippleAnimator.cancel();
|
||||||
|
mRippleAnimator.setIntValues(mRippleSize, 0);
|
||||||
|
mRippleAnimator.start();
|
||||||
|
|
||||||
|
mPrev.setVisibility(View.INVISIBLE);
|
||||||
|
mNext.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
|
ViewCompat.animate(mTarget)
|
||||||
|
.setDuration(PAGE_TURN_ANIM_DURATION)
|
||||||
|
.setInterpolator(new DecelerateInterpolator())
|
||||||
|
.translationY(0);
|
||||||
|
|
||||||
|
if (Math.abs(overscroll) > adjustedPageTurnLayoutSize && mOnPageTurnListener != null) {
|
||||||
|
if (overscroll > 0)
|
||||||
|
mOnPageTurnListener.onPrev(mCurrentPage-1);
|
||||||
|
if (overscroll < 0)
|
||||||
|
mOnPageTurnListener.onNext(mCurrentPage+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NestedScrollingParent
|
||||||
|
|
||||||
|
private int mTotalUnconsumed = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
|
||||||
|
return isEnabled() && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNestedScrollAccepted(View child, View target, int axes) {
|
||||||
|
mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
|
||||||
|
startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL);
|
||||||
|
|
||||||
|
mTotalUnconsumed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
|
||||||
|
if (mTotalUnconsumed != 0 && dy > 0 == mTotalUnconsumed > 0) {
|
||||||
|
if (Math.abs(dy) > Math.abs(mTotalUnconsumed)) {
|
||||||
|
consumed[1] = dy - mTotalUnconsumed;
|
||||||
|
mTotalUnconsumed = 0;
|
||||||
|
} else {
|
||||||
|
mTotalUnconsumed -= dy;
|
||||||
|
consumed[1] = dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
onOverscroll(mTotalUnconsumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int[] parentConsumed = new int[2];
|
||||||
|
if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {
|
||||||
|
consumed[0] += parentConsumed[0];
|
||||||
|
consumed[1] += parentConsumed[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
|
||||||
|
final int[] mParentOffsetInWindow = new int[2];
|
||||||
|
dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, mParentOffsetInWindow);
|
||||||
|
|
||||||
|
final int dy = dyUnconsumed + mParentOffsetInWindow[1];
|
||||||
|
|
||||||
|
if (mTotalUnconsumed == 0 && ((dy < 0 && !mShowPrev) || (dy > 0 && !mShowNext)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (dy != 0) {
|
||||||
|
mTotalUnconsumed -= dy;
|
||||||
|
onOverscroll(mTotalUnconsumed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopNestedScroll(View child) {
|
||||||
|
mNestedScrollingParentHelper.onStopNestedScroll(child);
|
||||||
|
|
||||||
|
if (Math.abs(mTotalUnconsumed) > 0) {
|
||||||
|
onOverscrollEnd(mTotalUnconsumed);
|
||||||
|
mTotalUnconsumed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopNestedScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NestedScrollingChild
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNestedScrollingEnabled(boolean enabled) {
|
||||||
|
mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNestedScrollingEnabled() {
|
||||||
|
return mNestedScrollingChildHelper.isNestedScrollingEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startNestedScroll(int axes) {
|
||||||
|
return mNestedScrollingChildHelper.startNestedScroll(axes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopNestedScroll() {
|
||||||
|
mNestedScrollingChildHelper.stopNestedScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNestedScrollingParent() {
|
||||||
|
return mNestedScrollingChildHelper.hasNestedScrollingParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) {
|
||||||
|
return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow) {
|
||||||
|
return mNestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
|
||||||
|
return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
|
||||||
|
return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnPageTurnListener {
|
||||||
|
void onPrev(int page);
|
||||||
|
void onNext(int page);
|
||||||
|
}
|
||||||
|
}
|
||||||
72
app/src/main/java/xyz/quaver/pupil/ui/view/ProgressCard.kt
Normal file
72
app/src/main/java/xyz/quaver/pupil/ui/view/ProgressCard.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package xyz.quaver.pupil.ui.view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.cardview.widget.CardView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
import kotlinx.android.synthetic.main.view_progress_card.view.*
|
||||||
|
import xyz.quaver.pupil.R
|
||||||
|
|
||||||
|
class ProgressCard @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, defStyle: Int = R.attr.cardViewStyle) : CardView(context, attr, defStyle) {
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
LOADING,
|
||||||
|
CACHE,
|
||||||
|
DOWNLOAD
|
||||||
|
}
|
||||||
|
|
||||||
|
var type: Type = Type.LOADING
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
|
||||||
|
when (field) {
|
||||||
|
Type.LOADING -> R.color.colorAccent
|
||||||
|
Type.CACHE -> R.color.material_blue_700
|
||||||
|
Type.DOWNLOAD -> R.color.material_green_a700
|
||||||
|
}.let {
|
||||||
|
val color = ContextCompat.getColor(context, it)
|
||||||
|
DrawableCompat.setTint(progressbar.progressDrawable, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var progress: Int
|
||||||
|
get() = progressbar?.progress ?: 0
|
||||||
|
set(value) {
|
||||||
|
progressbar?.progress = value
|
||||||
|
}
|
||||||
|
var max: Int
|
||||||
|
get() = progressbar?.max ?: 0
|
||||||
|
set(value) {
|
||||||
|
progressbar?.max = value
|
||||||
|
|
||||||
|
progressbar.visibility =
|
||||||
|
if (value == 0)
|
||||||
|
GONE
|
||||||
|
else
|
||||||
|
VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
inflate(context, R.layout.view_progress_card, this)
|
||||||
|
|
||||||
|
content.setOnClickListener {
|
||||||
|
performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
content.setOnLongClickListener {
|
||||||
|
performLongClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
|
||||||
|
if (childCount == 0)
|
||||||
|
super.addView(child, index, params)
|
||||||
|
else
|
||||||
|
content.addView(child, index, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import com.google.android.material.chip.Chip
|
|||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.favoriteTags
|
import xyz.quaver.pupil.favoriteTags
|
||||||
import xyz.quaver.pupil.types.Tag
|
import xyz.quaver.pupil.types.Tag
|
||||||
|
import xyz.quaver.pupil.util.translations
|
||||||
import xyz.quaver.pupil.util.wordCapitalize
|
import xyz.quaver.pupil.util.wordCapitalize
|
||||||
|
|
||||||
@SuppressLint("ViewConstructor")
|
@SuppressLint("ViewConstructor")
|
||||||
@@ -90,7 +91,7 @@ class TagChip(context: Context, _tag: Tag) : Chip(context) {
|
|||||||
|
|
||||||
text = when (tag.area) {
|
text = when (tag.area) {
|
||||||
"language" -> languages[tag.tag]
|
"language" -> languages[tag.tag]
|
||||||
else -> tag.tag.wordCapitalize()
|
else -> (translations[tag.tag] ?: tag.tag).wordCapitalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
setEnsureMinTouchTargetSize(false)
|
setEnsureMinTouchTargetSize(false)
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ package xyz.quaver.pupil.ui.view
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.TypedArray
|
import android.content.res.TypedArray
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import android.util.Log
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.chip.ChipGroup
|
import com.google.android.material.chip.ChipGroup
|
||||||
|
import kotlinx.coroutines.*
|
||||||
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.types.Tags
|
import xyz.quaver.pupil.types.Tags
|
||||||
@@ -30,7 +32,7 @@ import xyz.quaver.pupil.types.Tags
|
|||||||
class TagChipGroup @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, attrStyle: Int = R.attr.chipGroupStyle, val tags: Tags = Tags()) : ChipGroup(context, attr, attrStyle), MutableSet<Tag> by tags {
|
class TagChipGroup @JvmOverloads constructor(context: Context, attr: AttributeSet? = null, attrStyle: Int = R.attr.chipGroupStyle, val tags: Tags = Tags()) : ChipGroup(context, attr, attrStyle), MutableSet<Tag> by tags {
|
||||||
|
|
||||||
object Defaults {
|
object Defaults {
|
||||||
val maxChipSize = 10
|
const val maxChipSize = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxChipSize: Int = Defaults.maxChipSize
|
var maxChipSize: Int = Defaults.maxChipSize
|
||||||
@@ -66,20 +68,28 @@ class TagChipGroup @JvmOverloads constructor(context: Context, attr: AttributeSe
|
|||||||
maxChipSize = attr.getInt(R.styleable.TagChipGroup_maxTag, Defaults.maxChipSize)
|
maxChipSize = attr.getInt(R.styleable.TagChipGroup_maxTag, Defaults.maxChipSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var refreshJob: Job? = null
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
|
refreshJob?.cancel()
|
||||||
this.removeAllViews()
|
this.removeAllViews()
|
||||||
|
|
||||||
tags.take(maxChipSize).forEach {
|
refreshJob = CoroutineScope(Dispatchers.Main).launch {
|
||||||
this.addView(TagChip(context, it).apply {
|
tags.take(maxChipSize).map {
|
||||||
|
CoroutineScope(Dispatchers.Default).async {
|
||||||
|
TagChip(context, it).apply {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
onClickListener?.invoke(this.tag)
|
onClickListener?.invoke(this.tag)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
addView(it.await())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxChipSize > 0 && this.size > maxChipSize)
|
if (maxChipSize > 0 && tags.size > maxChipSize)
|
||||||
addView(moreView)
|
addView(moreView)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
applyAttributes(context.obtainStyledAttributes(attr, R.styleable.TagChipGroup))
|
applyAttributes(context.obtainStyledAttributes(attr, R.styleable.TagChipGroup))
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import kotlinx.serialization.*
|
|||||||
import kotlinx.serialization.builtins.ListSerializer
|
import kotlinx.serialization.builtins.ListSerializer
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class SavedSet <T: Any> (private val file: File, private val any: T, private val set: MutableSet<T> = mutableSetOf()) : MutableSet<T> by set {
|
class SavedSet <T: Any> (private val file: File, private val any: T, private val set: MutableSet<T> = mutableSetOf()) : MutableSet<T> by set {
|
||||||
|
|
||||||
@@ -38,8 +39,8 @@ class SavedSet <T: Any> (private val file: File, private val any: T, private val
|
|||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun load() {
|
fun load() {
|
||||||
synchronized(this) {
|
|
||||||
set.clear()
|
set.clear()
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
Json.decodeFromString(serializer, file.readText())
|
Json.decodeFromString(serializer, file.readText())
|
||||||
@@ -47,15 +48,14 @@ class SavedSet <T: Any> (private val file: File, private val any: T, private val
|
|||||||
set.addAll(it)
|
set.addAll(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
fun save() {
|
fun save() {
|
||||||
synchronized(this) {
|
|
||||||
file.writeText(Json.encodeToString(serializer, set.toList()))
|
file.writeText(Json.encodeToString(serializer, set.toList()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
override fun add(element: T): Boolean {
|
override fun add(element: T): Boolean {
|
||||||
load()
|
load()
|
||||||
|
|
||||||
@@ -66,6 +66,7 @@ class SavedSet <T: Any> (private val file: File, private val any: T, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
override fun addAll(elements: Collection<T>): Boolean {
|
override fun addAll(elements: Collection<T>): Boolean {
|
||||||
load()
|
load()
|
||||||
|
|
||||||
@@ -76,6 +77,7 @@ class SavedSet <T: Any> (private val file: File, private val any: T, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
override fun remove(element: T): Boolean {
|
override fun remove(element: T): Boolean {
|
||||||
load()
|
load()
|
||||||
|
|
||||||
@@ -84,6 +86,7 @@ class SavedSet <T: Any> (private val file: File, private val any: T, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
override fun clear() {
|
override fun clear() {
|
||||||
set.clear()
|
set.clear()
|
||||||
save()
|
save()
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.graphics.Paint
|
|
||||||
import android.text.style.LineHeightSpan
|
|
||||||
|
|
||||||
class SetLineOverlap(private val overlap: Boolean) : LineHeightSpan {
|
|
||||||
companion object {
|
|
||||||
private var originalBottom = 15
|
|
||||||
private var originalDescent = 13
|
|
||||||
private var overlapSaved = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chooseHeight(
|
|
||||||
text: CharSequence?,
|
|
||||||
start: Int,
|
|
||||||
end: Int,
|
|
||||||
spanstartv: Int,
|
|
||||||
lineHeight: Int,
|
|
||||||
fm: Paint.FontMetricsInt?
|
|
||||||
) {
|
|
||||||
fm ?: return
|
|
||||||
|
|
||||||
if (overlap) {
|
|
||||||
if (overlapSaved) {
|
|
||||||
originalBottom = fm.bottom
|
|
||||||
originalDescent = fm.descent
|
|
||||||
overlapSaved = true
|
|
||||||
}
|
|
||||||
fm.bottom += fm.top
|
|
||||||
fm.descent += fm.top
|
|
||||||
} else {
|
|
||||||
fm.bottom = originalBottom
|
|
||||||
fm.descent = originalDescent
|
|
||||||
overlapSaved = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,8 @@ import android.net.Uri
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
@@ -37,6 +39,7 @@ import xyz.quaver.io.FileX
|
|||||||
import xyz.quaver.io.util.*
|
import xyz.quaver.io.util.*
|
||||||
import xyz.quaver.pupil.client
|
import xyz.quaver.pupil.client
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.Preferences
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
@@ -60,8 +63,8 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun delete(galleryID: Int) {
|
fun delete(context: Context, galleryID: Int) {
|
||||||
instances[galleryID]?.cacheFolder?.deleteRecursively()
|
File(context.cacheDir, "imageCache/$galleryID").deleteRecursively()
|
||||||
instances.remove(galleryID)
|
instances.remove(galleryID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +145,12 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
|
|||||||
|
|
||||||
client.newCall(request).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.bytes() }
|
client.newCall(request).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.bytes() }
|
||||||
}.getOrNull()?.let { thumbnail -> kotlin.runCatching {
|
}.getOrNull()?.let { thumbnail -> kotlin.runCatching {
|
||||||
cacheFolder.getChild(".thumbnail").also { it.writeBytes(thumbnail) }
|
cacheFolder.getChild(".thumbnail").also {
|
||||||
|
if (!it.exists())
|
||||||
|
it.createNewFile()
|
||||||
|
|
||||||
|
it.writeBytes(thumbnail)
|
||||||
|
}
|
||||||
}.getOrNull()?.uri }
|
}.getOrNull()?.uri }
|
||||||
} } ?: Uri.EMPTY
|
} } ?: Uri.EMPTY
|
||||||
|
|
||||||
@@ -198,34 +206,39 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
|
|||||||
setMetadata { metadata -> metadata.imageList!![index] = fileName }
|
setMetadata { metadata -> metadata.imageList!![index] = fileName }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val lock = ConcurrentHashMap<Int, Mutex>()
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch {
|
fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch {
|
||||||
val downloadFolder = downloadFolder ?: return@launch
|
val downloadFolder = downloadFolder ?: return@launch
|
||||||
|
|
||||||
|
if (lock[galleryID]?.isLocked == true)
|
||||||
|
return@launch
|
||||||
|
|
||||||
|
(lock[galleryID] ?: Mutex().also { lock[galleryID] = it }).withLock {
|
||||||
val cacheMetadata = cacheFolder.getChild(".metadata")
|
val cacheMetadata = cacheFolder.getChild(".metadata")
|
||||||
val downloadMetadata = downloadFolder.getChild(".metadata")
|
val downloadMetadata = downloadFolder.getChild(".metadata")
|
||||||
|
|
||||||
if (downloadMetadata.exists() || !cacheMetadata.exists())
|
if (!cacheMetadata.exists())
|
||||||
return@launch
|
return@launch
|
||||||
|
|
||||||
if (cacheMetadata.exists()) {
|
if (cacheMetadata.exists()) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
|
if (!downloadMetadata.exists())
|
||||||
downloadMetadata.createNewFile()
|
downloadMetadata.createNewFile()
|
||||||
downloadMetadata.writeText(Json.encodeToString(metadata))
|
|
||||||
|
|
||||||
cacheMetadata.delete()
|
downloadMetadata.writeText(Json.encodeToString(metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cacheThumbnail = cacheFolder.getChild(".thumbnail")
|
val cacheThumbnail = cacheFolder.getChild(".thumbnail")
|
||||||
val downloadThumbnail = downloadFolder.getChild(".thumbnail")
|
val downloadThumbnail = downloadFolder.getChild(".thumbnail")
|
||||||
|
|
||||||
if (cacheThumbnail.exists() && !downloadThumbnail.exists()) {
|
if (cacheThumbnail.exists()) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
if (!downloadThumbnail.exists())
|
if (!downloadThumbnail.exists())
|
||||||
downloadThumbnail.createNewFile()
|
downloadThumbnail.createNewFile()
|
||||||
|
|
||||||
downloadThumbnail.outputStream()?.use { target -> cacheThumbnail.inputStream()?.use { source ->
|
downloadThumbnail.outputStream()?.use { target -> target.channel.truncate(0L); cacheThumbnail.inputStream()?.use { source ->
|
||||||
source.copyTo(target)
|
source.copyTo(target)
|
||||||
} }
|
} }
|
||||||
cacheThumbnail.delete()
|
cacheThumbnail.delete()
|
||||||
@@ -237,17 +250,20 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
|
|||||||
val target = downloadFolder.getChild(imageName)
|
val target = downloadFolder.getChild(imageName)
|
||||||
val source = cacheFolder.getChild(imageName)
|
val source = cacheFolder.getChild(imageName)
|
||||||
|
|
||||||
if (!source.exists() || target.exists())
|
if (!source.exists())
|
||||||
return@forEach
|
return@forEach
|
||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
if (!target.exists())
|
if (!target.exists())
|
||||||
target.createNewFile()
|
target.createNewFile()
|
||||||
|
|
||||||
target.outputStream()?.use { target -> source.inputStream()?.use { source ->
|
target.outputStream()?.use { target -> target.channel.truncate(0L); source.inputStream()?.use { source ->
|
||||||
source.copyTo(target)
|
source.copyTo(target)
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheFolder.deleteRecursively()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
|
|||||||
}.invoke()
|
}.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
return downloadFolderMapInstance!!
|
return downloadFolderMapInstance ?: mutableMapOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,22 +37,6 @@ fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch {
|
|||||||
val cacheFolder = File(context.cacheDir, "imageCache")
|
val cacheFolder = File(context.cacheDir, "imageCache")
|
||||||
val downloadManager = DownloadManager.getInstance(context)
|
val downloadManager = DownloadManager.getInstance(context)
|
||||||
|
|
||||||
cacheFolder.listFiles { file ->
|
|
||||||
val galleryID = file.name.toIntOrNull() ?: return@listFiles true
|
|
||||||
|
|
||||||
!(downloadManager.downloadFolderMap.containsKey(galleryID) || histories.contains(galleryID))
|
|
||||||
}?.forEach {
|
|
||||||
it.deleteRecursively()
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadManager.getInstance(context).downloadFolderMap.keys.forEach {
|
|
||||||
val folder = File(cacheFolder, it.toString())
|
|
||||||
|
|
||||||
if (!downloadManager.isDownloading(it) && folder.exists()) {
|
|
||||||
folder.deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val limit = (Preferences.get<String>("cache_limit").toLongOrNull() ?: 0L)*1024*1024*1024
|
val limit = (Preferences.get<String>("cache_limit").toLongOrNull() ?: 0L)*1024*1024*1024
|
||||||
|
|
||||||
if (limit == 0L) return@withLock
|
if (limit == 0L) return@withLock
|
||||||
@@ -71,10 +55,12 @@ fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch {
|
|||||||
while (cacheSize.invoke() > limit/2) {
|
while (cacheSize.invoke() > limit/2) {
|
||||||
val caches = cacheFolder.list() ?: return@withLock
|
val caches = cacheFolder.list() ?: return@withLock
|
||||||
|
|
||||||
|
synchronized(histories) {
|
||||||
(histories.firstOrNull {
|
(histories.firstOrNull {
|
||||||
caches.contains(it.toString()) && !downloadManager.isDownloading(it)
|
caches.contains(it.toString()) && !downloadManager.isDownloading(it)
|
||||||
} ?: return@withLock).let {
|
} ?: return@withLock).let {
|
||||||
Cache.delete(it)
|
Cache.delete(context, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import xyz.quaver.Code
|
import xyz.quaver.Code
|
||||||
@@ -93,14 +94,14 @@ fun GalleryBlock.formatDownloadFolder(): String =
|
|||||||
formatMap.entries.fold(it) { str, (k, v) ->
|
formatMap.entries.fold(it) { str, (k, v) ->
|
||||||
str.replace(k, v.invoke(this), true)
|
str.replace(k, v.invoke(this), true)
|
||||||
}
|
}
|
||||||
}.replace(Regex("""[*\\|"?><:/]"""), "")
|
}.replace(Regex("""[*\\|"?><:/]"""), "").ellipsize(127)
|
||||||
|
|
||||||
fun GalleryBlock.formatDownloadFolderTest(format: String): String =
|
fun GalleryBlock.formatDownloadFolderTest(format: String): String =
|
||||||
format.let {
|
format.let {
|
||||||
formatMap.entries.fold(it) { str, (k, v) ->
|
formatMap.entries.fold(it) { str, (k, v) ->
|
||||||
str.replace(k, v.invoke(this), true)
|
str.replace(k, v.invoke(this), true)
|
||||||
}
|
}
|
||||||
}.replace(Regex("""[*\\|"?><:/]"""), "")
|
}.replace(Regex("""[*\\|"?><:/]"""), "").ellipsize(127)
|
||||||
|
|
||||||
val Reader.requestBuilders: List<Request.Builder>
|
val Reader.requestBuilders: List<Request.Builder>
|
||||||
get() {
|
get() {
|
||||||
@@ -129,3 +130,12 @@ fun String.ellipsize(n: Int): String =
|
|||||||
this.slice(0 until n) + "…"
|
this.slice(0 until n) + "…"
|
||||||
else
|
else
|
||||||
this
|
this
|
||||||
|
|
||||||
|
operator fun JsonElement.get(index: Int) =
|
||||||
|
this.jsonArray[index]
|
||||||
|
|
||||||
|
operator fun JsonElement.get(tag: String) =
|
||||||
|
this.jsonObject[tag]
|
||||||
|
|
||||||
|
val JsonElement.content
|
||||||
|
get() = this.jsonPrimitive.contentOrNull
|
||||||
68
app/src/main/java/xyz/quaver/pupil/util/translation.kt
Normal file
68
app/src/main/java/xyz/quaver/pupil/util/translation.kt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Pupil, Hitomi.la viewer for Android
|
||||||
|
* Copyright (C) 2020 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 kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import okhttp3.Request
|
||||||
|
import xyz.quaver.pupil.client
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
private val filesURL = "https://api.github.com/repos/tom5079/Pupil/git/trees/tags"
|
||||||
|
private val contentURL = "https://raw.githubusercontent.com/tom5079/Pupil/tags/"
|
||||||
|
|
||||||
|
var translations: Map<String, String> = run {
|
||||||
|
updateTranslations()
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
|
fun updateTranslations() = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
translations = emptyMap()
|
||||||
|
kotlin.runCatching {
|
||||||
|
translations = Json.decodeFromString<Map<String, String>>(client.newCall(
|
||||||
|
Request.Builder()
|
||||||
|
.url(contentURL + "${Preferences["tag_translation", ""].let { if (it.isEmpty()) Locale.getDefault().language else it }}.json")
|
||||||
|
.build()
|
||||||
|
).execute().also { if (it.code() != 200) return@launch }.body()?.use { it.string() } ?: return@launch).filterValues { it.isNotEmpty() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAvailableLanguages(): List<String> {
|
||||||
|
val languages = Locale.getISOLanguages()
|
||||||
|
|
||||||
|
val json = Json.parseToJsonElement(client.newCall(
|
||||||
|
Request.Builder()
|
||||||
|
.url(filesURL)
|
||||||
|
.build()
|
||||||
|
).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.string() } ?: return emptyList())
|
||||||
|
|
||||||
|
return listOf("en") + (json["tree"]?.jsonArray?.mapNotNull {
|
||||||
|
val name = it["path"]?.jsonPrimitive?.content?.takeWhile { c -> c != '.' }
|
||||||
|
|
||||||
|
languages.firstOrNull { code -> code.equals(name, ignoreCase = true) }
|
||||||
|
} ?: emptyList())
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -256,6 +257,13 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
|
|||||||
|
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
job = CoroutineScope(Dispatchers.IO).launch {
|
job = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val images = listOf(
|
||||||
|
"jpg",
|
||||||
|
"png",
|
||||||
|
"gif",
|
||||||
|
"webp"
|
||||||
|
)
|
||||||
|
|
||||||
val downloadFolders = downloadFolder.listFiles { folder ->
|
val downloadFolders = downloadFolder.listFiles { folder ->
|
||||||
folder.isDirectory && !downloadFolderMap.values.contains(folder.name)
|
folder.isDirectory && !downloadFolderMap.values.contains(folder.name)
|
||||||
}?.map {
|
}?.map {
|
||||||
@@ -273,19 +281,28 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
|
|||||||
.setProgress(index, downloadFolders.size, false)
|
.setProgress(index, downloadFolders.size, false)
|
||||||
notificationManager.notify(R.id.notification_id_import, notification.build())
|
notificationManager.notify(R.id.notification_id_import, notification.build())
|
||||||
|
|
||||||
kotlin.runCatching {
|
|
||||||
val metadata = kotlin.runCatching {
|
val metadata = kotlin.runCatching {
|
||||||
folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it).jsonObject }
|
folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it) }
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
|
|
||||||
val galleryID = folder.name.toIntOrNull() ?: return@runCatching
|
val galleryID = metadata?.get("reader")?.get("galleryInfo")?.get("id")?.content?.toIntOrNull()
|
||||||
|
?: folder.name.toIntOrNull() ?: return@forEachIndexed
|
||||||
|
|
||||||
val galleryBlock: GalleryBlock? = kotlin.runCatching {
|
val galleryBlock: GalleryBlock? = kotlin.runCatching {
|
||||||
metadata?.get("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
|
metadata?.get("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
|
||||||
}.getOrNull() ?: getGalleryBlock(galleryID)
|
}.getOrNull() ?: kotlin.runCatching {
|
||||||
|
getGalleryBlock(galleryID)
|
||||||
|
}.getOrNull() ?: kotlin.runCatching {
|
||||||
|
xyz.quaver.hiyobi.getGalleryBlock(galleryID)
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
val reader: Reader? = kotlin.runCatching {
|
val reader: Reader? = kotlin.runCatching {
|
||||||
metadata?.get("reader")?.let { Json.decodeFromJsonElement<Reader>(it) }
|
metadata?.get("reader")?.let { Json.decodeFromJsonElement<Reader>(it) }
|
||||||
}.getOrNull() ?: getReader(galleryID)
|
}.getOrNull() ?: kotlin.runCatching {
|
||||||
|
getReader(galleryID)
|
||||||
|
}.getOrNull() ?: kotlin.runCatching {
|
||||||
|
xyz.quaver.hiyobi.getReader(galleryID)
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
metadata?.get("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail ->
|
metadata?.get("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail ->
|
||||||
val file = folder.getChild(".thumbnail").also {
|
val file = folder.getChild(".thumbnail").also {
|
||||||
@@ -300,26 +317,21 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
|
|||||||
val list: MutableList<String?> =
|
val list: MutableList<String?> =
|
||||||
MutableList(reader!!.galleryInfo.files.size) { null }
|
MutableList(reader!!.galleryInfo.files.size) { null }
|
||||||
|
|
||||||
folder.listFiles { file ->
|
folder.list { _, name ->
|
||||||
file?.nameWithoutExtension?.let {
|
name?.substringAfterLast('.') in images
|
||||||
Regex("""\d{5}""").matches(it) && it.toIntOrNull() != null
|
}?.sorted()?.take(list.size)?.forEachIndexed { i, name ->
|
||||||
} == true
|
list[i] = name
|
||||||
}?.forEach {
|
|
||||||
list[it.nameWithoutExtension.toInt()] = it.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
folder.getChild(".metadata").also { if (it.exists()) it.delete(); it.createNewFile() }.writeText(
|
folder.getChild(".metadata").also { if (it.exists()) it.delete(); it.createNewFile() }.writeText(
|
||||||
Json.encodeToString(Metadata(galleryBlock, reader, list))
|
Json.encodeToString(Metadata(galleryBlock, reader, list))
|
||||||
)
|
)
|
||||||
|
|
||||||
synchronized(Cache) {
|
Cache.delete(this@migrate, galleryID)
|
||||||
Cache.delete(galleryID)
|
|
||||||
}
|
|
||||||
downloadFolderMap[galleryID] = folder.name
|
downloadFolderMap[galleryID] = folder.name
|
||||||
|
|
||||||
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile(); it.writeText(Json.encodeToString(downloadFolderMap)) }
|
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile(); it.writeText(Json.encodeToString(downloadFolderMap)) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
notification
|
notification
|
||||||
.setContentText(getText(R.string.import_old_galleries_notification_done))
|
.setContentText(getText(R.string.import_old_galleries_notification_done))
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
|
||||||
<path android:fillColor="#FF000000" android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
<path android:fillColor="#FF000000" android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
||||||
</vector>
|
</vector>
|
||||||
6
app/src/main/res/drawable/navigate_prev.xml
Normal file
6
app/src/main/res/drawable/navigate_prev.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
|
||||||
|
<group android:pivotX="12" android:scaleX="-1">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
@@ -17,36 +17,18 @@
|
|||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
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="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.MainActivity">
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<xyz.quaver.pupil.ui.view.MainView
|
||||||
|
android:id="@+id/main_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/main_appbar_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:visibility="invisible"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="64dp"
|
|
||||||
android:visibility="invisible"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
|
<com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -70,6 +52,8 @@
|
|||||||
|
|
||||||
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
|
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
|
||||||
|
|
||||||
|
</xyz.quaver.pupil.ui.view.MainView>
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
style="?android:attr/progressBarStyle"
|
style="?android:attr/progressBarStyle"
|
||||||
android:id="@+id/main_progressbar"
|
android:id="@+id/main_progressbar"
|
||||||
@@ -83,7 +67,6 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/main_no_result"
|
android:text="@string/main_no_result"
|
||||||
android:linksClickable="true"
|
android:linksClickable="true"
|
||||||
android:visibility="invisible"/>
|
android:visibility="invisible"/>
|
||||||
@@ -126,8 +109,6 @@
|
|||||||
|
|
||||||
</com.github.clans.fab.FloatingActionMenu>
|
</com.github.clans.fab.FloatingActionMenu>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
<xyz.quaver.pupil.ui.view.FloatingSearchView
|
<xyz.quaver.pupil.ui.view.FloatingSearchView
|
||||||
android:id="@+id/main_searchview"
|
android:id="@+id/main_searchview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -141,7 +122,6 @@
|
|||||||
app:leftActionMode="showHamburger"
|
app:leftActionMode="showHamburger"
|
||||||
app:menu="@menu/main"
|
app:menu="@menu/main"
|
||||||
app:dismissOnOutsideTouch="true"
|
app:dismissOnOutsideTouch="true"
|
||||||
app:close_search_on_keyboard_dismiss="false"
|
app:close_search_on_keyboard_dismiss="false" />
|
||||||
tools:ignore="NewApi" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -17,95 +17,23 @@
|
|||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<xyz.quaver.pupil.ui.view.ProgressCard
|
||||||
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"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/galleryblock_card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
|
||||||
app:cardCornerRadius="8dp"
|
|
||||||
android:clipChildren="true"
|
android:clipChildren="true"
|
||||||
|
app:cardCornerRadius="4dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
tools:ignore="RtlHardcoded">
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
<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: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="?android:attr/selectableItemBackground"
|
|
||||||
android:focusable="true"
|
|
||||||
android:clickable="true"
|
|
||||||
tools:ignore="UnusedAttribute" />
|
|
||||||
|
|
||||||
<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="?android:attr/selectableItemBackground"
|
|
||||||
android:focusable="true"
|
|
||||||
android:clickable="true"
|
|
||||||
tools:ignore="UnusedAttribute" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/galleryblock_primary"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:focusable="true"
|
|
||||||
android:clickable="true">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/galleryblock_progressbar_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="4dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:id="@+id/galleryblock_progressbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="-4dp"
|
|
||||||
android:layout_marginTop="-4dp"
|
|
||||||
android:progress="50"
|
|
||||||
android:layout_gravity="center_vertical"/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/galleryblock_progress_complete"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="4dp"
|
|
||||||
android:visibility="invisible"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:contentDescription="@string/reader_imageview_description"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<com.github.piasy.biv.view.BigImageView
|
<com.github.piasy.biv.view.BigImageView
|
||||||
android:id="@+id/galleryblock_thumbnail"
|
android:id="@+id/galleryblock_thumbnail"
|
||||||
@@ -113,10 +41,11 @@
|
|||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:contentDescription="@string/galleryblock_thumbnail_description"
|
android:contentDescription="@string/galleryblock_thumbnail_description"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
|
android:clickable="false"
|
||||||
app:layout_constraintHeight_default="spread"
|
app:layout_constraintHeight_default="spread"
|
||||||
app:layout_constraintHeight_min="200dp"
|
app:layout_constraintHeight_min="200dp"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/galleryblock_progressbar_layout"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@id/barrier"/>
|
app:layout_constraintBottom_toTopOf="@id/barrier"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -128,7 +57,7 @@
|
|||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
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_progressbar_layout"/>
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/TextAppearance.AppCompat.Medium"
|
style="@style/TextAppearance.AppCompat.Medium"
|
||||||
@@ -138,7 +67,7 @@
|
|||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
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" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/galleryblock_series"
|
android:id="@+id/galleryblock_series"
|
||||||
@@ -197,9 +126,7 @@
|
|||||||
android:id="@+id/galleryblock_id"
|
android:id="@+id/galleryblock_id"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_margin="8dp"
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/divider"
|
app:layout_constraintTop_toBottomOf="@id/divider"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"/>
|
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||||
@@ -212,8 +139,8 @@
|
|||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/divider"
|
app:layout_constraintTop_toBottomOf="@id/divider"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toRightOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toLeftOf="parent" />
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/galleryblock_favorite"
|
android:id="@+id/galleryblock_favorite"
|
||||||
@@ -230,6 +157,4 @@
|
|||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</com.daimajia.swipe.SwipeLayout>
|
</xyz.quaver.pupil.ui.view.ProgressCard>
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?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:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/icon_next"
|
|
||||||
android:contentDescription="@string/page_indicator_placeholder"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:srcCompat="@drawable/ic_navigate_next_black_24dp"
|
|
||||||
app:tint="@color/colorAccent"
|
|
||||||
android:rotation="180"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_next"
|
|
||||||
android:layout_width="1dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:ellipsize="end" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?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:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/icon_prev"
|
|
||||||
android:contentDescription="@string/page_indicator_placeholder"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:srcCompat="@drawable/ic_navigate_next_black_24dp"
|
|
||||||
app:tint="@color/colorAccent"
|
|
||||||
android:rotation="180"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_prev"
|
|
||||||
android:layout_width="1dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:ellipsize="end" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
75
app/src/main/res/layout/view_progress_card.xml
Normal file
75
app/src/main/res/layout/view_progress_card.xml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<merge
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:parentTag="androidx.cardview.widget.CardView">
|
||||||
|
|
||||||
|
<com.daimajia.swipe.SwipeLayout
|
||||||
|
android:id="@+id/swipe_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:drag_edge="right"
|
||||||
|
app:show_mode="pull_out">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/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="?android:attr/selectableItemBackground"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true"
|
||||||
|
tools:ignore="UnusedAttribute" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/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="?android:attr/selectableItemBackground"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true"
|
||||||
|
tools:ignore="UnusedAttribute" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:id="@+id/progressbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:progress="50"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.daimajia.swipe.SwipeLayout>
|
||||||
|
|
||||||
|
</merge>
|
||||||
30
app/src/main/res/layout/view_swipe_pageturn.xml
Normal file
30
app/src/main/res/layout/view_swipe_pageturn.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:parentTag="android.widget.FrameLayout">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/prev"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top|center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
app:drawableStartCompat="@drawable/navigate_prev"
|
||||||
|
app:drawableLeftCompat="@drawable/navigate_prev"
|
||||||
|
app:drawableTint="@color/colorAccent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/next"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
app:drawableEndCompat="@drawable/navigate_next"
|
||||||
|
app:drawableRightCompat="@drawable/navigate_next"
|
||||||
|
app:drawableTint="@color/colorAccent" />
|
||||||
|
|
||||||
|
</merge>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<string name="main_jump_title">ページ移動</string>
|
<string name="main_jump_title">ページ移動</string>
|
||||||
<string name="main_jump_message">現ページ番号: %1$d\nページ数: %2$d</string>
|
<string name="main_jump_message">現ページ番号: %1$d\nページ数: %2$d</string>
|
||||||
<string name="unable_to_connect">hitomi.laに接続できません</string>
|
<string name="unable_to_connect">hitomi.laに接続できません</string>
|
||||||
<string name="main_move">%1$dページへ移動</string>
|
<string name="main_move_to_page">%1$dページへ移動</string>
|
||||||
<string name="settings_clear_downloads">ダウンロード削除</string>
|
<string name="settings_clear_downloads">ダウンロード削除</string>
|
||||||
<string name="settings_clear_downloads_alert_message">ダウンロードしたギャラリーを全て削除します。\n実行しますか?</string>
|
<string name="settings_clear_downloads_alert_message">ダウンロードしたギャラリーを全て削除します。\n実行しますか?</string>
|
||||||
<string name="settings_mirror_summary">ミラーサーバからイメージをロード</string>
|
<string name="settings_mirror_summary">ミラーサーバからイメージをロード</string>
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
<string name="settings_lock_fingerprint_prompt_subtitle">こうかはばつぐんだ!</string>
|
<string name="settings_lock_fingerprint_prompt_subtitle">こうかはばつぐんだ!</string>
|
||||||
<string name="default_query_dialog_filter_loli">登場人物を全て18歳以上にする</string>
|
<string name="default_query_dialog_filter_loli">登場人物を全て18歳以上にする</string>
|
||||||
<string name="settings_user_id">ユーザーID</string>
|
<string name="settings_user_id">ユーザーID</string>
|
||||||
<string name="settings_user_id_toast">ユーザーIDをクリップボードにコピーしました</string>
|
<string name="copied_to_clipboard">クリップボードにコピーしました</string>
|
||||||
<string name="reader_fab_retry">リトライ</string>
|
<string name="reader_fab_retry">リトライ</string>
|
||||||
<string name="reader_fab_auto">まばたき検知スクロール</string>
|
<string name="reader_fab_auto">まばたき検知スクロール</string>
|
||||||
<string name="search_all">全てのギャラリーを対象に検索</string>
|
<string name="search_all">全てのギャラリーを対象に検索</string>
|
||||||
@@ -152,4 +152,7 @@
|
|||||||
<string name="error">エラー</string>
|
<string name="error">エラー</string>
|
||||||
<string name="settings_cache_limit">キャッシュサイズ制限</string>
|
<string name="settings_cache_limit">キャッシュサイズ制限</string>
|
||||||
<string name="settings_cache_unlimited">制限なし</string>
|
<string name="settings_cache_unlimited">制限なし</string>
|
||||||
|
<string name="settings_tag_translation">タグ言語</string>
|
||||||
|
<string name="settings_tag_translation_message">Githubにて翻訳に参加できます</string>
|
||||||
|
<string name="settings_concurrent_download">並列ダウンロード</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<string name="main_jump_title">페이지 이동</string>
|
<string name="main_jump_title">페이지 이동</string>
|
||||||
<string name="main_jump_message">현재 페이지: %1$d\n페이지 수: %2$d</string>
|
<string name="main_jump_message">현재 페이지: %1$d\n페이지 수: %2$d</string>
|
||||||
<string name="unable_to_connect">hitomi.la에 연결할 수 없습니다</string>
|
<string name="unable_to_connect">hitomi.la에 연결할 수 없습니다</string>
|
||||||
<string name="main_move">%1$d 페이지로 이동</string>
|
<string name="main_move_to_page">%1$d 페이지로 이동</string>
|
||||||
<string name="settings_clear_downloads">다운로드 삭제</string>
|
<string name="settings_clear_downloads">다운로드 삭제</string>
|
||||||
<string name="settings_clear_downloads_alert_message">다운로드 된 만화를 모두 삭제합니다.\n계속하시겠습니까?</string>
|
<string name="settings_clear_downloads_alert_message">다운로드 된 만화를 모두 삭제합니다.\n계속하시겠습니까?</string>
|
||||||
<string name="main_drawer_favorite">즐겨찾기</string>
|
<string name="main_drawer_favorite">즐겨찾기</string>
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
<string name="settings_lock_fingerprint_prompt_subtitle">힘세고 강한 지문 인식</string>
|
<string name="settings_lock_fingerprint_prompt_subtitle">힘세고 강한 지문 인식</string>
|
||||||
<string name="default_query_dialog_filter_loli">판사님 저는 페도가 아닙니다</string>
|
<string name="default_query_dialog_filter_loli">판사님 저는 페도가 아닙니다</string>
|
||||||
<string name="settings_user_id">유저 ID</string>
|
<string name="settings_user_id">유저 ID</string>
|
||||||
<string name="settings_user_id_toast">유저 ID를 클립보드에 복사했습니다</string>
|
<string name="copied_to_clipboard">클립보드에 복사됨</string>
|
||||||
<string name="reader_fab_retry">재시도</string>
|
<string name="reader_fab_retry">재시도</string>
|
||||||
<string name="reader_fab_auto">눈 깜빡임 감지 스크롤</string>
|
<string name="reader_fab_auto">눈 깜빡임 감지 스크롤</string>
|
||||||
<string name="search_all">모든 갤러리 검색</string>
|
<string name="search_all">모든 갤러리 검색</string>
|
||||||
@@ -152,4 +152,7 @@
|
|||||||
<string name="error">오류</string>
|
<string name="error">오류</string>
|
||||||
<string name="settings_cache_limit">캐시 크기 제한</string>
|
<string name="settings_cache_limit">캐시 크기 제한</string>
|
||||||
<string name="settings_cache_unlimited">무제한</string>
|
<string name="settings_cache_unlimited">무제한</string>
|
||||||
|
<string name="settings_tag_translation">태그 언어</string>
|
||||||
|
<string name="settings_tag_translation_message">Github에서 번역에 참여하세요</string>
|
||||||
|
<string name="settings_concurrent_download">병렬 다운로드</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<string-array name="settings_galleries_per_page">
|
<string-array name="settings_galleries_per_page">
|
||||||
|
<item>5</item>
|
||||||
<item>10</item>
|
<item>10</item>
|
||||||
<item>25</item>
|
<item>25</item>
|
||||||
<item>50</item>
|
<item>50</item>
|
||||||
|
|||||||
@@ -21,4 +21,11 @@
|
|||||||
<declare-styleable name="TagChipGroup">
|
<declare-styleable name="TagChipGroup">
|
||||||
<attr name="maxTag" format="integer"/>
|
<attr name="maxTag" format="integer"/>
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="RippleCircleStatus">
|
||||||
|
<attr name="half" format="enum">
|
||||||
|
<enum name="top" value="1"/>
|
||||||
|
<enum name="bottom" value="-1"/>
|
||||||
|
</attr>
|
||||||
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
<color name="colorPrimaryDark">#0093c4</color>
|
<color name="colorPrimaryDark">#0093c4</color>
|
||||||
<color name="colorAccent">#D81B60</color>
|
<color name="colorAccent">#D81B60</color>
|
||||||
|
|
||||||
|
<color name="material_light_blue_300">#4fc3f7</color>
|
||||||
|
<color name="material_light_blue_700">#0288d1</color>
|
||||||
<color name="material_pink_600">#d81b60</color>
|
<color name="material_pink_600">#d81b60</color>
|
||||||
<color name="material_blue_700">#1976d2</color>
|
<color name="material_blue_700">#1976d2</color>
|
||||||
<color name="material_green_a700">#00c853</color>
|
<color name="material_green_a700">#00c853</color>
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
<string name="ignore_update">Ignore</string>
|
<string name="ignore_update">Ignore</string>
|
||||||
|
|
||||||
|
<string name="copied_to_clipboard">Copied to clipboard</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>
|
||||||
|
|
||||||
@@ -69,7 +71,7 @@
|
|||||||
<string name="main_fab_random">Open a random gallery</string>
|
<string name="main_fab_random">Open a random gallery</string>
|
||||||
<string name="main_fab_cancel">Cancel all downloads</string>
|
<string name="main_fab_cancel">Cancel all downloads</string>
|
||||||
|
|
||||||
<string name="main_move">Move to page %1$d</string>
|
<string name="main_move_to_page">Move to page %1$d</string>
|
||||||
|
|
||||||
<string name="main_download">DOWNLOAD</string>
|
<string name="main_download">DOWNLOAD</string>
|
||||||
<string name="main_delete">DELETE</string>
|
<string name="main_delete">DELETE</string>
|
||||||
@@ -175,6 +177,9 @@
|
|||||||
<!-- SETTINGS/MISCELLANEOUS -->
|
<!-- SETTINGS/MISCELLANEOUS -->
|
||||||
|
|
||||||
<string name="settings_miscellaneous_title">Miscellaneous</string>
|
<string name="settings_miscellaneous_title">Miscellaneous</string>
|
||||||
|
<string name="settings_tag_translation">Tag Language</string>
|
||||||
|
<string name="settings_concurrent_download">Concurrent Download</string>
|
||||||
|
<string name="settings_tag_translation_message">Participate in translation on Github</string>
|
||||||
<string name="settings_mirror_summary">Load images from mirrors</string>
|
<string name="settings_mirror_summary">Load images from mirrors</string>
|
||||||
<string name="settings_proxy_title">Proxy</string>
|
<string name="settings_proxy_title">Proxy</string>
|
||||||
<string name="settings_rtl">Turn pages Right-to-Left</string>
|
<string name="settings_rtl">Turn pages Right-to-Left</string>
|
||||||
@@ -184,7 +189,6 @@
|
|||||||
<string name="settings_dark_mode_summary">Protect yourself against light attacks!</string>
|
<string name="settings_dark_mode_summary">Protect yourself against light attacks!</string>
|
||||||
<string name="settings_import_old_galleries">Import old galleries</string>
|
<string name="settings_import_old_galleries">Import old galleries</string>
|
||||||
<string name="settings_user_id">User ID</string>
|
<string name="settings_user_id">User ID</string>
|
||||||
<string name="settings_user_id_toast">User ID is copied to clipboard</string>
|
|
||||||
<string name="settings_oss">Open Source Notice</string>
|
<string name="settings_oss">Open Source Notice</string>
|
||||||
|
|
||||||
<!-- MANAGE FAVORITES -->
|
<!-- MANAGE FAVORITES -->
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:key="app_version"
|
app:key="app_version"
|
||||||
@@ -76,6 +75,11 @@
|
|||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
app:title="@string/settings_miscellaneous_title">
|
app:title="@string/settings_miscellaneous_title">
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
app:key="tag_translation"
|
||||||
|
app:title="@string/settings_tag_translation"
|
||||||
|
app:useSimpleSummaryProvider="true"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:key="mirrors"
|
app:key="mirrors"
|
||||||
app:title="@string/settings_mirror_title"
|
app:title="@string/settings_mirror_title"
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "com.android.tools.build:gradle:4.0.1"
|
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||||
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.3"
|
classpath "com.google.gms:google-services:4.3.4"
|
||||||
// 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 "com.google.firebase:firebase-crashlytics-gradle:2.3.0"
|
classpath "com.google.firebase:firebase-crashlytics-gradle:2.4.1"
|
||||||
classpath "com.google.firebase:perf-plugin:1.3.1"
|
classpath "com.google.firebase:perf-plugin:1.3.4"
|
||||||
classpath "com.google.android.gms:oss-licenses-plugin:0.10.2"
|
classpath "com.google.android.gms:oss-licenses-plugin:0.10.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
|
|||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.configureondemand=true
|
org.gradle.configureondemand=true
|
||||||
|
org.gradle.caching=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
|
||||||
kotlin_version=1.4.10
|
kotlin_version=1.4.20
|
||||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Thu Oct 01 20:54:37 KST 2020
|
#Tue Oct 13 22:37:11 KST 2020
|
||||||
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-6.1.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||||
|
|||||||
Reference in New Issue
Block a user