Compare commits

...

54 Commits

Author SHA1 Message Date
tom5079
2c6ddcc64b hitomi.la image not loading fix 2020-10-21 14:42:07 +09:00
tom5079
8f2e757b77 Update README.md 2020-10-15 15:53:30 +09:00
tom5079
ff177955b3 Update README.md 2020-10-15 15:49:07 +09:00
tom5079
8bb8066a98 Apk built 2020-10-15 14:37:13 +09:00
tom5079
2747ddbf65 Adjust gallery_id margin 2020-10-15 14:32:36 +09:00
tom5079
b939e9424d Translate tag by default 2020-10-15 14:29:22 +09:00
tom5079
fb9dea5d1e Copy galleryID by clicking galleryblock_id 2020-10-15 12:52:38 +09:00
tom5079
da4d5d711b Prefetch
Resolves #109
2020-10-15 10:20:36 +09:00
tom5079
331cbec5f1 Bug fix 2020-10-14 18:36:36 +09:00
tom5079
7f02284285 Update README.md 2020-10-14 00:26:06 +09:00
tom5079
ac2c3a6d97 Merge remote-tracking branch 'origin/master' into master 2020-10-14 00:25:44 +09:00
tom5079
c3bc80fec6 Bug fix 2020-10-14 00:24:38 +09:00
tom5079
09779a0710 Update README.md 2020-10-13 23:47:56 +09:00
tom5079
e82c6ef866 App built
Possible build time optimization
2020-10-13 23:40:53 +09:00
tom5079
861ae9be64 Merge remote-tracking branch 'origin/dev' into dev 2020-10-13 23:34:28 +09:00
tom5079
96108bc1ec Improves Scroll Jitter 2020-10-13 23:34:16 +09:00
tom5079
016f217db0 Merge pull request #108 from klx7007/patch-1
Fix FloatingSearchView imeOptions to only affect keyboard visibility
2020-10-13 23:05:34 +09:00
tom5079
0688294f18 Dependency update
Support non external storage document Uris

Support non external storage document Uris
2020-10-13 22:59:29 +09:00
klx7007
9ad008255d FloatingSearchView imeOptions 수정
imeOption을 덮어씌워서 search할때 키보드만 숨겨짐
2020-10-13 22:58:13 +09:00
tom5079
4c5a862dd6 Update README.md 2020-10-13 17:57:19 +09:00
tom5079
b165a2308f Merge remote-tracking branch 'origin/master' into master 2020-10-13 17:13:10 +09:00
tom5079
8757b08cd2 Fixed pagecount not showing up 2020-10-13 17:12:53 +09:00
tom5079
3800543fba Update README.md 2020-10-13 13:59:02 +09:00
tom5079
02ef60c818 Update README.md 2020-10-13 13:57:23 +09:00
tom5079
88f3b30266 Merge branch 'dev' into master 2020-10-13 13:52:34 +09:00
tom5079
9203dc0112 Tag translation 2020-10-13 13:51:53 +09:00
tom5079
4c683bec68 Dependency update
Fixes concurrentmodificationexception
2020-10-13 08:34:04 +09:00
tom5079
0cfd1eb453 Update README.md 2020-10-04 23:05:30 +09:00
tom5079
19744dab37 Merge remote-tracking branch 'origin/master' into master 2020-10-04 23:04:30 +09:00
tom5079
12d58e5aa7 Don't cancel download onPause 2020-10-04 23:04:12 +09:00
tom5079
e46dd37a26 Update README.md 2020-10-04 22:47:45 +09:00
tom5079
49c3ebc36b Concurrency issue fixed
Don't cancel download onPause
Limit folder length to 127 characters
2020-10-04 22:44:31 +09:00
tom5079
11e9bc2235 Added five entries per page option 2020-10-04 20:39:33 +09:00
tom5079
3029b3bf0e Update README.md 2020-10-03 20:20:41 +09:00
tom5079
9a6c6f67ce Merge branch 'dev' into master 2020-10-03 20:18:53 +09:00
tom5079
a6ed0baef2 Fix auto cache cleanup 2020-10-03 20:18:20 +09:00
tom5079
d3b43d80da Update README.md 2020-10-03 10:06:17 +09:00
tom5079
46d4316d49 Merge remote-tracking branch 'origin/master' into master 2020-10-03 10:05:30 +09:00
tom5079
ade2864351 Fix auto cache cleanup 2020-10-03 10:03:57 +09:00
tom5079
365fc56e9d Update README.md 2020-10-02 13:09:46 +09:00
tom5079
54a5cd21ea Merge branch 'dev' into master 2020-10-02 13:00:02 +09:00
tom5079
38c0399b09 App built 2020-10-02 12:59:32 +09:00
tom5079
2b67858453 Auto cache clean 2020-10-02 12:51:59 +09:00
tom5079
87fdbdbb6e Open GalleryDialog first instead of opening Reader directly 2020-10-02 00:19:56 +09:00
tom5079
bab77a4116 (KO) Added support link 2020-10-02 00:13:34 +09:00
tom5079
d20756ab96 Reset security mode 2020-10-01 22:51:52 +09:00
tom5079
dc75a753c3 Minimum thumbnail height 2020-10-01 22:46:03 +09:00
tom5079
4712d47903 Show 10 tags maximum 2020-10-01 22:20:49 +09:00
tom5079
c5561801e1 Add group name to GalleryBlock 2020-10-01 21:32:42 +09:00
tom5079
5c259fa07a Dependency update
Fixed duplicated download file
Better download progress update handling

TODO: Add group name to GalleryBlock
2020-10-01 21:24:32 +09:00
tom5079
60e8b18702 Update README.md 2020-09-29 16:53:15 +09:00
tom5079
a8317824a9 Merge remote-tracking branch 'origin/master' into master 2020-09-27 21:40:32 +09:00
tom5079
0c3c78cc72 Fixed app crashing when loading thumbnail 2020-09-27 21:40:22 +09:00
tom5079
cfd4a8faac Update README.md 2020-09-27 21:39:05 +09:00
43 changed files with 706 additions and 329 deletions

6
.idea/compiler.xml generated Normal file
View File

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

1
.idea/gradle.xml generated
View File

@@ -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>

View 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
View File

@@ -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">

View File

@@ -1,7 +1,8 @@
![Banner](https://github.com/tom5079/Pupil/blob/gh-pages/assets/images/pupil-banner.png?raw=true) ![Banner](https://github.com/tom5079/Pupil/blob/gh-pages/assets/images/pupil-banner.png?raw=true)
*Pupil, Hitomi.la viewer for Android* *Pupil, Hitomi.la viewer for Android*
[![](https://img.shields.io/github/downloads/tom5079/Pupil/latest/Pupil-v5.1.1-hotfix2.apk?color=%234fc3f7&label=DOWNLOAD%20APP&style=for-the-badge)](https://github.com/tom5079/Pupil/releases/download/5.1.1-hotfix2/Pupil-v5.1.1-hotfix2.apk) ![](https://img.shields.io/github/downloads/tom5079/Pupil/total)
[![](https://img.shields.io/github/downloads/tom5079/Pupil/5.1.6-hotfix1/Pupil-v5.1.6-hotfix1.apk?color=%234fc3f7&label=DOWNLOAD%20APP&style=for-the-badge)](https://github.com/tom5079/Pupil/releases/download/5.1.6-hotfix1/Pupil-v5.1.6-hotfix1.apk)
[![](https://discordapp.com/api/guilds/610452916612104194/embed.png?style=banner2)](https://discord.gg/Stj4b5v) [![](https://discordapp.com/api/guilds/610452916612104194/embed.png?style=banner2)](https://discord.gg/Stj4b5v)
# Features # Features
@@ -20,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)

View File

@@ -37,8 +37,8 @@ android {
applicationId "xyz.quaver.pupil" applicationId "xyz.quaver.pupil"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 30 targetSdkVersion 30
versionCode 61 versionCode 63
versionName "5.1.1-hotfix2" versionName "5.1.6-hotfix1"
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,26 +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.0-M1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0"
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.2"
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 "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-alpha03"
implementation "com.google.firebase:firebase-core:17.5.0" implementation "com.google.firebase:firebase-core:17.5.1"
implementation "com.google.firebase:firebase-analytics:17.5.0" implementation "com.google.firebase:firebase-analytics:17.6.0"
implementation "com.google.firebase:firebase-crashlytics:17.2.1" implementation "com.google.firebase:firebase-crashlytics:17.2.2"
implementation "com.google.firebase:firebase-perf:19.0.8" implementation "com.google.firebase:firebase-perf:19.0.9"
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"
@@ -124,9 +123,9 @@ 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.4"
implementation "xyz.quaver:documentfilex:0.2.15" implementation "xyz.quaver:documentfilex:0.4-alpha02"
implementation "xyz.quaver:floatingsearchview:1.0.5" implementation "xyz.quaver:floatingsearchview:1.0.7"
testImplementation "junit:junit:4.13" testImplementation "junit:junit:4.13"
androidTestImplementation "androidx.test.ext:junit:1.1.2" androidTestImplementation "androidx.test.ext:junit:1.1.2"

View File

@@ -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
@@ -47,5 +32,4 @@
kotlinx.serialization.KSerializer serializer(...); kotlinx.serialization.KSerializer serializer(...);
} }
-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

View File

@@ -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": 63,
"versionCode": 61, "versionName": "5.1.6-hotfix1",
"versionName": "5.1.1-hotfix2",
"enabled": true,
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }
] ]

View File

@@ -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
@@ -112,6 +115,11 @@ class Pupil : Application() {
Preferences.remove("download_folder") Preferences.remove("download_folder")
} }
if (!Preferences["reset_secure", false]) {
Preferences["security_mode"] = false
Preferences["reset_secure"] = true
}
histories = SavedSet(File(ContextCompat.getDataDir(this), "histories.json"), 0) histories = SavedSet(File(ContextCompat.getDataDir(this), "histories.json"), 0)
favorites = SavedSet(File(ContextCompat.getDataDir(this), "favorites.json"), 0) favorites = SavedSet(File(ContextCompat.getDataDir(this), "favorites.json"), 0)
favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse("")) favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse(""))

View File

@@ -18,14 +18,17 @@
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.net.Uri import android.util.Log
import android.util.SparseBooleanArray import android.util.SparseBooleanArray
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -36,15 +39,14 @@ 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.item_reader.view.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import xyz.quaver.hitomi.getGallery
import xyz.quaver.hitomi.getReader import xyz.quaver.hitomi.getReader
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.getChild
import xyz.quaver.pupil.R 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.TagChip
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
@@ -59,13 +61,22 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
PREV PREV
} }
var update = true var updateAll = true
var thin: Boolean = Preferences["thin"] var thin: Boolean = Preferences["thin"]
inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) { inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
var updateJob: Job? = null private var galleryID: Int = 0
private fun updateProgress(context: Context, galleryID: Int) { init {
CoroutineScope(Dispatchers.Main).launch {
while (updateAll) {
updateProgress(view.context)
delay(1000)
}
}
}
private fun updateProgress(context: Context) {
val cache = Cache.getInstance(context, galleryID) val cache = Cache.getInstance(context, galleryID)
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
@@ -86,6 +97,13 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
visibility = View.VISIBLE visibility = View.VISIBLE
} }
view.galleryblock_id.setOnClickListener {
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
ClipData.newPlainText("gallery_id", galleryID.toString())
)
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
}
if (!imageList.contains(null)) { if (!imageList.contains(null)) {
val downloadManager = DownloadManager.getInstance(context) val downloadManager = DownloadManager.getInstance(context)
@@ -118,9 +136,14 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
} }
fun bind(galleryID: Int) { fun bind(galleryID: Int) {
this.galleryID = galleryID
updateProgress(view.context)
val cache = Cache.getInstance(view.context, galleryID) val cache = Cache.getInstance(view.context, galleryID)
val galleryBlock = cache.metadata.galleryBlock ?: return val galleryBlock = runBlocking {
cache.getGalleryBlock()
} ?: return
with(view) { with(view) {
val resources = context.resources val resources = context.resources
@@ -158,25 +181,36 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
}) })
ssiv?.recycle() ssiv?.recycle()
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
showImage(cache.getThumbnail() ?: Uri.EMPTY) cache.getThumbnail().let { launch(Dispatchers.Main) {
showImage(it)
} }
} }
} }
if (updateJob == null)
updateJob = CoroutineScope(Dispatchers.Main).launch {
while (update) {
updateProgress(context, galleryID)
delay(1000)
}
}
galleryblock_title.text = galleryBlock.title galleryblock_title.text = galleryBlock.title
with(galleryblock_artist) { with(galleryblock_artist) {
text = artists.joinToString(", ") { it.wordCapitalize() } text = artists.joinToString { it.wordCapitalize() }
visibility = when { visibility = when {
artists.isNotEmpty() -> View.VISIBLE artists.isNotEmpty() -> View.VISIBLE
else -> View.GONE else -> View.GONE
} }
CoroutineScope(Dispatchers.IO).launch {
val gallery = runCatching {
getGallery(galleryID)
}.getOrNull()
if (gallery?.groups?.isNotEmpty() != true)
return@launch
launch(Dispatchers.Main) {
text = context.getString(
R.string.galleryblock_artist_with_group,
artists.joinToString { it.wordCapitalize() },
gallery.groups.joinToString { it.wordCapitalize() }
)
}
}
} }
with(galleryblock_series) { with(galleryblock_series) {
text = text =
@@ -198,27 +232,37 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
} }
} }
galleryblock_tag_group.removeAllViews() with(galleryblock_tag_group) {
CoroutineScope(Dispatchers.Default).launch { onClickListener = {
galleryBlock.relatedTags.sortedBy { onChipClickedHandler.forEach { callback ->
val tag = Tag.parse(it) callback.invoke(it)
if (favoriteTags.contains(tag))
-1
else
when(Tag.parse(it).area) {
"female" -> 0
"male" -> 1
else -> 2
}
}.map {
TagChip(context, Tag.parse(it)).apply {
setOnClickListener { view ->
for (callback in onChipClickedHandler)
callback.invoke((view as TagChip).tag)
}
} }
}.let { launch(Dispatchers.Main) { it.forEach { galleryblock_tag_group.addView(it) } } } }
tags.clear()
CoroutineScope(Dispatchers.IO).launch {
tags.addAll(
galleryBlock.relatedTags.sortedBy {
val tag = Tag.parse(it)
if (favoriteTags.contains(tag))
-1
else
when(Tag.parse(it).area) {
"female" -> 0
"male" -> 1
else -> 2
}
}.map {
Tag.parse(it)
}
)
launch(Dispatchers.Main) {
refresh()
}
}
} }
galleryblock_id.text = galleryBlock.id.toString() galleryblock_id.text = galleryBlock.id.toString()
@@ -262,8 +306,6 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
// Make some views invisible to make it thinner // Make some views invisible to make it thinner
if (thin) { if (thin) {
galleryblock_language.visibility = View.GONE
galleryblock_type.visibility = View.GONE
galleryblock_tag_group.visibility = View.GONE galleryblock_tag_group.visibility = View.GONE
} }
} }
@@ -358,15 +400,6 @@ class GalleryBlockAdapter(private val galleries: List<Int>) : RecyclerSwipeAdapt
} }
} }
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
super.onViewDetachedFromWindow(holder)
if (holder is GalleryViewHolder) {
holder.updateJob?.cancel()
holder.updateJob = null
}
}
override fun getItemCount() = override fun getItemCount() =
galleries.size + galleries.size +
(if (showNext) 1 else 0) + (if (showNext) 1 else 0) +

View File

@@ -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
@@ -62,7 +61,14 @@ class ReaderAdapter(
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun clear() { fun clear() {
view.image.ssiv?.recycle() view.image.mainView.let {
when (it) {
is SubsamplingScaleImageView ->
it.recycle()
is SimpleDraweeView ->
it.controller = null
}
}
} }
} }

View File

@@ -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,17 +36,16 @@ 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.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.ellipsize import xyz.quaver.pupil.util.ellipsize
import xyz.quaver.pupil.util.normalizeID import xyz.quaver.pupil.util.normalizeID
import xyz.quaver.pupil.util.requestBuilders import xyz.quaver.pupil.util.requestBuilders
import java.io.IOException import java.io.IOException
import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.log10 import kotlin.math.log10
@@ -90,7 +88,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)
@@ -98,7 +96,7 @@ class DownloadService : Service() {
addAction(action) addAction(action)
setProgress(0, 0, true) setProgress(0, 0, true)
setOngoing(true) setOngoing(true)
}) }
notify(galleryID) notify(galleryID)
} }
@@ -123,7 +121,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)
@@ -198,6 +196,7 @@ 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
@@ -289,14 +288,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 {
if (progress.containsKey(galleryID)) if (DownloadManager.getInstance(this@DownloadService).isDownloading(galleryID))
cancel(galleryID) return@launch
cleanCache(this@DownloadService)
val cache = Cache.getInstance(this@DownloadService, galleryID) val cache = Cache.getInstance(this@DownloadService, galleryID)
@@ -311,6 +312,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 {

View File

@@ -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

View File

@@ -23,11 +23,15 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
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.MotionEvent
import android.view.View import android.view.View
import android.widget.* import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
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
@@ -58,6 +62,7 @@ import xyz.quaver.pupil.util.checkUpdate
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.restore import xyz.quaver.pupil.util.restore
import java.util.regex.Pattern
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
@@ -144,7 +149,7 @@ class MainActivity :
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
(main_recyclerview?.adapter as? GalleryBlockAdapter)?.update = false (main_recyclerview?.adapter as? GalleryBlockAdapter)?.updateAll = false
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
@@ -202,6 +207,8 @@ class MainActivity :
} }
) )
Linkify.addLinks(main_noresult, Pattern.compile(getString(R.string.https_text)), null, null, { _, _ -> getString(R.string.https) })
//NavigationView //NavigationView
main_nav_view.setNavigationItemSelectedListener(this) main_nav_view.setNavigationItemSelectedListener(this)
@@ -282,12 +289,22 @@ class MainActivity :
setTitle(R.string.main_open_gallery_by_id) setTitle(R.string.main_open_gallery_by_id)
setPositiveButton(android.R.string.ok) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
val galleryID = editText.text.toString().toIntOrNull() ?: return@setPositiveButton val galleryID = editText.text.toString().toIntOrNull() ?: return@setPositiveButton
val intent = Intent(this@MainActivity, ReaderActivity::class.java).apply {
putExtra("galleryID", galleryID)
}
startActivity(intent) GalleryDialog(this@MainActivity, galleryID).apply {
onChipClickedHandler.add {
runOnUiThread {
query = it.toQuery()
currentPage = 0
cancelFetch()
clearGalleries()
fetchGalleries(query, sortMode)
loadBlocks()
}
dismiss()
}
}.show()
} }
}.show() }.show()
} }
@@ -363,7 +380,7 @@ class MainActivity :
if (v !is CardView) if (v !is CardView)
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 {

View File

@@ -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)

View File

@@ -42,7 +42,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import xyz.quaver.hitomi.Gallery import xyz.quaver.hitomi.Gallery
import xyz.quaver.hitomi.getGallery import xyz.quaver.hitomi.getGallery
import xyz.quaver.pupil.BuildConfig
import xyz.quaver.pupil.R 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
@@ -54,6 +53,8 @@ import xyz.quaver.pupil.ui.view.TagChip
import xyz.quaver.pupil.util.ItemClickSupport import xyz.quaver.pupil.util.ItemClickSupport
import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.wordCapitalize import xyz.quaver.pupil.util.wordCapitalize
import java.util.*
import kotlin.collections.ArrayList
class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(context) { class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(context) {
@@ -76,7 +77,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)
} }
} }
@@ -113,7 +113,12 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
addRelated(gallery) addRelated(gallery)
} }
} catch (e: Exception) { } catch (e: Exception) {
Snackbar.make(gallery_layout, R.string.unable_to_connect, Snackbar.LENGTH_INDEFINITE).show() Snackbar.make(gallery_layout, R.string.unable_to_connect, Snackbar.LENGTH_INDEFINITE).apply {
if (Locale.getDefault().language == "ko")
setAction(context.getText(R.string.https_text)) {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.https))))
}
}.show()
} }
} }
} }
@@ -232,7 +237,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 {

View File

@@ -31,6 +31,7 @@ import xyz.quaver.io.util.deleteRecursively
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.histories import xyz.quaver.pupil.histories
import xyz.quaver.pupil.util.byteToString import xyz.quaver.pupil.util.byteToString
import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.DownloadManager import xyz.quaver.pupil.util.downloader.DownloadManager
import java.io.File import java.io.File
@@ -61,6 +62,8 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
if (dir.exists()) if (dir.exists())
dir.deleteRecursively() dir.deleteRecursively()
Cache.instances.clear()
summary = context.getString(R.string.settings_storage_usage, byteToString(0)) summary = context.getString(R.string.settings_storage_usage, byteToString(0))
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
var size = 0L var size = 0L

View File

@@ -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
} }

View File

@@ -57,7 +57,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 +164,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)
@@ -217,4 +215,4 @@ class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: Attr
} }
} }
} }
} }

View File

@@ -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)

View File

@@ -0,0 +1,100 @@
/*
* 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.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.util.Log
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import kotlinx.coroutines.*
import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tag
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 {
object Defaults {
val maxChipSize = 10
}
var maxChipSize: Int = Defaults.maxChipSize
set(value) {
field = value
refresh()
}
private val moreView = Chip(context).apply {
text = ""
setEnsureMinTouchTargetSize(false)
setOnClickListener {
removeView(this)
for (i in maxChipSize until tags.size) {
val tag = tags.elementAt(i)
addView(TagChip(context, tag).apply {
setOnClickListener {
onClickListener?.invoke(tag)
}
})
}
}
}
var onClickListener: ((Tag) -> Unit)? = null
private fun applyAttributes(attr: TypedArray) {
maxChipSize = attr.getInt(R.styleable.TagChipGroup_maxTag, Defaults.maxChipSize)
}
private var refreshJob: Job? = null
fun refresh() {
refreshJob?.cancel()
this.removeAllViews()
refreshJob = CoroutineScope(Dispatchers.Main).launch {
tags.take(maxChipSize).map {
CoroutineScope(Dispatchers.Default).async {
TagChip(context, it).apply {
setOnClickListener {
onClickListener?.invoke(this.tag)
}
}
}
}.forEach {
addView(it.await())
}
if (maxChipSize > 0 && tags.size > maxChipSize && parent == null)
addView(moreView)
}
}
init {
applyAttributes(context.obtainStyledAttributes(attr, R.styleable.TagChipGroup))
refresh()
}
}

View File

@@ -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,24 +39,23 @@ 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()) }.onSuccess {
}.onSuccess { 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()

View File

@@ -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
}
}
}

View File

@@ -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)
} }
} }
@@ -132,7 +135,7 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
} }
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
suspend fun getThumbnail(): Uri? = suspend fun getThumbnail(): Uri =
findFile(".thumbnail")?.uri findFile(".thumbnail")?.uri
?: getGalleryBlock()?.thumbnails?.firstOrNull()?.let { withContext(Dispatchers.IO) { ?: getGalleryBlock()?.thumbnails?.firstOrNull()?.let { withContext(Dispatchers.IO) {
kotlin.runCatching { kotlin.runCatching {
@@ -142,9 +145,14 @@ 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
suspend fun getReader(): Reader? { suspend fun getReader(): Reader? {
val mirrors = Preferences.get<String>("mirrors").let { if (it.isEmpty()) emptyList() else it.split('>') } val mirrors = Preferences.get<String>("mirrors").let { if (it.isEmpty()) emptyList() else it.split('>') }
@@ -186,67 +194,76 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
} }
fun getImage(index: Int): FileX? = fun getImage(index: Int): FileX? =
metadata.imageList?.get(index)?.let { findFile(it) } metadata.imageList?.getOrNull(index)?.let { findFile(it) }
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
fun putImage(index: Int, fileName: String, data: ByteArray) { fun putImage(index: Int, fileName: String, data: ByteArray) {
val file = cacheFolder.getChild(fileName) val file = cacheFolder.getChild(fileName)
file.createNewFile() if (!file.exists())
file.createNewFile()
file.writeBytes(data) file.writeBytes(data)
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
val cacheMetadata = cacheFolder.getChild(".metadata") if (lock[galleryID]?.isLocked == true)
val downloadMetadata = downloadFolder.getChild(".metadata")
if (downloadMetadata.exists() || !cacheMetadata.exists())
return@launch return@launch
if (cacheMetadata.exists()) { (lock[galleryID] ?: Mutex().also { lock[galleryID] = it }).withLock {
kotlin.runCatching { val cacheMetadata = cacheFolder.getChild(".metadata")
downloadMetadata.createNewFile() val downloadMetadata = downloadFolder.getChild(".metadata")
downloadMetadata.writeText(Json.encodeToString(metadata))
cacheMetadata.delete() if (!cacheMetadata.exists())
return@launch
if (cacheMetadata.exists()) {
kotlin.runCatching {
if (!downloadMetadata.exists())
downloadMetadata.createNewFile()
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()
}
} }
}
metadata.imageList?.forEach { imageName -> metadata.imageList?.forEach { imageName ->
imageName ?: return@forEach imageName ?: return@forEach
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()
} }
} }
} }

View File

@@ -80,7 +80,7 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
}.invoke() }.invoke()
} }
return downloadFolderMapInstance!! return downloadFolderMapInstance ?: mutableMapOf()
} }

View File

@@ -19,35 +19,49 @@
package xyz.quaver.pupil.util package xyz.quaver.pupil.util
import android.content.Context import android.content.Context
import android.os.storage.StorageManager import kotlinx.coroutines.CoroutineScope
import androidx.core.content.ContextCompat import kotlinx.coroutines.Dispatchers
import androidx.core.net.toUri import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import xyz.quaver.pupil.histories
import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.DownloadManager
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.lang.reflect.Array
import java.net.URL
@Suppress("DEPRECATION") val mutex = Mutex()
@Deprecated("Use downloader.Cache instead") fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch {
fun getCachedGallery(context: Context, galleryID: Int) = if (mutex.isLocked) return@launch
File(getDownloadDirectory(context), galleryID.toString()).let {
if (it.exists()) mutex.withLock {
it val cacheFolder = File(context.cacheDir, "imageCache")
else val downloadManager = DownloadManager.getInstance(context)
File(context.cacheDir, "imageCache/$galleryID")
val limit = (Preferences.get<String>("cache_limit").toLongOrNull() ?: 0L)*1024*1024*1024
if (limit == 0L) return@withLock
val cacheSize = {
var size = 0L
cacheFolder.walk().forEach {
size += it.length()
}
size
}
if (cacheSize.invoke() > limit)
while (cacheSize.invoke() > limit/2) {
val caches = cacheFolder.list() ?: return@withLock
synchronized(histories) {
(histories.firstOrNull {
caches.contains(it.toString()) && !downloadManager.isDownloading(it)
} ?: return@withLock).let {
Cache.delete(context, it)
}
}
}
} }
}
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache instead")
fun getDownloadDirectory(context: Context) =
Preferences.get<String>("dl_location").let {
if (it.isNotEmpty() && !it.startsWith("content"))
File(it)
else
context.getExternalFilesDir(null)!!
}
@Suppress("DEPRECATION")
@Deprecated("Use FileX instead")
fun File.isParentOf(another: File) =
another.absolutePath.startsWith(this.absolutePath)

View File

@@ -23,6 +23,9 @@ 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.JsonElement
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import xyz.quaver.Code import xyz.quaver.Code
@@ -93,14 +96,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() {
@@ -128,4 +131,10 @@ fun String.ellipsize(n: Int): String =
if (this.length > n) if (this.length > n)
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]

View 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())
}

View File

@@ -313,7 +313,7 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
) )
synchronized(Cache) { synchronized(Cache) {
Cache.delete(galleryID) Cache.delete(this@migrate, galleryID)
} }
downloadFolderMap[galleryID] = folder.name downloadFolderMap[galleryID] = folder.name

View File

@@ -47,22 +47,6 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.ContentLoadingProgressBar
style="?android:attr/progressBarStyle"
android:id="@+id/main_progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"/>
<TextView
android:id="@+id/main_noresult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/main_no_result"
android:visibility="invisible"/>
<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"
@@ -86,6 +70,24 @@
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller> </com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
<androidx.core.widget.ContentLoadingProgressBar
style="?android:attr/progressBarStyle"
android:id="@+id/main_progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"/>
<TextView
android:id="@+id/main_noresult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/main_no_result"
android:linksClickable="true"
android:visibility="invisible"/>
<com.github.clans.fab.FloatingActionMenu <com.github.clans.fab.FloatingActionMenu
android:id="@+id/main_fab" android:id="@+id/main_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@@ -113,9 +113,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"
app:layout_constraintHeight_default="spread"
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_toBottomOf="@id/galleryblock_progressbar_layout"
app:layout_constraintBottom_toBottomOf="@id/barrier"/> app:layout_constraintBottom_toTopOf="@id/barrier"/>
<TextView <TextView
style="@style/TextAppearance.AppCompat.Headline" style="@style/TextAppearance.AppCompat.Headline"
@@ -164,14 +166,7 @@
app:layout_constraintTop_toBottomOf="@id/galleryblock_type" app:layout_constraintTop_toBottomOf="@id/galleryblock_type"
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail" /> app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail" />
<androidx.constraintlayout.widget.Barrier <xyz.quaver.pupil.ui.view.TagChipGroup
android:id="@+id/padding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="galleryblock_language"/>
<com.google.android.material.chip.ChipGroup
android:id="@+id/galleryblock_tag_group" android:id="@+id/galleryblock_tag_group"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -179,7 +174,7 @@
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
app:chipSpacing="4dp" app:chipSpacing="4dp"
app:layout_constraintTop_toBottomOf="@id/padding" app:layout_constraintTop_toBottomOf="@id/galleryblock_language"
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail" app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
app:layout_constraintRight_toRightOf="parent"/> app:layout_constraintRight_toRightOf="parent"/>
@@ -188,7 +183,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:barrierDirection="bottom" app:barrierDirection="bottom"
app:constraint_referenced_ids="galleryblock_tag_group"/> app:constraint_referenced_ids="galleryblock_thumbnail, galleryblock_tag_group"/>
<View <View
android:id="@+id/divider" android:id="@+id/divider"
@@ -202,9 +197,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"/>
@@ -217,8 +210,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"

View File

@@ -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>
@@ -150,4 +150,9 @@
<string name="camera_denied">カメラ権限が拒否されているため、まばたき検知使用できません</string> <string name="camera_denied">カメラ権限が拒否されているため、まばたき検知使用できません</string>
<string name="no_camera">この機器には前面カメラが装着されていません</string> <string name="no_camera">この機器には前面カメラが装着されていません</string>
<string name="error">エラー</string> <string name="error">エラー</string>
<string name="settings_cache_limit">キャッシュサイズ制限</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>

View File

@@ -12,10 +12,10 @@
<string name="settings_galleries_per_page">한 번에 로드할 갤러리 수</string> <string name="settings_galleries_per_page">한 번에 로드할 갤러리 수</string>
<string name="settings_search_title">검색 설정</string> <string name="settings_search_title">검색 설정</string>
<string name="settings_title">설정</string> <string name="settings_title">설정</string>
<string name="update_notification_description">apk 다운로드중&#8230;</string> <string name="update_notification_description">업데이트 다운로드중&#8230;</string>
<string name="update_title">업데이트가 있습니다!</string> <string name="update_title">업데이트가 있습니다!</string>
<string name="warning">경고</string> <string name="warning">경고</string>
<string name="main_no_result">결과 없음</string> <string name="main_no_result">결과 없음\n해결법</string>
<string name="settings_miscellaneous_title">기타</string> <string name="settings_miscellaneous_title">기타</string>
<string name="settings_clear_history">기록 삭제</string> <string name="settings_clear_history">기록 삭제</string>
<string name="settings_clear_history_alert_message">기록을 삭제하시겠습니까?</string> <string name="settings_clear_history_alert_message">기록을 삭제하시겠습니까?</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>
@@ -150,4 +150,9 @@
<string name="camera_denied">카메라 권한이 거부되었기 때문에 눈 깜빡임 감지가 불가능합니다</string> <string name="camera_denied">카메라 권한이 거부되었기 때문에 눈 깜빡임 감지가 불가능합니다</string>
<string name="no_camera">이 장치에는 전면 카메라가 없습니다</string> <string name="no_camera">이 장치에는 전면 카메라가 없습니다</string>
<string name="error">오류</string> <string name="error">오류</string>
<string name="settings_cache_limit">캐시 크기 제한</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>

View File

@@ -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>
@@ -58,4 +59,24 @@
<item>SOCKS</item> <item>SOCKS</item>
</string-array> </string-array>
<string-array name="cache_size">
<item>0</item>
<item>1</item>
<item>2</item>
<item>4</item>
<item>8</item>
<item>16</item>
<item>32</item>
</string-array>
<string-array name="cache_size_text">
<item>@string/settings_cache_unlimited</item>
<item>1G</item>
<item>2G</item>
<item>4G</item>
<item>8G</item>
<item>16G</item>
<item>32G</item>
</string-array>
</resources> </resources>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<resources>
<declare-styleable name="TagChipGroup">
<attr name="maxTag" format="integer"/>
</declare-styleable>
</resources>

View File

@@ -9,6 +9,10 @@
<string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string> <string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string>
<string name="discord" translatable="false">https://discord.gg/Stj4b5v</string> <string name="discord" translatable="false">https://discord.gg/Stj4b5v</string>
<!-- Korean only -->
<string name="https_text" translatable="false">해결법</string>
<string name="https" translatable="false">https://bit.ly/34dUBwy</string>
<string name="backup_url" translatable="false">http://ix.io/</string> <string name="backup_url" translatable="false">http://ix.io/</string>
<string name="main_settings" translatable="false">Settings</string> <string name="main_settings" translatable="false">Settings</string>
@@ -17,6 +21,8 @@
<string name="reader_imageview_description" translatable="false">Content ImageView</string> <string name="reader_imageview_description" translatable="false">Content ImageView</string>
<string name="page_indicator_placeholder" translatable="false">-/-</string> <string name="page_indicator_placeholder" translatable="false">-/-</string>
<string name="galleryblock_artist_with_group" translatable="false">%s (%s)</string>
<!-- Translate needed down here --> <!-- Translate needed down here -->
<string name="warning">Warning</string> <string name="warning">Warning</string>
@@ -24,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>
@@ -155,6 +163,9 @@
<string name="settings_download_folder_available">%s available</string> <string name="settings_download_folder_available">%s available</string>
<string name="settings_download_folder_custom">Custom Location</string> <string name="settings_download_folder_custom">Custom Location</string>
<string name="settings_download_folder_not_writable">This folder is not writable. Please select another folder.</string> <string name="settings_download_folder_not_writable">This folder is not writable. Please select another folder.</string>
<string name="settings_cache_limit">Cache Limit</string>
<string name="settings_cache_unlimited">Unlimited</string>
<string name="settings_nomedia_title">Hide image from gallery</string>
<string name="settings_low_quality">Low quality images</string> <string name="settings_low_quality">Low quality images</string>
<string name="settings_low_quality_summary">Load low quality images to improve load speed and data usage</string> <string name="settings_low_quality_summary">Load low quality images to improve load speed and data usage</string>
@@ -166,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>
@@ -173,10 +187,8 @@
<string name="settings_security_mode_summary">Enable security mode to make the screen invisible on recent app window</string> <string name="settings_security_mode_summary">Enable security mode to make the screen invisible on recent app window</string>
<string name="settings_dark_mode_title">Dark mode</string> <string name="settings_dark_mode_title">Dark mode</string>
<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_nomedia_title">Hide image from gallery</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 -->

View File

@@ -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"
@@ -44,6 +43,14 @@
app:key="download_folder" app:key="download_folder"
app:title="@string/settings_download_folder"/> app:title="@string/settings_download_folder"/>
<ListPreference
app:key="cache_limit"
app:title="@string/settings_cache_limit"
app:entries="@array/cache_size_text"
app:entryValues="@array/cache_size"
app:defaultValue="8"
app:useSimpleSummaryProvider="true"/>
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="nomedia" app:key="nomedia"
app:title="@string/settings_nomedia_title"/> app:title="@string/settings_nomedia_title"/>
@@ -68,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"

View File

@@ -6,7 +6,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath "com.android.tools.build:gradle:4.0.1" classpath 'com.android.tools.build:gradle:4.1.0'
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"
@@ -14,7 +14,7 @@ buildscript {
// 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.3.0"
classpath "com.google.firebase:perf-plugin:1.3.1" classpath "com.google.firebase:perf-plugin:1.3.2"
classpath "com.google.android.gms:oss-licenses-plugin:0.10.2" classpath "com.google.android.gms:oss-licenses-plugin:0.10.2"
} }
} }

View File

@@ -15,6 +15,7 @@ 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

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Thu Jun 18 15:48:09 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

51
gradlew vendored
View File

@@ -1,5 +1,21 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## ##
## Gradle start up script for UN*X ## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="" DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
@@ -138,19 +154,19 @@ if $cygwin ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@@ -159,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

18
gradlew.bat vendored
View File

@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS= set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome