Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0eb6d40d7e | ||
|
|
f7b9260a2b | ||
|
|
7df09aa74b | ||
|
|
b3ce36f81c | ||
|
|
27dded11c1 | ||
|
|
758210658e | ||
|
|
830d490822 | ||
|
|
236ce2c189 | ||
|
|
7f3f17d08e | ||
|
|
72937cdd42 | ||
|
|
9c878f5e44 | ||
|
|
db928a168f | ||
|
|
0eff941d48 | ||
|
|
be4aca1f6b | ||
|
|
038b8e0ac5 | ||
|
|
79a4917897 | ||
|
|
8c8ead5830 | ||
|
|
a3b6b010be | ||
|
|
8bf936ee20 | ||
|
|
c69972f289 | ||
|
|
05f555bb91 | ||
|
|
83d6058f2b | ||
|
|
f888535389 | ||
|
|
0f2336eccf | ||
|
|
e8443664dc | ||
|
|
0f9b6963a6 | ||
|
|
6727ac1014 | ||
|
|
a15e2c30cb | ||
|
|
6c603b2bf3 | ||
|
|
8146fc8473 | ||
|
|
3eff34e585 | ||
|
|
b24f4b5306 | ||
|
|
a6d5336608 |
11
README.md
11
README.md
@@ -1,26 +1,29 @@
|
|||||||

|

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

|

|
||||||
[](https://github.com/tom5079/Pupil/releases/download/5.3.13/Pupil-v5.3.13.apk)
|
[](https://github.com/tom5079/Pupil/releases/download/5.3.18/Pupil-v5.3.18.apk)
|
||||||
[](https://discord.gg/Stj4b5v)
|
[](https://discord.gg/Stj4b5v)
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Go [Releases page](https://github.com/tom5079/Pupil/releases) and get latest version or
|
Go [Releases page](https://github.com/tom5079/Pupil/releases) and get latest version or
|
||||||
Visit [github page](https://tom5079.github.io/Pupil/) (only available in Korean)
|
Visit [github page](https://tom5079.github.io/Pupil/) (only available in Korean)
|
||||||
or Build app yourself
|
or Build app yourself
|
||||||
|
|
||||||
# Manual
|
# Manual
|
||||||
|
|
||||||
[Manual](https://tom5079.github.io/Pupil/2019/06/06/manual-kr.html) is only available in Korean. Consider using translator.
|
[Manual](https://tom5079.github.io/Pupil/2019/06/06/manual-kr.html) is only available in Korean.
|
||||||
|
Consider using translator.
|
||||||
|
|
||||||
# Contribution
|
# Contribution
|
||||||
|
|
||||||
Any kind of contribution is appreciated. Feel free to leave PR!
|
Any kind of contribution is appreciated. Feel free to leave PR!
|
||||||
|
|
||||||
## Tag Translation
|
## Tag Translation
|
||||||
|
|
||||||
Head over to [tags branch](https://github.com/tom5079/Pupil/tree/tags)
|
Head over to [tags branch](https://github.com/tom5079/Pupil/tree/tags)
|
||||||
|
|||||||
145
app/build.gradle
145
app/build.gradle
@@ -1,145 +0,0 @@
|
|||||||
apply plugin: "com.android.application"
|
|
||||||
apply plugin: "kotlin-android"
|
|
||||||
apply plugin: "kotlin-kapt"
|
|
||||||
apply plugin: "kotlin-parcelize"
|
|
||||||
apply plugin: "kotlinx-serialization"
|
|
||||||
apply plugin: "com.google.android.gms.oss-licenses-plugin"
|
|
||||||
|
|
||||||
if (file("google-services.json").exists()) {
|
|
||||||
logger.lifecycle("Firebase Enabled")
|
|
||||||
apply plugin: "com.google.gms.google-services"
|
|
||||||
apply plugin: "com.google.firebase.crashlytics"
|
|
||||||
apply plugin: "com.google.firebase.firebase-perf"
|
|
||||||
} else {
|
|
||||||
logger.lifecycle("Firebase Disabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
ext {
|
|
||||||
okhttp_version = "3.12.12"
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
all {
|
|
||||||
resolutionStrategy {
|
|
||||||
eachDependency { DependencyResolveDetails details ->
|
|
||||||
if (details.requested.group == "com.squareup.okhttp3" && details.requested.name == "okhttp") {
|
|
||||||
// OkHttp drops support before 5.0 since 3.13.0
|
|
||||||
details.useVersion okhttp_version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "xyz.quaver.pupil"
|
|
||||||
minSdkVersion 16
|
|
||||||
compileSdk 34
|
|
||||||
targetSdkVersion 34
|
|
||||||
versionCode 69
|
|
||||||
versionName "5.3.15"
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
vectorDrawables.useSupportLibrary = true
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
debug {
|
|
||||||
defaultConfig.minSdkVersion 21
|
|
||||||
|
|
||||||
minifyEnabled false
|
|
||||||
shrinkResources false
|
|
||||||
|
|
||||||
debuggable true
|
|
||||||
applicationIdSuffix ".debug"
|
|
||||||
versionNameSuffix "-DEBUG"
|
|
||||||
|
|
||||||
ext.enableCrashlytics = false
|
|
||||||
ext.alwaysUpdateBuildId = false
|
|
||||||
}
|
|
||||||
release {
|
|
||||||
minifyEnabled true
|
|
||||||
shrinkResources true
|
|
||||||
|
|
||||||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildFeatures {
|
|
||||||
viewBinding true
|
|
||||||
}
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
|
||||||
freeCompilerArgs += "-Xuse-experimental=kotlin.Experimental"
|
|
||||||
}
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.3.2"
|
|
||||||
|
|
||||||
implementation "androidx.core:core-ktx:1.12.0"
|
|
||||||
implementation "androidx.appcompat:appcompat:1.4.1"
|
|
||||||
implementation "androidx.activity:activity-ktx:1.4.0"
|
|
||||||
implementation "androidx.fragment:fragment-ktx:1.4.1"
|
|
||||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
|
||||||
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
|
||||||
implementation "androidx.biometric:biometric:1.1.0"
|
|
||||||
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
|
||||||
|
|
||||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
|
||||||
|
|
||||||
implementation "com.google.android.material:material:1.11.0"
|
|
||||||
|
|
||||||
implementation platform('com.google.firebase:firebase-bom:32.7.0')
|
|
||||||
implementation "com.google.firebase:firebase-analytics-ktx"
|
|
||||||
implementation "com.google.firebase:firebase-crashlytics-ktx"
|
|
||||||
implementation "com.google.firebase:firebase-perf-ktx"
|
|
||||||
|
|
||||||
implementation "com.google.android.gms:play-services-oss-licenses:17.0.1"
|
|
||||||
implementation "com.google.android.gms:play-services-mlkit-face-detection:17.1.0"
|
|
||||||
|
|
||||||
implementation "com.github.clans:fab:1.6.4"
|
|
||||||
|
|
||||||
//implementation "com.quiph.ui:recyclerviewfastscroller:0.2.1"
|
|
||||||
|
|
||||||
implementation 'com.github.piasy:BigImageViewer:1.8.1'
|
|
||||||
implementation 'com.github.piasy:FrescoImageLoader:1.8.1'
|
|
||||||
implementation 'com.github.piasy:FrescoImageViewFactory:1.8.1'
|
|
||||||
implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.6.0'
|
|
||||||
|
|
||||||
//noinspection GradleDependency
|
|
||||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
|
||||||
implementation "io.ktor:ktor-network:2.3.10"
|
|
||||||
|
|
||||||
implementation "com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2"
|
|
||||||
|
|
||||||
implementation "net.rdrei.android.dirchooser:library:3.2@aar"
|
|
||||||
implementation "com.gu:option:1.3"
|
|
||||||
|
|
||||||
implementation "com.andrognito.patternlockview:patternlockview:1.0.0"
|
|
||||||
//implementation "com.andrognito.pinlockview:pinlockview:2.1.0"
|
|
||||||
|
|
||||||
implementation "ru.noties.markwon:core:3.1.0"
|
|
||||||
|
|
||||||
implementation "com.skyfishjy.ripplebackground:library:1.0.1"
|
|
||||||
|
|
||||||
implementation "org.jsoup:jsoup:1.14.3"
|
|
||||||
|
|
||||||
implementation "xyz.quaver:documentfilex:0.7.2"
|
|
||||||
implementation "xyz.quaver:floatingsearchview:1.1.7"
|
|
||||||
|
|
||||||
testImplementation "junit:junit:4.13.2"
|
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1"
|
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.3"
|
|
||||||
androidTestImplementation "androidx.test:rules:1.4.0"
|
|
||||||
androidTestImplementation "androidx.test:runner:1.4.0"
|
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0"
|
|
||||||
}
|
|
||||||
129
app/build.gradle.kts
Normal file
129
app/build.gradle.kts
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.kotlinx.serialization)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
alias(libs.plugins.gms.oss.licenses)
|
||||||
|
alias(libs.plugins.gms.google.services)
|
||||||
|
alias(libs.plugins.firebase.crashlytics)
|
||||||
|
alias(libs.plugins.firebase.perf)
|
||||||
|
id("kotlin-parcelize")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "xyz.quaver.pupil"
|
||||||
|
compileSdk = 35
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "xyz.quaver.pupil"
|
||||||
|
minSdk = 21
|
||||||
|
targetSdk = 35
|
||||||
|
versionCode = 70
|
||||||
|
versionName = "5.3.22"
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
isShrinkResources = false
|
||||||
|
|
||||||
|
isDebuggable = true
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
versionNameSuffix = "-DEBUG"
|
||||||
|
|
||||||
|
extra.apply {
|
||||||
|
set("enableCrashlytics", false)
|
||||||
|
set("alwaysUpdateBuildId", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding = true
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.kotlin.stdlib.jdk8)
|
||||||
|
implementation(libs.kotlinx.coroutines.android)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.kotlinx.datetime)
|
||||||
|
|
||||||
|
implementation(libs.androidx.compose.runtime)
|
||||||
|
|
||||||
|
implementation(libs.core.ktx)
|
||||||
|
implementation(libs.appcompat)
|
||||||
|
implementation(libs.activity.ktx)
|
||||||
|
implementation(libs.fragment.ktx)
|
||||||
|
implementation(libs.preference.ktx)
|
||||||
|
implementation(libs.recyclerview)
|
||||||
|
implementation(libs.constraintlayout)
|
||||||
|
implementation(libs.gridlayout)
|
||||||
|
implementation(libs.biometric)
|
||||||
|
implementation(libs.work.runtime.ktx)
|
||||||
|
|
||||||
|
implementation(libs.library)
|
||||||
|
|
||||||
|
implementation(libs.material)
|
||||||
|
|
||||||
|
implementation(platform(libs.firebase.bom))
|
||||||
|
implementation(libs.firebase.analytics.ktx)
|
||||||
|
implementation(libs.firebase.crashlytics.ktx)
|
||||||
|
implementation(libs.firebase.perf.ktx)
|
||||||
|
|
||||||
|
implementation(libs.play.services.oss.licenses)
|
||||||
|
implementation(libs.play.services.mlkit.face.detection)
|
||||||
|
|
||||||
|
implementation(libs.fab)
|
||||||
|
|
||||||
|
implementation(libs.bigimageviewer)
|
||||||
|
implementation(libs.frescoimageloader)
|
||||||
|
implementation(libs.frescoimageviewfactory)
|
||||||
|
implementation(libs.imagepipeline.okhttp3)
|
||||||
|
|
||||||
|
//noinspection GradleDependency
|
||||||
|
implementation(libs.okhttp)
|
||||||
|
implementation(libs.ktor.network)
|
||||||
|
|
||||||
|
implementation(libs.dotsindicator)
|
||||||
|
|
||||||
|
implementation(libs.pinlockview)
|
||||||
|
implementation(libs.patternlockview)
|
||||||
|
|
||||||
|
implementation(libs.core)
|
||||||
|
|
||||||
|
implementation(libs.ripplebackground.library)
|
||||||
|
implementation(libs.recyclerview.fastscroller)
|
||||||
|
|
||||||
|
implementation(libs.jsoup)
|
||||||
|
|
||||||
|
implementation(libs.documentfilex)
|
||||||
|
implementation(libs.floatingsearchview)
|
||||||
|
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
testImplementation(libs.kotlinx.coroutines.test)
|
||||||
|
androidTestImplementation(libs.ext.junit)
|
||||||
|
androidTestImplementation(libs.rules)
|
||||||
|
androidTestImplementation(libs.runner)
|
||||||
|
androidTestImplementation(libs.espresso.core)
|
||||||
|
}
|
||||||
BIN
app/release/baselineProfiles/0/app-release.dm
Normal file
BIN
app/release/baselineProfiles/0/app-release.dm
Normal file
Binary file not shown.
BIN
app/release/baselineProfiles/1/app-release.dm
Normal file
BIN
app/release/baselineProfiles/1/app-release.dm
Normal file
Binary file not shown.
@@ -11,10 +11,27 @@
|
|||||||
"type": "SINGLE",
|
"type": "SINGLE",
|
||||||
"filters": [],
|
"filters": [],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 69,
|
"versionCode": 70,
|
||||||
"versionName": "5.3.14",
|
"versionName": "5.3.22",
|
||||||
"outputFile": "app-release.apk"
|
"outputFile": "app-release.apk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"elementType": "File"
|
"elementType": "File",
|
||||||
|
"baselineProfiles": [
|
||||||
|
{
|
||||||
|
"minApi": 28,
|
||||||
|
"maxApi": 30,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/1/app-release.dm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minApi": 31,
|
||||||
|
"maxApi": 2147483647,
|
||||||
|
"baselineProfiles": [
|
||||||
|
"baselineProfiles/0/app-release.dm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"minSdkVersionForDexing": 21
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
~ Copyright (C) 2020 tom5079
|
~ Copyright (C) 2020 tom5079
|
||||||
~
|
~
|
||||||
@@ -17,6 +16,4 @@
|
|||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources></resources>
|
||||||
<string name="app_name" translatable="false" tools:override="true">Pupil-Debug</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
package="xyz.quaver.pupil">
|
|
||||||
|
|
||||||
<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" />
|
||||||
|
|||||||
@@ -16,12 +16,14 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.hitomi
|
package xyz.quaver.pupil.hitomi
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.datetime.Clock.System.now
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.Callback
|
import okhttp3.Callback
|
||||||
@@ -30,9 +32,7 @@ import okhttp3.Response
|
|||||||
import xyz.quaver.pupil.client
|
import xyz.quaver.pupil.client
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlin.time.Duration.Companion.minutes
|
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
|
|
||||||
const val protocol = "https:"
|
const val protocol = "https:"
|
||||||
@@ -40,25 +40,25 @@ const val protocol = "https:"
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class Artist(
|
data class Artist(
|
||||||
val artist: String,
|
val artist: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Group(
|
data class Group(
|
||||||
val group: String,
|
val group: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Parody(
|
data class Parody(
|
||||||
val parody: String,
|
val parody: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Character(
|
data class Character(
|
||||||
val character: String,
|
val character: String,
|
||||||
val url: String
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -66,7 +66,7 @@ data class Tag(
|
|||||||
val tag: String,
|
val tag: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
val female: String? = null,
|
val female: String? = null,
|
||||||
val male: String? = null
|
val male: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -74,7 +74,7 @@ data class Language(
|
|||||||
val galleryid: String,
|
val galleryid: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
val language_localname: String,
|
val language_localname: String,
|
||||||
val name: String
|
val name: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -93,7 +93,7 @@ data class GalleryInfo(
|
|||||||
val languages: List<Language> = emptyList(),
|
val languages: List<Language> = emptyList(),
|
||||||
val characters: List<Character>? = null,
|
val characters: List<Character>? = null,
|
||||||
val scene_indexes: List<Int>? = emptyList(),
|
val scene_indexes: List<Int>? = emptyList(),
|
||||||
val files: List<GalleryFiles> = emptyList()
|
val files: List<GalleryFiles> = emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val json = Json {
|
val json = Json {
|
||||||
@@ -104,13 +104,16 @@ val json = Json {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typealias HeaderSetter = (Request.Builder) -> Request.Builder
|
typealias HeaderSetter = (Request.Builder) -> Request.Builder
|
||||||
|
|
||||||
fun URL.readText(settings: HeaderSetter? = null): String {
|
fun URL.readText(settings: HeaderSetter? = null): String {
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(this).let {
|
.url(this).let {
|
||||||
settings?.invoke(it) ?: it
|
settings?.invoke(it) ?: it
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
return client.newCall(request).execute().also{ if (it.code() != 200) throw IOException("CODE ${it.code()}") }.body()?.use { it.string() } ?: throw IOException()
|
return client.newCall(request).execute()
|
||||||
|
.also { if (it.code() != 200) throw IOException("CODE ${it.code()}") }.body()
|
||||||
|
?.use { it.string() } ?: throw IOException()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun URL.readBytes(settings: HeaderSetter? = null): ByteArray {
|
fun URL.readBytes(settings: HeaderSetter? = null): ByteArray {
|
||||||
@@ -119,7 +122,9 @@ fun URL.readBytes(settings: HeaderSetter? = null): ByteArray {
|
|||||||
settings?.invoke(it) ?: it
|
settings?.invoke(it) ?: it
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
return client.newCall(request).execute().also { if (it.code() != 200) throw IOException("CODE ${it.code()}") }.body()?.use { it.bytes() } ?: throw IOException()
|
return client.newCall(request).execute()
|
||||||
|
.also { if (it.code() != 200) throw IOException("CODE ${it.code()}") }.body()
|
||||||
|
?.use { it.bytes() } ?: throw IOException()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||||
@@ -130,7 +135,7 @@ fun getGalleryInfo(galleryID: Int) =
|
|||||||
)
|
)
|
||||||
|
|
||||||
//common.js
|
//common.js
|
||||||
const val domain = "ltn.hitomi.la"
|
const val domain = "ltn.gold-usergeneratedcontent.net"
|
||||||
const val galleryblockextension = ".html"
|
const val galleryblockextension = ".html"
|
||||||
const val galleryblockdir = "galleryblock"
|
const val galleryblockdir = "galleryblock"
|
||||||
const val nozomiextension = ".nozomi"
|
const val nozomiextension = ".nozomi"
|
||||||
@@ -152,9 +157,13 @@ object gg {
|
|||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
if (lastRetrieval == null || (lastRetrieval!! + 60000) < System.currentTimeMillis()) {
|
if (lastRetrieval == null || (lastRetrieval!! + 60000) < System.currentTimeMillis()) {
|
||||||
val ggjs: String = suspendCancellableCoroutine { continuation ->
|
val ggjs: String = suspendCancellableCoroutine { continuation ->
|
||||||
val call = client.newCall(Request.Builder().url("https://ltn.hitomi.la/gg.js").build())
|
val call =
|
||||||
|
client.newCall(
|
||||||
|
Request.Builder().url("https://ltn.gold-usergeneratedcontent.net/gg.js")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
call.enqueue(object: Callback {
|
call.enqueue(object : Callback {
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
if (continuation.isCancelled) return
|
if (continuation.isCancelled) return
|
||||||
continuation.resumeWithException(e)
|
continuation.resumeWithException(e)
|
||||||
@@ -202,64 +211,90 @@ object gg {
|
|||||||
refresh()
|
refresh()
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
fun s(h: String): String {
|
fun s(h: String): String {
|
||||||
val m = Regex("(..)(.)$").find(h)
|
val m = Regex("(..)(.)$").find(h)
|
||||||
return m!!.groupValues.let { it[2]+it[1] }.toInt(16).toString(10)
|
return m!!.groupValues.let { it[2] + it[1] }.toInt(16).toString(10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun subdomainFromURL(url: String, base: String? = null) : String {
|
suspend fun subdomainFromURL(url: String, base: String? = null, dir: String? = null): String {
|
||||||
var retval = "b"
|
var retval = ""
|
||||||
|
|
||||||
if (!base.isNullOrBlank())
|
if (base.isNullOrBlank()) {
|
||||||
retval = base
|
when {
|
||||||
|
dir == "webp" -> retval = "w"
|
||||||
|
dir == "avif" -> retval = "a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val b = 16
|
val b = 16
|
||||||
|
|
||||||
val r = Regex("""/[0-9a-f]{61}([0-9a-f]{2})([0-9a-f])""")
|
val r = Regex("""/[0-9a-f]{61}([0-9a-f]{2})([0-9a-f])""")
|
||||||
val m = r.find(url) ?: return "a"
|
val m = r.find(url) ?: return ""
|
||||||
|
|
||||||
val g = m.groupValues.let { it[2]+it[1] }.toIntOrNull(b)
|
val g = m.groupValues.let { it[2] + it[1] }.toIntOrNull(b)
|
||||||
|
|
||||||
if (g != null) {
|
if (g != null) {
|
||||||
retval = (97+ gg.m(g)).toChar().toString() + retval
|
retval = if (base.isNullOrEmpty()) {
|
||||||
|
retval + (1 + gg.m(g)).toString()
|
||||||
|
} else {
|
||||||
|
(97 + gg.m(g)).toChar().toString() + base
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun urlFromUrl(url: String, base: String? = null) : String {
|
suspend fun urlFromUrl(url: String, base: String? = null, dir: String? = null): String {
|
||||||
return url.replace(Regex("""//..?\.hitomi\.la/"""), "//${subdomainFromURL(url, base)}.hitomi.la/")
|
return url.replace(
|
||||||
|
Regex("""//..?\.(?:gold-usergeneratedcontent\.net|hitomi\.la)/"""),
|
||||||
|
"//${subdomainFromURL(url, base, dir)}.gold-usergeneratedcontent.net/"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fullPathFromHash(hash: String) : String =
|
suspend fun fullPathFromHash(hash: String): String =
|
||||||
"${gg.b()}${gg.s(hash)}/$hash"
|
"${gg.b()}${gg.s(hash)}/$hash"
|
||||||
|
|
||||||
fun realFullPathFromHash(hash: String): String =
|
fun realFullPathFromHash(hash: String): String =
|
||||||
hash.replace(Regex("""^.*(..)(.)$"""), "$2/$1/$hash")
|
hash.replace(Regex("""^.*(..)(.)$"""), "$2/$1/$hash")
|
||||||
|
|
||||||
suspend fun urlFromHash(galleryID: Int, image: GalleryFiles, dir: String? = null, ext: String? = null) : String {
|
suspend fun urlFromHash(
|
||||||
|
galleryID: Int,
|
||||||
|
image: GalleryFiles,
|
||||||
|
dir: String? = null,
|
||||||
|
ext: String? = null,
|
||||||
|
): String {
|
||||||
val ext = ext ?: dir ?: image.name.takeLastWhile { it != '.' }
|
val ext = ext ?: dir ?: image.name.takeLastWhile { it != '.' }
|
||||||
val dir = dir ?: "images"
|
return buildString {
|
||||||
return "https://a.hitomi.la/$dir/${fullPathFromHash(image.hash)}.$ext"
|
append("https://a.gold-usergeneratedcontent.net/")
|
||||||
}
|
if (dir != "webp" && dir != "avif") {
|
||||||
|
append(dir)
|
||||||
suspend fun urlFromUrlFromHash(galleryID: Int, image: GalleryFiles, dir: String? = null, ext: String? = null, base: String? = null) =
|
append("/")
|
||||||
if (base == "tn")
|
|
||||||
urlFromUrl("https://a.hitomi.la/$dir/${realFullPathFromHash(image.hash)}.$ext", base)
|
|
||||||
else
|
|
||||||
urlFromUrl(urlFromHash(galleryID, image, dir, ext), base)
|
|
||||||
|
|
||||||
suspend fun rewriteTnPaths(html: String) {
|
|
||||||
html.replace(Regex("""//tn\.hitomi\.la/[^/]+/[0-9a-f]/[0-9a-f]{2}/[0-9a-f]{64}""")) { url ->
|
|
||||||
runBlocking {
|
|
||||||
urlFromUrl(url.value, "tn")
|
|
||||||
}
|
}
|
||||||
|
append(fullPathFromHash(image.hash))
|
||||||
|
append(".")
|
||||||
|
append(ext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun imageUrlFromImage(galleryID: Int, image: GalleryFiles, noWebp: Boolean) : String {
|
suspend fun urlFromUrlFromHash(
|
||||||
return urlFromUrlFromHash(galleryID, image, "webp", null, "a")
|
galleryID: Int,
|
||||||
|
image: GalleryFiles,
|
||||||
|
dir: String? = null,
|
||||||
|
ext: String? = null,
|
||||||
|
base: String? = null,
|
||||||
|
) =
|
||||||
|
if (base == "tn")
|
||||||
|
urlFromUrl(
|
||||||
|
"https://a.gold-usergeneratedcontent.net/$dir/${realFullPathFromHash(image.hash)}.$ext",
|
||||||
|
base,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
urlFromUrl(urlFromHash(galleryID, image, dir, ext), base, dir)
|
||||||
|
|
||||||
|
suspend fun imageUrlFromImage(galleryID: Int, image: GalleryFiles, noWebp: Boolean): String {
|
||||||
|
return urlFromUrlFromHash(galleryID, image, "webp")
|
||||||
// return when {
|
// return when {
|
||||||
// noWebp ->
|
// noWebp ->
|
||||||
// urlFromUrlFromHash(galleryID, image)
|
// urlFromUrlFromHash(galleryID, image)
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ package xyz.quaver.pupil.hitomi
|
|||||||
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import java.util.*
|
import java.util.LinkedList
|
||||||
|
|
||||||
suspend fun doSearch(query: String, sortByPopularity: Boolean = false) : Set<Int> = coroutineScope {
|
suspend fun doSearch(query: String, sortMode: SortMode): List<Int> = coroutineScope {
|
||||||
val terms = query
|
val terms = query
|
||||||
.trim()
|
.trim()
|
||||||
.replace(Regex("""^\?"""), "")
|
.replace(Regex("""^\?"""), "")
|
||||||
@@ -34,8 +34,8 @@ suspend fun doSearch(query: String, sortByPopularity: Boolean = false) : Set<Int
|
|||||||
val negativeTerms = LinkedList<String>()
|
val negativeTerms = LinkedList<String>()
|
||||||
|
|
||||||
for (term in terms) {
|
for (term in terms) {
|
||||||
if (term.matches(Regex("^-.+")))
|
if (term.startsWith("-"))
|
||||||
negativeTerms.push(term.replace(Regex("^-"), ""))
|
negativeTerms.push(term.substring(1))
|
||||||
else if (term.isNotBlank())
|
else if (term.isNotBlank())
|
||||||
positiveTerms.push(term)
|
positiveTerms.push(term)
|
||||||
}
|
}
|
||||||
@@ -43,22 +43,25 @@ suspend fun doSearch(query: String, sortByPopularity: Boolean = false) : Set<Int
|
|||||||
val positiveResults = positiveTerms.map {
|
val positiveResults = positiveTerms.map {
|
||||||
async {
|
async {
|
||||||
runCatching {
|
runCatching {
|
||||||
getGalleryIDsForQuery(it)
|
getGalleryIDsForQuery(it, sortMode)
|
||||||
}.getOrElse { emptySet() }
|
}.getOrElse { emptySet() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val negativeResults = negativeTerms.mapIndexed { index, it ->
|
val negativeResults = negativeTerms.map {
|
||||||
async {
|
async {
|
||||||
runCatching {
|
runCatching {
|
||||||
getGalleryIDsForQuery(it)
|
getGalleryIDsForQuery(it, sortMode)
|
||||||
}.getOrElse { emptySet() }
|
}.getOrElse { emptySet() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val results = when {
|
val results = when {
|
||||||
sortByPopularity -> getGalleryIDsFromNozomi(null, "popular", "all")
|
positiveTerms.isEmpty() -> getGalleryIDsFromNozomi(
|
||||||
positiveTerms.isEmpty() -> getGalleryIDsFromNozomi(null, "index", "all")
|
SearchArgs("all", "index", "all"),
|
||||||
|
sortMode
|
||||||
|
)
|
||||||
|
|
||||||
else -> emptySet()
|
else -> emptySet()
|
||||||
}.toMutableSet()
|
}.toMutableSet()
|
||||||
|
|
||||||
@@ -79,9 +82,13 @@ suspend fun doSearch(query: String, sortByPopularity: Boolean = false) : Set<Int
|
|||||||
}
|
}
|
||||||
|
|
||||||
//negative results
|
//negative results
|
||||||
negativeResults.forEachIndexed { index, it ->
|
negativeResults.forEach {
|
||||||
filterNegative(it.await())
|
filterNegative(it.await())
|
||||||
}
|
}
|
||||||
|
|
||||||
results
|
return@coroutineScope if (sortMode != SortMode.RANDOM) {
|
||||||
|
results.toList()
|
||||||
|
} else {
|
||||||
|
results.shuffled()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,54 @@ import java.nio.ByteOrder
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
data class SearchArgs(
|
||||||
|
val area: String?,
|
||||||
|
val tag: String,
|
||||||
|
val language: String,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun fromQuery(query: String): SearchArgs? {
|
||||||
|
if (!query.contains(':')) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val (left, right) = query.split(':')
|
||||||
|
|
||||||
|
return when (left) {
|
||||||
|
"male", "female" -> SearchArgs("tag", query, "all")
|
||||||
|
"language" -> SearchArgs("all", "index", right)
|
||||||
|
else -> SearchArgs(left, right, "all")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SortMode {
|
||||||
|
DATE_ADDED,
|
||||||
|
DATE_PUBLISHED,
|
||||||
|
POPULAR_TODAY,
|
||||||
|
POPULAR_WEEK,
|
||||||
|
POPULAR_MONTH,
|
||||||
|
POPULAR_YEAR,
|
||||||
|
RANDOM;
|
||||||
|
|
||||||
|
val orderBy: String
|
||||||
|
get() = when (this) {
|
||||||
|
DATE_ADDED, DATE_PUBLISHED, RANDOM -> "date"
|
||||||
|
POPULAR_TODAY, POPULAR_WEEK, POPULAR_MONTH, POPULAR_YEAR -> "popular"
|
||||||
|
}
|
||||||
|
|
||||||
|
val orderByKey: String
|
||||||
|
get() = when (this) {
|
||||||
|
DATE_ADDED, RANDOM -> "added"
|
||||||
|
DATE_PUBLISHED -> "published"
|
||||||
|
POPULAR_TODAY -> "today"
|
||||||
|
POPULAR_WEEK -> "week"
|
||||||
|
POPULAR_MONTH -> "month"
|
||||||
|
POPULAR_YEAR -> "year"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//searchlib.js
|
//searchlib.js
|
||||||
const val separator = "-"
|
const val separator = "-"
|
||||||
const val extension = ".html"
|
const val extension = ".html"
|
||||||
@@ -39,51 +87,35 @@ val tag_index_version: String by lazy { getIndexVersion("tagindex") }
|
|||||||
val galleries_index_version: String by lazy { getIndexVersion("galleriesindex") }
|
val galleries_index_version: String by lazy { getIndexVersion("galleriesindex") }
|
||||||
val tagIndexDomain = "tagindex.hitomi.la"
|
val tagIndexDomain = "tagindex.hitomi.la"
|
||||||
|
|
||||||
fun sha256(data: ByteArray) : ByteArray {
|
fun sha256(data: ByteArray): ByteArray {
|
||||||
return MessageDigest.getInstance("SHA-256").digest(data)
|
return MessageDigest.getInstance("SHA-256").digest(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
fun hashTerm(term: String) : UByteArray {
|
fun hashTerm(term: String): UByteArray {
|
||||||
return sha256(term.toByteArray()).toUByteArray().sliceArray(0 until 4)
|
return sha256(term.toByteArray()).toUByteArray().sliceArray(0 until 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sanitize(input: String) : String {
|
fun sanitize(input: String): String {
|
||||||
return input.replace(Regex("[/#]"), "")
|
return input.replace(Regex("[/#]"), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIndexVersion(name: String) =
|
fun getIndexVersion(name: String) =
|
||||||
URL("$protocol//$domain/$name/version?_=${System.currentTimeMillis()}").readText()
|
URL("$protocol//$domain/$name/version?_=${System.currentTimeMillis()}").readText()
|
||||||
|
|
||||||
//search.js
|
//search.js
|
||||||
fun getGalleryIDsForQuery(query: String) : Set<Int> {
|
fun getGalleryIDsForQuery(query: String, sortMode: SortMode): Set<Int> {
|
||||||
query.replace("_", " ").let {
|
val sanitizedQuery = query.replace("_", " ")
|
||||||
if (it.indexOf(':') > -1) {
|
|
||||||
val sides = it.split(":")
|
|
||||||
val ns = sides[0]
|
|
||||||
var tag = sides[1]
|
|
||||||
|
|
||||||
var area : String? = ns
|
val args = SearchArgs.fromQuery(sanitizedQuery)
|
||||||
var language = "all"
|
|
||||||
when (ns) {
|
|
||||||
"female", "male" -> {
|
|
||||||
area = "tag"
|
|
||||||
tag = it
|
|
||||||
}
|
|
||||||
"language" -> {
|
|
||||||
area = null
|
|
||||||
language = tag
|
|
||||||
tag = "index"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getGalleryIDsFromNozomi(area, tag, language)
|
return if (args != null) {
|
||||||
}
|
getGalleryIDsFromNozomi(args, sortMode)
|
||||||
|
} else {
|
||||||
val key = hashTerm(it)
|
val key = hashTerm(sanitizedQuery)
|
||||||
val field = "galleries"
|
val field = "galleries"
|
||||||
|
|
||||||
val node = getNodeAtAddress(field, 0) ?: return emptySet()
|
val node = getNodeAtAddress(field, 0)
|
||||||
|
|
||||||
val data = bSearch(field, key, node)
|
val data = bSearch(field, key, node)
|
||||||
|
|
||||||
@@ -95,14 +127,14 @@ fun getGalleryIDsForQuery(query: String) : Set<Int> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun encodeSearchQueryForUrl(s: Char) =
|
fun encodeSearchQueryForUrl(s: Char) =
|
||||||
when(s) {
|
when (s) {
|
||||||
' ' -> "_"
|
' ' -> "_"
|
||||||
'/' -> "slash"
|
'/' -> "slash"
|
||||||
'.' -> "dot"
|
'.' -> "dot"
|
||||||
else -> s.toString()
|
else -> s.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuggestionsForQuery(query: String) : List<Suggestion> {
|
fun getSuggestionsForQuery(query: String): List<Suggestion> {
|
||||||
query.replace('_', ' ').let {
|
query.replace('_', ' ').let {
|
||||||
var field = "global"
|
var field = "global"
|
||||||
var term = it
|
var term = it
|
||||||
@@ -114,13 +146,16 @@ fun getSuggestionsForQuery(query: String) : List<Suggestion> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val chars = term.map(::encodeSearchQueryForUrl)
|
val chars = term.map(::encodeSearchQueryForUrl)
|
||||||
val url = "https://$tagIndexDomain/$field${if (chars.isNotEmpty()) "/${chars.joinToString("/")}" else ""}.json"
|
val url =
|
||||||
|
"https://$tagIndexDomain/$field${if (chars.isNotEmpty()) "/${chars.joinToString("/")}" else ""}.json"
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val suggestions = json.parseToJsonElement(client.newCall(request).execute().body()?.use { body -> body.string() } ?: return emptyList())
|
val suggestions = json.parseToJsonElement(
|
||||||
|
client.newCall(request).execute().body()?.use { body -> body.string() }
|
||||||
|
?: return emptyList())
|
||||||
|
|
||||||
return buildList {
|
return buildList {
|
||||||
suggestions.jsonArray.forEach { suggestionRaw ->
|
suggestions.jsonArray.forEach { suggestionRaw ->
|
||||||
@@ -131,26 +166,34 @@ fun getSuggestionsForQuery(query: String) : List<Suggestion> {
|
|||||||
val ns = suggestion[2].content ?: ""
|
val ns = suggestion[2].content ?: ""
|
||||||
|
|
||||||
val tagname = sanitize(suggestion[0].content ?: return@forEach)
|
val tagname = sanitize(suggestion[0].content ?: return@forEach)
|
||||||
val url = when(ns) {
|
val url = when (ns) {
|
||||||
"female", "male" -> "/tag/$ns:$tagname${separator}1$extension"
|
"female", "male" -> "/tag/$ns:$tagname${separator}1$extension"
|
||||||
"language" -> "/index-$tagname${separator}1$extension"
|
"language" -> "/index-$tagname${separator}1$extension"
|
||||||
else -> "/$ns/$tagname${separator}all${separator}1$extension"
|
else -> "/$ns/$tagname${separator}all${separator}1$extension"
|
||||||
}
|
}
|
||||||
|
|
||||||
add(Suggestion(suggestion[0].content ?: "", suggestion[1].content?.toIntOrNull() ?: 0, url, ns))
|
add(
|
||||||
|
Suggestion(
|
||||||
|
suggestion[0].content ?: "",
|
||||||
|
suggestion[1].content?.toIntOrNull() ?: 0,
|
||||||
|
url,
|
||||||
|
ns
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Suggestion(val s: String, val t: Int, val u: String, val n: String)
|
data class Suggestion(val s: String, val t: Int, val u: String, val n: String)
|
||||||
fun getSuggestionsFromData(field: String, data: Pair<Long, Int>) : List<Suggestion> {
|
|
||||||
|
fun getSuggestionsFromData(field: String, data: Pair<Long, Int>): List<Suggestion> {
|
||||||
val url = "$protocol//$domain/$index_dir/$field.$tag_index_version.data"
|
val url = "$protocol//$domain/$index_dir/$field.$tag_index_version.data"
|
||||||
val (offset, length) = data
|
val (offset, length) = data
|
||||||
if (length > 10000 || length <= 0)
|
if (length > 10000 || length <= 0)
|
||||||
throw Exception("length $length is too long")
|
throw Exception("length $length is too long")
|
||||||
|
|
||||||
val inbuf = getURLAtRange(url, offset.until(offset+length))
|
val inbuf = getURLAtRange(url, offset.until(offset + length))
|
||||||
|
|
||||||
val suggestions = ArrayList<Suggestion>()
|
val suggestions = ArrayList<Suggestion>()
|
||||||
|
|
||||||
@@ -165,23 +208,25 @@ fun getSuggestionsFromData(field: String, data: Pair<Long, Int>) : List<Suggesti
|
|||||||
for (i in 0.until(numberOfSuggestions)) {
|
for (i in 0.until(numberOfSuggestions)) {
|
||||||
var top = buffer.int
|
var top = buffer.int
|
||||||
|
|
||||||
val ns = inbuf.sliceArray(buffer.position().until(buffer.position()+top)).toString(charset("UTF-8"))
|
val ns = inbuf.sliceArray(buffer.position().until(buffer.position() + top))
|
||||||
buffer.position(buffer.position()+top)
|
.toString(charset("UTF-8"))
|
||||||
|
buffer.position(buffer.position() + top)
|
||||||
|
|
||||||
top = buffer.int
|
top = buffer.int
|
||||||
|
|
||||||
val tag = inbuf.sliceArray(buffer.position().until(buffer.position()+top)).toString(charset("UTF-8"))
|
val tag = inbuf.sliceArray(buffer.position().until(buffer.position() + top))
|
||||||
buffer.position(buffer.position()+top)
|
.toString(charset("UTF-8"))
|
||||||
|
buffer.position(buffer.position() + top)
|
||||||
|
|
||||||
val count = buffer.int
|
val count = buffer.int
|
||||||
|
|
||||||
val tagname = sanitize(tag)
|
val tagname = sanitize(tag)
|
||||||
val u =
|
val u =
|
||||||
when(ns) {
|
when (ns) {
|
||||||
"female", "male" -> "/tag/$ns:$tagname${separator}1$extension"
|
"female", "male" -> "/tag/$ns:$tagname${separator}1$extension"
|
||||||
"language" -> "/index-$tagname${separator}1$extension"
|
"language" -> "/index-$tagname${separator}1$extension"
|
||||||
else -> "/$ns/$tagname${separator}all${separator}1$extension"
|
else -> "/$ns/$tagname${separator}all${separator}1$extension"
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions.add(Suggestion(tag, count, u, ns))
|
suggestions.add(Suggestion(tag, count, u, ns))
|
||||||
}
|
}
|
||||||
@@ -189,12 +234,17 @@ fun getSuggestionsFromData(field: String, data: Pair<Long, Int>) : List<Suggesti
|
|||||||
return suggestions
|
return suggestions
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : Set<Int> {
|
fun nozomiAddressFromArgs(args: SearchArgs, sortMode: SortMode) = when {
|
||||||
val nozomiAddress =
|
sortMode != SortMode.DATE_ADDED && sortMode != SortMode.RANDOM ->
|
||||||
when(area) {
|
if (args.area == "all") "$protocol//$domain/$compressed_nozomi_prefix/${sortMode.orderBy}/${sortMode.orderByKey}-${args.language}$nozomiextension"
|
||||||
null -> "$protocol//$domain/$compressed_nozomi_prefix/$tag-$language$nozomiextension"
|
else "$protocol//$domain/$compressed_nozomi_prefix/${args.area}/${sortMode.orderBy}/${sortMode.orderByKey}/${args.tag}-${args.language}$nozomiextension"
|
||||||
else -> "$protocol//$domain/$compressed_nozomi_prefix/$area/$tag-$language$nozomiextension"
|
|
||||||
}
|
args.area == "all" -> "$protocol//$domain/$compressed_nozomi_prefix/${args.tag}-${args.language}$nozomiextension"
|
||||||
|
else -> "$protocol//$domain/$compressed_nozomi_prefix/${args.area}/${args.tag}-${args.language}$nozomiextension"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getGalleryIDsFromNozomi(args: SearchArgs, sortMode: SortMode): Set<Int> {
|
||||||
|
val nozomiAddress = nozomiAddressFromArgs(args, sortMode)
|
||||||
|
|
||||||
val bytes = URL(nozomiAddress).readBytes()
|
val bytes = URL(nozomiAddress).readBytes()
|
||||||
|
|
||||||
@@ -210,13 +260,13 @@ fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : Set<
|
|||||||
return nozomi
|
return nozomi
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGalleryIDsFromData(data: Pair<Long, Int>) : Set<Int> {
|
fun getGalleryIDsFromData(data: Pair<Long, Int>): Set<Int> {
|
||||||
val url = "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.data"
|
val url = "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.data"
|
||||||
val (offset, length) = data
|
val (offset, length) = data
|
||||||
if (length > 100000000 || length <= 0)
|
if (length > 100000000 || length <= 0)
|
||||||
throw Exception("length $length is too long")
|
throw Exception("length $length is too long")
|
||||||
|
|
||||||
val inbuf = getURLAtRange(url, offset.until(offset+length))
|
val inbuf = getURLAtRange(url, offset.until(offset + length))
|
||||||
|
|
||||||
val galleryIDs = mutableSetOf<Int>()
|
val galleryIDs = mutableSetOf<Int>()
|
||||||
|
|
||||||
@@ -226,7 +276,7 @@ fun getGalleryIDsFromData(data: Pair<Long, Int>) : Set<Int> {
|
|||||||
|
|
||||||
val numberOfGalleryIDs = buffer.int
|
val numberOfGalleryIDs = buffer.int
|
||||||
|
|
||||||
val expectedLength = numberOfGalleryIDs*4+4
|
val expectedLength = numberOfGalleryIDs * 4 + 4
|
||||||
|
|
||||||
if (numberOfGalleryIDs > 10000000 || numberOfGalleryIDs <= 0)
|
if (numberOfGalleryIDs > 10000000 || numberOfGalleryIDs <= 0)
|
||||||
throw Exception("number_of_galleryids $numberOfGalleryIDs is too long")
|
throw Exception("number_of_galleryids $numberOfGalleryIDs is too long")
|
||||||
@@ -239,33 +289,38 @@ fun getGalleryIDsFromData(data: Pair<Long, Int>) : Set<Int> {
|
|||||||
return galleryIDs
|
return galleryIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNodeAtAddress(field: String, address: Long) : Node? {
|
fun getNodeAtAddress(field: String, address: Long): Node {
|
||||||
val url =
|
val url =
|
||||||
when(field) {
|
when (field) {
|
||||||
"galleries" -> "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.index"
|
"galleries" -> "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.index"
|
||||||
"languages" -> "$protocol//$domain/$galleries_index_dir/languages.$galleries_index_version.index"
|
"languages" -> "$protocol//$domain/$galleries_index_dir/languages.$galleries_index_version.index"
|
||||||
"nozomiurl" -> "$protocol//$domain/$galleries_index_dir/nozomiurl.$galleries_index_version.index"
|
"nozomiurl" -> "$protocol//$domain/$galleries_index_dir/nozomiurl.$galleries_index_version.index"
|
||||||
else -> "$protocol//$domain/$index_dir/$field.$tag_index_version.index"
|
else -> "$protocol//$domain/$index_dir/$field.$tag_index_version.index"
|
||||||
}
|
}
|
||||||
|
|
||||||
val nodedata = getURLAtRange(url, address.until(address+ max_node_size))
|
val nodedata = getURLAtRange(url, address.until(address + max_node_size))
|
||||||
|
|
||||||
return decodeNode(nodedata)
|
return decodeNode(nodedata)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getURLAtRange(url: String, range: LongRange) : ByteArray {
|
fun getURLAtRange(url: String, range: LongRange): ByteArray {
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.header("Range", "bytes=${range.first}-${range.last}")
|
.header("Range", "bytes=${range.first}-${range.last}")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return client.newCall(request).execute().body()?.use { it.bytes() } ?: byteArrayOf()
|
return client.newCall(request).execute().body()?.use { it.bytes() } ?: byteArrayOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
data class Node(val keys: List<UByteArray>, val datas: List<Pair<Long, Int>>, val subNodeAddresses: List<Long>)
|
data class Node(
|
||||||
|
val keys: List<UByteArray>,
|
||||||
|
val datas: List<Pair<Long, Int>>,
|
||||||
|
val subNodeAddresses: List<Long>
|
||||||
|
)
|
||||||
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
fun decodeNode(data: ByteArray) : Node {
|
fun decodeNode(data: ByteArray): Node {
|
||||||
val buffer = ByteBuffer
|
val buffer = ByteBuffer
|
||||||
.wrap(data)
|
.wrap(data)
|
||||||
.order(ByteOrder.BIG_ENDIAN)
|
.order(ByteOrder.BIG_ENDIAN)
|
||||||
@@ -281,8 +336,8 @@ fun decodeNode(data: ByteArray) : Node {
|
|||||||
if (keySize == 0 || keySize > 32)
|
if (keySize == 0 || keySize > 32)
|
||||||
throw Exception("fatal: !keySize || keySize > 32")
|
throw Exception("fatal: !keySize || keySize > 32")
|
||||||
|
|
||||||
keys.add(uData.sliceArray(buffer.position().until(buffer.position()+keySize)))
|
keys.add(uData.sliceArray(buffer.position().until(buffer.position() + keySize)))
|
||||||
buffer.position(buffer.position()+keySize)
|
buffer.position(buffer.position() + keySize)
|
||||||
}
|
}
|
||||||
|
|
||||||
val numberOfDatas = buffer.int
|
val numberOfDatas = buffer.int
|
||||||
@@ -295,7 +350,7 @@ fun decodeNode(data: ByteArray) : Node {
|
|||||||
datas.add(Pair(offset, length))
|
datas.add(Pair(offset, length))
|
||||||
}
|
}
|
||||||
|
|
||||||
val numberOfSubNodeAddresses = B +1
|
val numberOfSubNodeAddresses = B + 1
|
||||||
val subNodeAddresses = ArrayList<Long>()
|
val subNodeAddresses = ArrayList<Long>()
|
||||||
|
|
||||||
for (i in 0.until(numberOfSubNodeAddresses)) {
|
for (i in 0.until(numberOfSubNodeAddresses)) {
|
||||||
@@ -307,8 +362,8 @@ fun decodeNode(data: ByteArray) : Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
fun bSearch(field: String, key: UByteArray, node: Node) : Pair<Long, Int>? {
|
fun bSearch(field: String, key: UByteArray, node: Node): Pair<Long, Int>? {
|
||||||
fun compareArrayBuffers(dv1: UByteArray, dv2: UByteArray) : Int {
|
fun compareArrayBuffers(dv1: UByteArray, dv2: UByteArray): Int {
|
||||||
val top = min(dv1.size, dv2.size)
|
val top = min(dv1.size, dv2.size)
|
||||||
|
|
||||||
for (i in 0.until(top)) {
|
for (i in 0.until(top)) {
|
||||||
@@ -321,18 +376,18 @@ fun bSearch(field: String, key: UByteArray, node: Node) : Pair<Long, Int>? {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun locateKey(key: UByteArray, node: Node) : Pair<Boolean, Int> {
|
fun locateKey(key: UByteArray, node: Node): Pair<Boolean, Int> {
|
||||||
for (i in node.keys.indices) {
|
for (i in node.keys.indices) {
|
||||||
val cmpResult = compareArrayBuffers(key, node.keys[i])
|
val cmpResult = compareArrayBuffers(key, node.keys[i])
|
||||||
|
|
||||||
if (cmpResult <= 0)
|
if (cmpResult <= 0)
|
||||||
return Pair(cmpResult==0, i)
|
return Pair(cmpResult == 0, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(false, node.keys.size)
|
return Pair(false, node.keys.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isLeaf(node: Node) : Boolean {
|
fun isLeaf(node: Node): Boolean {
|
||||||
for (subnode in node.subNodeAddresses)
|
for (subnode in node.subNodeAddresses)
|
||||||
if (subnode != 0L)
|
if (subnode != 0L)
|
||||||
return false
|
return false
|
||||||
@@ -349,6 +404,6 @@ fun bSearch(field: String, key: UByteArray, node: Node) : Pair<Long, Int>? {
|
|||||||
else if (isLeaf(node))
|
else if (isLeaf(node))
|
||||||
return null
|
return null
|
||||||
|
|
||||||
val nextNode = getNodeAtAddress(field, node.subNodeAddresses[where]) ?: return null
|
val nextNode = getNodeAtAddress(field, node.subNodeAddresses[where])
|
||||||
return bSearch(field, key, nextNode)
|
return bSearch(field, key, nextNode)
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,6 @@ import android.widget.EditText
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
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.app.ActivityCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
@@ -42,19 +41,40 @@ 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
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.MainScope
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
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.view.MenuView
|
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.*
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||||
import xyz.quaver.pupil.databinding.MainActivityBinding
|
import xyz.quaver.pupil.databinding.MainActivityBinding
|
||||||
|
import xyz.quaver.pupil.favoriteTags
|
||||||
|
import xyz.quaver.pupil.favorites
|
||||||
|
import xyz.quaver.pupil.histories
|
||||||
|
import xyz.quaver.pupil.hitomi.SortMode
|
||||||
import xyz.quaver.pupil.hitomi.doSearch
|
import xyz.quaver.pupil.hitomi.doSearch
|
||||||
import xyz.quaver.pupil.hitomi.getGalleryIDsFromNozomi
|
|
||||||
import xyz.quaver.pupil.hitomi.getSuggestionsForQuery
|
import xyz.quaver.pupil.hitomi.getSuggestionsForQuery
|
||||||
|
import xyz.quaver.pupil.searchHistory
|
||||||
import xyz.quaver.pupil.services.DownloadService
|
import xyz.quaver.pupil.services.DownloadService
|
||||||
import xyz.quaver.pupil.types.*
|
import xyz.quaver.pupil.types.FavoriteHistorySwitch
|
||||||
|
import xyz.quaver.pupil.types.LoadingSuggestion
|
||||||
|
import xyz.quaver.pupil.types.NoResultSuggestion
|
||||||
|
import xyz.quaver.pupil.types.Suggestion
|
||||||
|
import xyz.quaver.pupil.types.Tag
|
||||||
|
import xyz.quaver.pupil.types.TagSuggestion
|
||||||
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.MainView
|
||||||
@@ -73,10 +93,19 @@ import kotlin.math.max
|
|||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
val sortModeLookup = mapOf(
|
||||||
|
R.id.main_menu_sort_date_added to SortMode.DATE_ADDED,
|
||||||
|
R.id.main_menu_sort_date_published to SortMode.DATE_PUBLISHED,
|
||||||
|
R.id.main_menu_sort_popular_today to SortMode.POPULAR_TODAY,
|
||||||
|
R.id.main_menu_sort_popular_week to SortMode.POPULAR_WEEK,
|
||||||
|
R.id.main_menu_sort_popular_month to SortMode.POPULAR_MONTH,
|
||||||
|
R.id.main_menu_sort_popular_year to SortMode.POPULAR_YEAR,
|
||||||
|
R.id.main_menu_sort_random to SortMode.RANDOM
|
||||||
|
)
|
||||||
|
|
||||||
class MainActivity :
|
class MainActivity :
|
||||||
BaseActivity(),
|
BaseActivity(),
|
||||||
NavigationView.OnNavigationItemSelectedListener
|
NavigationView.OnNavigationItemSelectedListener {
|
||||||
{
|
|
||||||
|
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
SEARCH,
|
SEARCH,
|
||||||
@@ -85,25 +114,21 @@ class MainActivity :
|
|||||||
FAVORITE
|
FAVORITE
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class SortMode {
|
|
||||||
NEWEST,
|
|
||||||
POPULAR
|
|
||||||
}
|
|
||||||
|
|
||||||
private val galleries = ArrayList<Int>()
|
private val galleries = ArrayList<Int>()
|
||||||
|
|
||||||
private var query = ""
|
private var query = ""
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
with(findViewById<SearchInputView>(R.id.search_bar_text)) {
|
with(findViewById<SearchInputView>(R.id.search_bar_text)) {
|
||||||
if (text.toString() != value)
|
if (text.toString() != value)
|
||||||
setText(query, TextView.BufferType.EDITABLE)
|
setText(query, TextView.BufferType.EDITABLE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
private var queryStack = mutableListOf<String>()
|
private var queryStack = mutableListOf<String>()
|
||||||
|
|
||||||
private var mode = Mode.SEARCH
|
private var mode = Mode.SEARCH
|
||||||
private var sortMode = SortMode.NEWEST
|
private var sortMode = SortMode.DATE_ADDED
|
||||||
|
|
||||||
private var galleryIDs: Deferred<List<Int>>? = null
|
private var galleryIDs: Deferred<List<Int>>? = null
|
||||||
private var totalItems = 0
|
private var totalItems = 0
|
||||||
@@ -112,11 +137,12 @@ class MainActivity :
|
|||||||
|
|
||||||
private lateinit var binding: MainActivityBinding
|
private lateinit var binding: MainActivityBinding
|
||||||
|
|
||||||
private val requestNotificationPermssionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
private val requestNotificationPermssionLauncher =
|
||||||
if (!isGranted) {
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||||
showNotificationPermissionExplanationDialog(this)
|
if (!isGranted) {
|
||||||
|
showNotificationPermissionExplanationDialog(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -127,9 +153,17 @@ class MainActivity :
|
|||||||
intent.dataString?.let { url ->
|
intent.dataString?.let { url ->
|
||||||
restore(url,
|
restore(url,
|
||||||
onFailure = {
|
onFailure = {
|
||||||
Snackbar.make(binding.contents.recyclerview, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show()
|
Snackbar.make(
|
||||||
|
binding.contents.recyclerview,
|
||||||
|
R.string.settings_backup_failed,
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
).show()
|
||||||
}, onSuccess = {
|
}, onSuccess = {
|
||||||
Snackbar.make(binding.contents.recyclerview, getString(R.string.settings_restore_success, it), Snackbar.LENGTH_LONG).show()
|
Snackbar.make(
|
||||||
|
binding.contents.recyclerview,
|
||||||
|
getString(R.string.settings_restore_success, it),
|
||||||
|
Snackbar.LENGTH_LONG
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -138,17 +172,24 @@ class MainActivity :
|
|||||||
requestNotificationPermission(this, requestNotificationPermssionLauncher, false) {}
|
requestNotificationPermission(this, requestNotificationPermssionLauncher, false) {}
|
||||||
|
|
||||||
if (Preferences["download_folder", ""].isEmpty())
|
if (Preferences["download_folder", ""].isEmpty())
|
||||||
DownloadLocationDialogFragment().show(supportFragmentManager, "Download Location Dialog")
|
DownloadLocationDialogFragment().show(
|
||||||
|
supportFragmentManager,
|
||||||
|
"Download Location Dialog"
|
||||||
|
)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Preferences["download_folder_ignore_warning", false] &&
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Preferences["download_folder_ignore_warning", false] &&
|
||||||
ContextCompat.getExternalFilesDirs(this, null).filterNotNull().map { Uri.fromFile(it).toString() }
|
ContextCompat.getExternalFilesDirs(this, null).filterNotNull()
|
||||||
|
.map { Uri.fromFile(it).toString() }
|
||||||
.contains(Preferences["download_folder", ""])
|
.contains(Preferences["download_folder", ""])
|
||||||
) {
|
) {
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setTitle(R.string.warning)
|
.setTitle(R.string.warning)
|
||||||
.setMessage(R.string.unaccessible_download_folder)
|
.setMessage(R.string.unaccessible_download_folder)
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
DownloadLocationDialogFragment().show(supportFragmentManager, "Download Location Dialog")
|
DownloadLocationDialogFragment().show(
|
||||||
|
supportFragmentManager,
|
||||||
|
"Download Location Dialog"
|
||||||
|
)
|
||||||
}.setNegativeButton(R.string.ignore) { _, _ ->
|
}.setNegativeButton(R.string.ignore) { _, _ ->
|
||||||
Preferences["download_folder_ignore_warning"] = true
|
Preferences["download_folder_ignore_warning"] = true
|
||||||
}.show()
|
}.show()
|
||||||
@@ -163,10 +204,12 @@ class MainActivity :
|
|||||||
checkUpdate(this)
|
checkUpdate(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
when {
|
when {
|
||||||
binding.drawer.isDrawerOpen(GravityCompat.START) -> binding.drawer.closeDrawer(GravityCompat.START)
|
binding.drawer.isDrawerOpen(GravityCompat.START) -> binding.drawer.closeDrawer(
|
||||||
|
GravityCompat.START
|
||||||
|
)
|
||||||
|
|
||||||
queryStack.removeLastOrNull() != null && queryStack.isNotEmpty() -> runOnUiThread {
|
queryStack.removeLastOrNull() != null && queryStack.isNotEmpty() -> runOnUiThread {
|
||||||
query = queryStack.last()
|
query = queryStack.last()
|
||||||
|
|
||||||
@@ -175,6 +218,7 @@ class MainActivity :
|
|||||||
fetchGalleries(query, sortMode)
|
fetchGalleries(query, sortMode)
|
||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> super.onBackPressed()
|
else -> super.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +233,7 @@ class MainActivity :
|
|||||||
val perPage = Preferences["per_page", "25"].toInt()
|
val perPage = Preferences["per_page", "25"].toInt()
|
||||||
val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
|
val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
|
||||||
|
|
||||||
return when(keyCode) {
|
return when (keyCode) {
|
||||||
KeyEvent.KEYCODE_VOLUME_UP -> {
|
KeyEvent.KEYCODE_VOLUME_UP -> {
|
||||||
if (currentPage > 0) {
|
if (currentPage > 0) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
@@ -204,6 +248,7 @@ class MainActivity :
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||||
if (currentPage < maxPage) {
|
if (currentPage < maxPage) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
@@ -218,20 +263,22 @@ class MainActivity :
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> super.onKeyDown(keyCode, event)
|
else -> super.onKeyDown(keyCode, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
binding.contents.recyclerview.addOnScrollListener(object: RecyclerView.OnScrollListener() {
|
binding.contents.recyclerview.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
// -height of the search view < translationY < 0
|
// -height of the search view < translationY < 0
|
||||||
binding.contents.searchview.translationY =
|
binding.contents.searchview.translationY =
|
||||||
min(
|
min(
|
||||||
max(
|
max(
|
||||||
binding.contents.searchview.translationY - dy,
|
binding.contents.searchview.translationY - dy,
|
||||||
-binding.contents.searchview.binding.querySection.root.height.toFloat()
|
-binding.contents.searchview.binding.querySection.root.height.toFloat()
|
||||||
), 0F)
|
), 0F
|
||||||
|
)
|
||||||
|
|
||||||
if (dy > 0)
|
if (dy > 0)
|
||||||
binding.contents.fab.hideMenuButton(true)
|
binding.contents.fab.hideMenuButton(true)
|
||||||
@@ -240,7 +287,12 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Linkify.addLinks(binding.contents.noresult, Pattern.compile(getString(R.string.https_text)), null, null, { _, _ -> getString(R.string.https) })
|
Linkify.addLinks(
|
||||||
|
binding.contents.noresult,
|
||||||
|
Pattern.compile(getString(R.string.https_text)),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{ _, _ -> getString(R.string.https) })
|
||||||
|
|
||||||
//NavigationView
|
//NavigationView
|
||||||
binding.navView.setNavigationItemSelectedListener(this)
|
binding.navView.setNavigationItemSelectedListener(this)
|
||||||
@@ -261,14 +313,17 @@ class MainActivity :
|
|||||||
AlertDialog.Builder(context).apply {
|
AlertDialog.Builder(context).apply {
|
||||||
setView(editText)
|
setView(editText)
|
||||||
setTitle(R.string.main_jump_title)
|
setTitle(R.string.main_jump_title)
|
||||||
setMessage(getString(
|
setMessage(
|
||||||
R.string.main_jump_message,
|
getString(
|
||||||
currentPage+1,
|
R.string.main_jump_message,
|
||||||
ceil(totalItems / perPage.toDouble()).roundToInt()
|
currentPage + 1,
|
||||||
))
|
ceil(totalItems / perPage.toDouble()).roundToInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
currentPage = (editText.text.toString().toIntOrNull() ?: return@setPositiveButton)-1
|
currentPage =
|
||||||
|
(editText.text.toString().toIntOrNull() ?: return@setPositiveButton) - 1
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
@@ -322,7 +377,8 @@ 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
|
||||||
|
|
||||||
GalleryDialog(this@MainActivity, galleryID).apply {
|
GalleryDialog(this@MainActivity, galleryID).apply {
|
||||||
onChipClickedHandler.add {
|
onChipClickedHandler.add {
|
||||||
@@ -344,7 +400,7 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
with(binding.contents.view) {
|
with(binding.contents.view) {
|
||||||
setOnPageTurnListener(object: MainView.OnPageTurnListener {
|
setOnPageTurnListener(object : MainView.OnPageTurnListener {
|
||||||
override fun onPrev(page: Int) {
|
override fun onPrev(page: Int) {
|
||||||
currentPage--
|
currentPage--
|
||||||
|
|
||||||
@@ -409,10 +465,11 @@ class MainActivity :
|
|||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
requestNotificationPermssionLauncher
|
requestNotificationPermssionLauncher
|
||||||
) {
|
) {
|
||||||
if (DownloadManager.getInstance(context).isDownloading(galleryID)) { //download in progress
|
if (DownloadManager.getInstance(context)
|
||||||
|
.isDownloading(galleryID)
|
||||||
|
) { //download in progress
|
||||||
DownloadService.cancel(this@MainActivity, galleryID)
|
DownloadService.cancel(this@MainActivity, galleryID)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
DownloadManager.getInstance(context).addDownloadFolder(galleryID)
|
DownloadManager.getInstance(context).addDownloadFolder(galleryID)
|
||||||
DownloadService.download(this@MainActivity, galleryID)
|
DownloadService.download(this@MainActivity, galleryID)
|
||||||
}
|
}
|
||||||
@@ -485,6 +542,7 @@ class MainActivity :
|
|||||||
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
||||||
} + FavoriteHistorySwitch(getString(R.string.search_show_histories))
|
} + FavoriteHistorySwitch(getString(R.string.search_show_histories))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
searchHistory.map {
|
searchHistory.map {
|
||||||
Suggestion(it)
|
Suggestion(it)
|
||||||
@@ -492,7 +550,7 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
}.reversed()
|
}.reversed()
|
||||||
|
|
||||||
private var suggestionJob : Job? = null
|
private var suggestionJob: Job? = null
|
||||||
private fun setupSearchBar() {
|
private fun setupSearchBar() {
|
||||||
with(binding.contents.searchview) {
|
with(binding.contents.searchview) {
|
||||||
val scrollSuggestionToTop = {
|
val scrollSuggestionToTop = {
|
||||||
@@ -500,7 +558,10 @@ class MainActivity :
|
|||||||
MainScope().launch {
|
MainScope().launch {
|
||||||
withTimeout(1000) {
|
withTimeout(1000) {
|
||||||
val layoutManager = layoutManager as LinearLayoutManager
|
val layoutManager = layoutManager as LinearLayoutManager
|
||||||
while (layoutManager.findLastVisibleItemPosition() != adapter?.itemCount?.minus(1)) {
|
while (layoutManager.findLastVisibleItemPosition() != adapter?.itemCount?.minus(
|
||||||
|
1
|
||||||
|
)
|
||||||
|
) {
|
||||||
layoutManager.scrollToPosition(adapter?.itemCount?.minus(1) ?: 0)
|
layoutManager.scrollToPosition(adapter?.itemCount?.minus(1) ?: 0)
|
||||||
delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
@@ -509,7 +570,7 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMenuStatusChangeListener = object: FloatingSearchView.OnMenuStatusChangeListener {
|
onMenuStatusChangeListener = object : FloatingSearchView.OnMenuStatusChangeListener {
|
||||||
override fun onMenuOpened() {
|
override fun onMenuOpened() {
|
||||||
(this@MainActivity.binding.contents.recyclerview.adapter as GalleryBlockAdapter).closeAllItems()
|
(this@MainActivity.binding.contents.recyclerview.adapter as GalleryBlockAdapter).closeAllItems()
|
||||||
}
|
}
|
||||||
@@ -561,7 +622,8 @@ class MainActivity :
|
|||||||
|
|
||||||
suggestionJob = CoroutineScope(Dispatchers.IO).launch {
|
suggestionJob = CoroutineScope(Dispatchers.IO).launch {
|
||||||
val suggestions = kotlin.runCatching {
|
val suggestions = kotlin.runCatching {
|
||||||
getSuggestionsForQuery(currentQuery).map { TagSuggestion(it) }.toMutableList()
|
getSuggestionsForQuery(currentQuery).map { TagSuggestion(it) }
|
||||||
|
.toMutableList()
|
||||||
}.getOrElse { mutableListOf() }
|
}.getOrElse { mutableListOf() }
|
||||||
|
|
||||||
suggestions.filter {
|
suggestions.filter {
|
||||||
@@ -573,12 +635,16 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
swapSuggestions(if (suggestions.isNotEmpty()) suggestions else listOf(NoResultSuggestion(getText(R.string.main_no_result).toString())))
|
swapSuggestions(
|
||||||
|
if (suggestions.isNotEmpty()) suggestions else listOf(
|
||||||
|
NoResultSuggestion(getText(R.string.main_no_result).toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocusChangeListener = object: FloatingSearchView.OnFocusChangeListener {
|
onFocusChangeListener = object : FloatingSearchView.OnFocusChangeListener {
|
||||||
override fun onFocus() {
|
override fun onFocus() {
|
||||||
if (query.isEmpty() or query.endsWith(' ')) {
|
if (query.isEmpty() or query.endsWith(' ')) {
|
||||||
swapSuggestions(defaultSuggestions)
|
swapSuggestions(defaultSuggestions)
|
||||||
@@ -604,8 +670,14 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onActionMenuItemSelected(item: MenuItem?) {
|
fun onActionMenuItemSelected(item: MenuItem?) {
|
||||||
when(item?.itemId) {
|
when (item?.itemId) {
|
||||||
R.id.main_menu_settings -> startActivity(Intent(this@MainActivity, SettingsActivity::class.java))
|
R.id.main_menu_settings -> startActivity(
|
||||||
|
Intent(
|
||||||
|
this@MainActivity,
|
||||||
|
SettingsActivity::class.java
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
R.id.main_menu_thin -> {
|
R.id.main_menu_thin -> {
|
||||||
val thin = !item.isChecked
|
val thin = !item.isChecked
|
||||||
|
|
||||||
@@ -620,21 +692,15 @@ class MainActivity :
|
|||||||
adapter = adapter // Force to redraw
|
adapter = adapter // Force to redraw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.main_menu_sort_newest -> {
|
|
||||||
sortMode = SortMode.NEWEST
|
|
||||||
item.isChecked = true
|
|
||||||
|
|
||||||
runOnUiThread {
|
R.id.main_menu_sort_date_added,
|
||||||
currentPage = 0
|
R.id.main_menu_sort_date_published,
|
||||||
|
R.id.main_menu_sort_popular_today,
|
||||||
cancelFetch()
|
R.id.main_menu_sort_popular_week,
|
||||||
clearGalleries()
|
R.id.main_menu_sort_popular_month,
|
||||||
fetchGalleries(query, sortMode)
|
R.id.main_menu_sort_popular_year,
|
||||||
loadBlocks()
|
R.id.main_menu_sort_random -> {
|
||||||
}
|
sortMode = sortModeLookup[item.itemId]!!
|
||||||
}
|
|
||||||
R.id.main_menu_sort_popular -> {
|
|
||||||
sortMode = SortMode.POPULAR
|
|
||||||
item.isChecked = true
|
item.isChecked = true
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
@@ -653,7 +719,7 @@ class MainActivity :
|
|||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
binding.drawer.closeDrawers()
|
binding.drawer.closeDrawers()
|
||||||
|
|
||||||
when(item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.main_drawer_home -> {
|
R.id.main_drawer_home -> {
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
@@ -664,6 +730,7 @@ class MainActivity :
|
|||||||
fetchGalleries(query, sortMode)
|
fetchGalleries(query, sortMode)
|
||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_history -> {
|
R.id.main_drawer_history -> {
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
@@ -674,6 +741,7 @@ class MainActivity :
|
|||||||
fetchGalleries(query, sortMode)
|
fetchGalleries(query, sortMode)
|
||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_downloads -> {
|
R.id.main_drawer_downloads -> {
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
@@ -684,6 +752,7 @@ class MainActivity :
|
|||||||
fetchGalleries(query, sortMode)
|
fetchGalleries(query, sortMode)
|
||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_favorite -> {
|
R.id.main_drawer_favorite -> {
|
||||||
cancelFetch()
|
cancelFetch()
|
||||||
clearGalleries()
|
clearGalleries()
|
||||||
@@ -694,20 +763,35 @@ class MainActivity :
|
|||||||
fetchGalleries(query, sortMode)
|
fetchGalleries(query, sortMode)
|
||||||
loadBlocks()
|
loadBlocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_help -> {
|
R.id.main_drawer_help -> {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help))))
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help))))
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_github -> {
|
R.id.main_drawer_github -> {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github))))
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github))))
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_homepage -> {
|
R.id.main_drawer_homepage -> {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.home_page))))
|
startActivity(
|
||||||
|
Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse(getString(R.string.home_page))
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_email -> {
|
R.id.main_drawer_email -> {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.main_drawer_kakaotalk -> {
|
R.id.main_drawer_kakaotalk -> {
|
||||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.discord))))
|
startActivity(
|
||||||
|
Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse(getString(R.string.discord))
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -745,17 +829,18 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (query.isNotEmpty() && mode != Mode.SEARCH) {
|
if (query.isNotEmpty() && mode != Mode.SEARCH) {
|
||||||
Snackbar.make(binding.contents.recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT).apply {
|
Snackbar.make(binding.contents.recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT)
|
||||||
setAction(android.R.string.ok) {
|
.apply {
|
||||||
cancelFetch()
|
setAction(android.R.string.ok) {
|
||||||
clearGalleries()
|
cancelFetch()
|
||||||
currentPage = 0
|
clearGalleries()
|
||||||
mode = Mode.SEARCH
|
currentPage = 0
|
||||||
queryStack.clear()
|
mode = Mode.SEARCH
|
||||||
fetchGalleries(query, sortMode)
|
queryStack.clear()
|
||||||
loadBlocks()
|
fetchGalleries(query, sortMode)
|
||||||
}
|
loadBlocks()
|
||||||
}.show()
|
}
|
||||||
|
}.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
galleryIDs = null
|
galleryIDs = null
|
||||||
@@ -764,22 +849,16 @@ class MainActivity :
|
|||||||
return
|
return
|
||||||
|
|
||||||
galleryIDs = CoroutineScope(Dispatchers.IO).async {
|
galleryIDs = CoroutineScope(Dispatchers.IO).async {
|
||||||
when(mode) {
|
when (mode) {
|
||||||
Mode.SEARCH -> {
|
Mode.SEARCH -> {
|
||||||
when {
|
doSearch(
|
||||||
query.isEmpty() and defaultQuery.isEmpty() -> {
|
"$defaultQuery $query",
|
||||||
when(sortMode) {
|
sortMode
|
||||||
SortMode.POPULAR -> getGalleryIDsFromNozomi(null, "popular", "all")
|
).also {
|
||||||
else -> getGalleryIDsFromNozomi(null, "index", "all")
|
totalItems = it.size
|
||||||
}.also {
|
|
||||||
totalItems = it.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> doSearch("$defaultQuery $query", sortMode == SortMode.POPULAR).also {
|
|
||||||
totalItems = it.size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mode.HISTORY -> {
|
Mode.HISTORY -> {
|
||||||
when {
|
when {
|
||||||
query.isEmpty() -> {
|
query.isEmpty() -> {
|
||||||
@@ -787,36 +866,42 @@ class MainActivity :
|
|||||||
totalItems = it.size
|
totalItems = it.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val result = doSearch(query).sorted()
|
val result = doSearch(query, SortMode.DATE_ADDED).sorted()
|
||||||
histories.reversed().filter { result.binarySearch(it) >= 0 }.also {
|
histories.reversed().filter { result.binarySearch(it) >= 0 }.also {
|
||||||
totalItems = it.size
|
totalItems = it.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mode.DOWNLOAD -> {
|
Mode.DOWNLOAD -> {
|
||||||
val downloads = DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList()
|
val downloads =
|
||||||
|
DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList()
|
||||||
|
|
||||||
when {
|
when {
|
||||||
query.isEmpty() -> downloads.reversed().also {
|
query.isEmpty() -> downloads.reversed().also {
|
||||||
totalItems = it.size
|
totalItems = it.size
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val result = doSearch(query).sorted()
|
val result = doSearch(query, SortMode.DATE_ADDED).sorted()
|
||||||
downloads.reversed().filter { result.binarySearch(it) >= 0 }.also {
|
downloads.reversed().filter { result.binarySearch(it) >= 0 }.also {
|
||||||
totalItems = it.size
|
totalItems = it.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mode.FAVORITE -> {
|
Mode.FAVORITE -> {
|
||||||
when {
|
when {
|
||||||
query.isEmpty() -> favorites.reversed().also {
|
query.isEmpty() -> favorites.reversed().also {
|
||||||
totalItems = it.size
|
totalItems = it.size
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val result = doSearch(query).sorted()
|
val result = doSearch(query, SortMode.DATE_ADDED).sorted()
|
||||||
favorites.reversed().filter { result.binarySearch(it) >= 0 }.also {
|
favorites.reversed().filter { result.binarySearch(it) >= 0 }.also {
|
||||||
totalItems = it.size
|
totalItems = it.size
|
||||||
}
|
}
|
||||||
@@ -849,10 +934,18 @@ class MainActivity :
|
|||||||
}
|
}
|
||||||
|
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
binding.contents.view.setCurrentPage(currentPage + 1, galleryIDs.size > (currentPage+1)*perPage)
|
binding.contents.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 ->
|
||||||
async {
|
async {
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class DefaultQueryDialog : DialogFragment() {
|
|||||||
s.replace(
|
s.replace(
|
||||||
0,
|
0,
|
||||||
s.length,
|
s.length,
|
||||||
s.toString().toLowerCase(java.util.Locale.getDefault())
|
s.toString().lowercase()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package xyz.quaver.pupil.ui.dialog
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
@@ -29,8 +28,6 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import net.rdrei.android.dirchooser.DirectoryChooserActivity
|
|
||||||
import net.rdrei.android.dirchooser.DirectoryChooserConfig
|
|
||||||
import xyz.quaver.io.FileX
|
import xyz.quaver.io.FileX
|
||||||
import xyz.quaver.io.util.toFile
|
import xyz.quaver.io.util.toFile
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
@@ -56,8 +53,7 @@ class DownloadLocationDialogFragment : DialogFragment() {
|
|||||||
it.data?.data?.also { uri ->
|
it.data?.data?.also { uri ->
|
||||||
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
context.contentResolver.takePersistableUriPermission(uri, takeFlags)
|
||||||
context.contentResolver.takePersistableUriPermission(uri, takeFlags)
|
|
||||||
|
|
||||||
if (kotlin.runCatching { FileX(context, uri).canWrite() }.getOrDefault(false)) {
|
if (kotlin.runCatching { FileX(context, uri).canWrite() }.getOrDefault(false)) {
|
||||||
entries[null]?.locationAvailable?.text = uri.toFile(context)?.canonicalPath
|
entries[null]?.locationAvailable?.text = uri.toFile(context)?.canonicalPath
|
||||||
@@ -87,32 +83,6 @@ class DownloadLocationDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val requestDownloadFolderOldLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
val context = context ?: return@registerForActivityResult
|
|
||||||
val dialog = dialog ?: return@registerForActivityResult
|
|
||||||
|
|
||||||
if (it.resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
|
|
||||||
val directory = it.data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
|
|
||||||
|
|
||||||
if (!File(directory).canWrite()) {
|
|
||||||
Snackbar.make(
|
|
||||||
dialog.window!!.decorView.rootView,
|
|
||||||
R.string.settings_download_folder_not_writable,
|
|
||||||
Snackbar.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
|
|
||||||
val downloadFolder = DownloadManager.getInstance(context).downloadFolder.canonicalPath
|
|
||||||
val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder }
|
|
||||||
entries[key]!!.button.isChecked = true
|
|
||||||
if (key == null) entries[key]!!.locationAvailable.text = downloadFolder
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
entries[null]?.locationAvailable?.text = directory
|
|
||||||
Preferences["download_folder"] = File(directory).toURI().toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
val externalFilesDirs = ContextCompat.getExternalFilesDirs(requireContext(), null)
|
val externalFilesDirs = ContextCompat.getExternalFilesDirs(requireContext(), null)
|
||||||
|
|
||||||
@@ -147,24 +117,11 @@ class DownloadLocationDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
button.performClick()
|
button.performClick()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
putExtra("android.content.extra.SHOW_ADVANCED", true)
|
||||||
putExtra("android.content.extra.SHOW_ADVANCED", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestDownloadFolderLauncher.launch(intent)
|
|
||||||
} else { // Can't use SAF on old Androids!
|
|
||||||
val config = DirectoryChooserConfig.builder()
|
|
||||||
.newDirectoryName("Pupil")
|
|
||||||
.allowNewDirectoryNameModification(true)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val intent = Intent(context, DirectoryChooserActivity::class.java).apply {
|
|
||||||
putExtra(DirectoryChooserActivity.EXTRA_CONFIG, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestDownloadFolderOldLauncher.launch(intent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestDownloadFolderLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
entries[null] = this
|
entries[null] = this
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class FloatingSearchView @JvmOverloads constructor(context: Context, attrs: Attr
|
|||||||
s ?: return
|
s ?: return
|
||||||
|
|
||||||
if (s.any { it.isUpperCase() })
|
if (s.any { it.isUpperCase() })
|
||||||
s.replace(0, s.length, s.toString().toLowerCase(Locale.getDefault()))
|
s.replace(0, s.length, s.toString().lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) {
|
override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
~ Copyright (C) 2020 tom5079
|
~ Copyright (C) 2020 tom5079
|
||||||
~
|
~
|
||||||
@@ -18,8 +17,8 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
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">
|
||||||
@@ -32,44 +31,43 @@
|
|||||||
<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"
|
||||||
|
app:addLastItemPadding="true"
|
||||||
app:handleDrawable="@drawable/thumb"
|
app:handleDrawable="@drawable/thumb"
|
||||||
app:handleHasFixedSize="true"
|
app:handleHasFixedSize="true"
|
||||||
app:handleHeight="72dp"
|
app:handleHeight="72dp"
|
||||||
|
app:handleVisibilityDuration="1000"
|
||||||
app:handleWidth="24dp"
|
app:handleWidth="24dp"
|
||||||
app:disableTrack="true"
|
app:popupDrawable="@android:color/transparent"
|
||||||
app:hideHandleAfter="1000"
|
app:trackMarginStart="64dp">
|
||||||
app:trackMarginStart="64dp"
|
|
||||||
app:addLastItemPadding="true"
|
|
||||||
app:popupDrawable="@android:color/transparent">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerview"
|
android:id="@+id/recyclerview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingTop="64dp"
|
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
android:paddingTop="64dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||||
|
|
||||||
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
|
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
|
||||||
|
|
||||||
</xyz.quaver.pupil.ui.view.MainView>
|
</xyz.quaver.pupil.ui.view.MainView>
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:id="@+id/progressbar"
|
android:id="@+id/progressbar"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
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:indeterminate="true"/>
|
android:indeterminate="true" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/noresult"
|
android:id="@+id/noresult"
|
||||||
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:text="@string/main_no_result"
|
|
||||||
android:linksClickable="true"
|
android:linksClickable="true"
|
||||||
android:visibility="invisible"/>
|
android:text="@string/main_no_result"
|
||||||
|
android:visibility="invisible" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionMenu
|
<com.github.clans.fab.FloatingActionMenu
|
||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
@@ -84,28 +82,28 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_label="@string/main_fab_cancel"
|
app:fab_label="@string/main_fab_cancel"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/jump_fab"
|
android:id="@+id/jump_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_label="@string/main_jump_title"
|
app:fab_label="@string/main_jump_title"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/random_fab"
|
android:id="@+id/random_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_label="@string/main_fab_random"
|
app:fab_label="@string/main_fab_random"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/id_fab"
|
android:id="@+id/id_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:fab_label="@string/main_open_gallery_by_id"
|
app:fab_label="@string/main_open_gallery_by_id"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini" />
|
||||||
|
|
||||||
</com.github.clans.fab.FloatingActionMenu>
|
</com.github.clans.fab.FloatingActionMenu>
|
||||||
|
|
||||||
@@ -113,15 +111,15 @@
|
|||||||
android:id="@+id/searchview"
|
android:id="@+id/searchview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
app:close_search_on_keyboard_dismiss="false"
|
||||||
|
app:dismissOnOutsideTouch="true"
|
||||||
|
app:leftActionMode="showHamburger"
|
||||||
|
app:menu="@menu/main"
|
||||||
app:searchBarMarginLeft="6dp"
|
app:searchBarMarginLeft="6dp"
|
||||||
app:searchBarMarginRight="6dp"
|
app:searchBarMarginRight="6dp"
|
||||||
app:searchBarMarginTop="6dp"
|
app:searchBarMarginTop="6dp"
|
||||||
app:searchHint="@string/search_hint"
|
app:searchHint="@string/search_hint"
|
||||||
app:suggestionAnimDuration="250"
|
|
||||||
app:showSearchKey="true"
|
app:showSearchKey="true"
|
||||||
app:leftActionMode="showHamburger"
|
app:suggestionAnimDuration="250" />
|
||||||
app:menu="@menu/main"
|
|
||||||
app:dismissOnOutsideTouch="true"
|
|
||||||
app:close_search_on_keyboard_dismiss="false" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
~ Pupil, Hitomi.la viewer for Android
|
||||||
~ Copyright (C) 2019 tom5079
|
~ Copyright (C) 2019 tom5079
|
||||||
~
|
~
|
||||||
@@ -29,37 +28,37 @@
|
|||||||
android:id="@+id/scroller"
|
android:id="@+id/scroller"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:handleDrawable="@drawable/thumb"
|
|
||||||
app:handleHeight="72dp"
|
|
||||||
app:handleWidth="24dp"
|
|
||||||
app:disableTrack="true"
|
|
||||||
app:hideHandleAfter="1000"
|
|
||||||
app:handleHasFixedSize="true"
|
|
||||||
app:addLastItemPadding="true"
|
app:addLastItemPadding="true"
|
||||||
|
app:handleDrawable="@drawable/thumb"
|
||||||
|
app:handleHasFixedSize="true"
|
||||||
|
app:handleHeight="72dp"
|
||||||
|
app:handleVisibilityDuration="1000"
|
||||||
|
app:handleWidth="24dp"
|
||||||
app:popupDrawable="@android:color/transparent">
|
app:popupDrawable="@android:color/transparent">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerview"
|
android:id="@+id/recyclerview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||||
|
|
||||||
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
|
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
|
||||||
|
|
||||||
<include layout="@layout/reader_eye_card"
|
<include
|
||||||
android:id="@+id/eye_card"
|
android:id="@+id/eye_card"
|
||||||
android:visibility="gone"
|
layout="@layout/reader_eye_card"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:layout_margin="8dp"/>
|
android:layout_margin="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/download_progressbar"
|
android:id="@+id/download_progressbar"
|
||||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="4dp"/>
|
android:layout_height="4dp" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionMenu
|
<com.github.clans.fab.FloatingActionMenu
|
||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -72,33 +71,33 @@
|
|||||||
android:id="@+id/download_fab"
|
android:id="@+id/download_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:srcCompat="@drawable/ic_download"
|
|
||||||
app:fab_label="@string/reader_fab_download"
|
app:fab_label="@string/reader_fab_download"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini"
|
||||||
|
app:srcCompat="@drawable/ic_download" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/retry_fab"
|
android:id="@+id/retry_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:srcCompat="@drawable/refresh"
|
|
||||||
app:fab_label="@string/reader_fab_retry"
|
app:fab_label="@string/reader_fab_retry"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini"
|
||||||
|
app:srcCompat="@drawable/refresh" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/auto_fab"
|
android:id="@+id/auto_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:srcCompat="@drawable/eye_white"
|
|
||||||
app:fab_label="@string/reader_fab_auto"
|
app:fab_label="@string/reader_fab_auto"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini"
|
||||||
|
app:srcCompat="@drawable/eye_white" />
|
||||||
|
|
||||||
<com.github.clans.fab.FloatingActionButton
|
<com.github.clans.fab.FloatingActionButton
|
||||||
android:id="@+id/fullscreen_fab"
|
android:id="@+id/fullscreen_fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:srcCompat="@drawable/ic_fullscreen"
|
|
||||||
app:fab_label="@string/reader_fab_fullscreen"
|
app:fab_label="@string/reader_fab_fullscreen"
|
||||||
app:fab_size="mini"/>
|
app:fab_size="mini"
|
||||||
|
app:srcCompat="@drawable/ic_fullscreen" />
|
||||||
|
|
||||||
</com.github.clans.fab.FloatingActionMenu>
|
</com.github.clans.fab.FloatingActionMenu>
|
||||||
|
|
||||||
|
|||||||
@@ -26,11 +26,21 @@
|
|||||||
app:showAsAction="ifRoom">
|
app:showAsAction="ifRoom">
|
||||||
<menu>
|
<menu>
|
||||||
<group android:checkableBehavior="single">
|
<group android:checkableBehavior="single">
|
||||||
<item android:id="@+id/main_menu_sort_newest"
|
<item android:id="@+id/main_menu_sort_date_added"
|
||||||
android:title="@string/main_menu_sort_newest"
|
android:title="@string/main_menu_sort_date_added"
|
||||||
android:checked="true"/>
|
android:checked="true"/>
|
||||||
<item android:id="@+id/main_menu_sort_popular"
|
<item android:id="@+id/main_menu_sort_date_published"
|
||||||
android:title="@string/main_menu_sort_popular"/>
|
android:title="@string/main_menu_sort_date_published"/>
|
||||||
|
<item android:id="@+id/main_menu_sort_popular_today"
|
||||||
|
android:title="@string/main_menu_sort_popular_today"/>
|
||||||
|
<item android:id="@+id/main_menu_sort_popular_week"
|
||||||
|
android:title="@string/main_menu_sort_popular_week"/>
|
||||||
|
<item android:id="@+id/main_menu_sort_popular_month"
|
||||||
|
android:title="@string/main_menu_sort_popular_month"/>
|
||||||
|
<item android:id="@+id/main_menu_sort_popular_year"
|
||||||
|
android:title="@string/main_menu_sort_popular_year"/>
|
||||||
|
<item android:id="@+id/main_menu_sort_random"
|
||||||
|
android:title="@string/main_menu_sort_random"/>
|
||||||
</group>
|
</group>
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
26
app/src/main/res/values-in/arrays.xml
Normal file
26
app/src/main/res/values-in/arrays.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
<string-array name="proxy_type">
|
||||||
|
<item>Tanpa Proxy</item>
|
||||||
|
<item>HTTP</item>
|
||||||
|
<item>SOCKS</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
</resources>
|
||||||
236
app/src/main/res/values-in/strings.xml
Normal file
236
app/src/main/res/values-in/strings.xml
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<string name="warning">Peringatan</string>
|
||||||
|
<string name="error">Kesalahan</string>
|
||||||
|
|
||||||
|
<string name="ignore">Abaikan</string>
|
||||||
|
|
||||||
|
<string name="unlimited">Tidak Terbatas</string>
|
||||||
|
|
||||||
|
<string name="copied_to_clipboard">Disalin ke papan klip</string>
|
||||||
|
|
||||||
|
<string name="channel_download">Unduh</string>
|
||||||
|
<string name="channel_download_description">Tampilkan status unduhan</string>
|
||||||
|
|
||||||
|
<string name="channel_downloader">Pengunduh</string>
|
||||||
|
<string name="channel_downloader_description">Tampilkan status pengunduh</string>
|
||||||
|
|
||||||
|
<string name="channel_update">Perbarui</string>
|
||||||
|
<string name="channel_update_description">Tampilkan kemajuan pembaruan</string>
|
||||||
|
|
||||||
|
<string name="channel_transfer">Transfer</string>
|
||||||
|
<string name="channel_transfer_description">Tampilkan kemajuan transfer data ke perangkat lain</string>
|
||||||
|
|
||||||
|
<string name="unable_to_connect">Gagal tersambung ke hitomi.la</string>
|
||||||
|
|
||||||
|
<string name="lock_corrupted">Berkas kunci rusak! Dimohon untuk menginstal ulang Pupil</string>
|
||||||
|
|
||||||
|
<string name="main_no_result">Hasil kosong</string>
|
||||||
|
|
||||||
|
<string name="unaccessible_download_folder">Dari Android 11 dan yang lebih baru, folder Unduhan yang sekarang tidak akan bisa diakses dari aplikasi luar. Pindahkan folder Unduhan?</string>
|
||||||
|
|
||||||
|
<string name="notification_denied">Izin memunculkan notifikasi dibutuhkan untuk melakukan unduhan latar belakang. Jika anda menolak notifikasi dari aplikasi ini, pembaruan dari dalam aplikasi dan unduhan latar belakang akan dinonaktifkan.</string>
|
||||||
|
|
||||||
|
<string name="main_drawer_home">Beranda</string>
|
||||||
|
<string name="main_drawer_history">Riwayat</string>
|
||||||
|
<string name="main_drawer_downloads">Diunduh</string>
|
||||||
|
<string name="main_drawer_favorite">Favorit</string>
|
||||||
|
<string name="main_drawer_group_contact_title">Kontak</string>
|
||||||
|
<string name="main_drawer_group_contact_help">Bantuan</string>
|
||||||
|
<string name="main_drawer_group_contact_homepage">Kunjungi situs apl.</string>
|
||||||
|
<string name="main_drawer_group_contact_github">Kunjungi github</string>
|
||||||
|
<string name="main_drawer_group_contact_email">Email saya!</string>
|
||||||
|
<string name="main_drawer_grouop_contact_discord">Discord</string>
|
||||||
|
|
||||||
|
<string name="main_menu_thin">Mode Tipis</string>
|
||||||
|
|
||||||
|
<string name="main_menu_sort">Urutan</string>
|
||||||
|
<string name="main_menu_sort_date_added">Tanggal Ditambahkan</string>
|
||||||
|
<string name="main_menu_sort_date_published">Tanggal Publikasi</string>
|
||||||
|
<string name="main_menu_sort_popular_today">Populer: Hari Ini</string>
|
||||||
|
<string name="main_menu_sort_popular_week">Populer: Minggu Ini</string>
|
||||||
|
<string name="main_menu_sort_popular_month">Populer: Bulan Ini</string>
|
||||||
|
<string name="main_menu_sort_popular_year">Populer: Tahun ini</string>
|
||||||
|
<string name="main_menu_sort_random">Acak</string>
|
||||||
|
|
||||||
|
<string name="main_jump_title">Lompat ke Halaman</string>
|
||||||
|
<string name="main_jump_message">Sekarang halaman: %1$d\nHalaman paling belakang: %2$d</string>
|
||||||
|
<string name="main_open_gallery_by_id">Buka Galeri dari ID</string>
|
||||||
|
<string name="reader_failed_to_find_gallery">Gagal membuka galeri</string>
|
||||||
|
<string name="main_fab_random">Buka galeri acak</string>
|
||||||
|
<string name="main_fab_cancel">Batalkan semua unduhan</string>
|
||||||
|
|
||||||
|
<string name="main_move_to_page">Pergi ke halaman %1$d</string>
|
||||||
|
|
||||||
|
<string name="main_download">UNDUH</string>
|
||||||
|
<string name="main_delete">HAPUS</string>
|
||||||
|
|
||||||
|
<string name="update_title">Pembaruan tersedia</string>
|
||||||
|
<string name="update_download_completed">Unduhan Pembaruan Selesai</string>
|
||||||
|
<string name="update_download_completed_description">Tap disini untuh memperbarui</string>
|
||||||
|
<string name="update_notification_description">Mengunduh pembaruan…</string>
|
||||||
|
<string name="update_release_note"># Catatan rilis (v%1$s)\n%2$s</string>
|
||||||
|
|
||||||
|
<string name="search_hint">Pencarian galeri</string>
|
||||||
|
<string name="search_all">Mencari seluruh galeri</string>
|
||||||
|
<string name="search_show_histories">Tampilkan riwayat</string>
|
||||||
|
<string name="search_show_tags">Tampilkan tag favorit</string>
|
||||||
|
|
||||||
|
<string name="gallery_details">Rincian</string>
|
||||||
|
<string name="gallery_thumbnails">Thumbnail</string>
|
||||||
|
<string name="gallery_related">Galeri Terkait</string>
|
||||||
|
<string name="gallery_artists">Seniman</string>
|
||||||
|
<string name="gallery_groups">Grup</string>
|
||||||
|
<string name="gallery_language">Bahasa</string>
|
||||||
|
<string name="gallery_series">Seri</string>
|
||||||
|
<string name="gallery_characters">Karakter</string>
|
||||||
|
<string name="gallery_tags">Tag</string>
|
||||||
|
|
||||||
|
<string name="galleryblock_series">Seri: %1$s</string>
|
||||||
|
<string name="galleryblock_type">Jenis: %1$s</string>
|
||||||
|
<string name="galleryblock_language">Bahasa: %1$s</string>
|
||||||
|
|
||||||
|
<!-- READER -->
|
||||||
|
|
||||||
|
<string name="reader_loading">Memuat</string>
|
||||||
|
<string name="reader_go_to_page">Ke halaman</string>
|
||||||
|
<string name="reader_fab_fullscreen">Layar penuh</string>>
|
||||||
|
<string name="reader_fab_retry">Coba lagi</string>
|
||||||
|
<string name="reader_fab_auto">Gulir dengan kedipan mata</string>
|
||||||
|
<string name="reader_fab_auto_cancel">Berhenti gulir dengan kedipan mata</string>
|
||||||
|
<string name="reader_fab_download">Unduhan latar belakang</string>
|
||||||
|
<string name="reader_fab_download_cancel">Batalkan unduhan latar belakang</string>
|
||||||
|
<string name="reader_notification_text">Mengunduh…</string>
|
||||||
|
<string name="reader_notification_complete">Unduhan selesai</string>
|
||||||
|
|
||||||
|
<string name="camera_denied">Deteksi kedipan mata tidak bisa berfungsi tanpa izin kamera</string>
|
||||||
|
<string name="no_camera">Tidak terdeteksi kamera depan di perangkat ini</string>
|
||||||
|
|
||||||
|
<!-- DOWNLOADER -->
|
||||||
|
<string name="downloader_running">Pengunduh dimulai…</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS -->
|
||||||
|
|
||||||
|
<string name="settings_title">Pengaturan</string>
|
||||||
|
|
||||||
|
<string name="settings_app_version_title">Versi aplikasi (Tap untuk memeriksa pembaruan)</string>
|
||||||
|
<string name="settings_app_version_description">v%s</string>
|
||||||
|
<string name="settings_beta">Perbarui dari kanal beta</string>
|
||||||
|
|
||||||
|
<!-- SEARCH -->
|
||||||
|
|
||||||
|
<string name="settings_search_title">Pengaturan pencarian</string>
|
||||||
|
<string name="settings_galleries_per_page">Galeri per halaman</string>
|
||||||
|
<string name="settings_default_query">Kueri pencarian default</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/STORAGE -->
|
||||||
|
|
||||||
|
<string name="settings_storage">Penyimpanan</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/STORAGE / MANAGE STORAGE -->
|
||||||
|
|
||||||
|
<string name="settings_manage_storage">Kelola Penyimpanan</string>
|
||||||
|
<string name="settings_storage_usage">Sedang menggunakan %s</string>
|
||||||
|
<string name="settings_storage_usage_loading">Menghitung penggunaan penyimpanan…</string>
|
||||||
|
<string name="settings_clear_cache">Bersihkan cache</string>
|
||||||
|
<string name="settings_clear_cache_alert_message">Menghapus cache bisa mempengaruhi kecepatan memuat gambar, lanjutkan?</string>
|
||||||
|
<string name="settings_recover_downloads">Buat ulang daftar terunduh</string>
|
||||||
|
<string name="settings_clear_downloads">Bersihkan daftar unduhan</string>
|
||||||
|
<string name="settings_clear_downloads_alert_message">Ini akan menghapus semua hasil unduhan.\nLanjutkan?</string>
|
||||||
|
<string name="settings_clear_history">Hapus riwayat</string>
|
||||||
|
<string name="settings_clear_history_alert_message">Ini akan menghapus seluruh isi riwayat, lanjutkan?</string>
|
||||||
|
<string name="settings_clear_history_summary">%1$d entri riwayat disimpan</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/STORAGE / MISCELLANEOUS -->
|
||||||
|
|
||||||
|
<string name="settings_download_folder_name">Pola nama folder</string>
|
||||||
|
<string name="settings_invalid_download_folder_name">Pola nama folder mengandung karakter yang tidak sah.</string>
|
||||||
|
<string name="settings_download_folder_name_message">%s akan digantikan dengan nilai yang disesuaikan\n\n%s</string>
|
||||||
|
<string name="settings_download_folder">Folder unduhan</string>
|
||||||
|
<string name="settings_download_folder_removable">Penyimpanan Lepasan</string>
|
||||||
|
<string name="settings_download_folder_internal">Penyimpanan Internal</string>
|
||||||
|
<string name="settings_download_folder_available">%s tersedia</string>
|
||||||
|
<string name="settings_download_folder_custom">Lokasi Kustom</string>
|
||||||
|
<string name="settings_download_folder_not_writable">Folder tidak dapat ditulisi. Silahkan pilih yang lain.</string>
|
||||||
|
<string name="settings_cache_limit">Batas Besar Cache</string>
|
||||||
|
<string name="settings_nomedia_title">Sembunyikan gambar dari galeri</string>
|
||||||
|
<string name="settings_low_quality">Gambar kualitas rendah</string>
|
||||||
|
<string name="settings_low_quality_summary">Muat versi gambar kualitas rendah untuk menghemat waktu muat dan penggunaan data</string>
|
||||||
|
<string name="settings_transfer_data">Transfer data ke perangkat lain</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/APP LOCK -->
|
||||||
|
|
||||||
|
<string name="settings_app_lock">Kunci Aplikasi</string>
|
||||||
|
<string name="settings_app_lock_type">Jenis kunci aplikasi</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/NETWORKING -->
|
||||||
|
<string name="settings_networking">Jaringan</string>
|
||||||
|
<string name="settings_mirror_summary">Muat gambar dari jaringan mirror</string>
|
||||||
|
<string name="settings_proxy_title">Proxy</string>
|
||||||
|
<string name="settings_max_concurrent_download">Jumlah Unduhan Bersamaan</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/MISCELLANEOUS -->
|
||||||
|
|
||||||
|
<string name="settings_miscellaneous_title">Lain-Lain</string>
|
||||||
|
<string name="settings_tag_translation">Bahasa Tag</string>
|
||||||
|
<string name="settings_tag_translation_message">Berpartisipasi dalam terjemahan di GitHub</string>
|
||||||
|
<string name="settings_rtl">Membalik halaman dari kanan-ke-kiri</string>
|
||||||
|
<string name="settings_security_mode_title">Nyalakan mode kemananan</string>
|
||||||
|
<string name="settings_security_mode_summary">Buat tampilan tidak bisa dilihat dari aplikasi baru-baru ini dan tangkapan layar.</string>
|
||||||
|
<string name="settings_dark_mode_title">Tema gelap</string>
|
||||||
|
<string name="settings_dark_mode_summary">Jaga matamu dari sinar terang layar.</string>
|
||||||
|
<string name="settings_import_old_galleries">Impor galeri lama</string>
|
||||||
|
<string name="settings_user_id">ID Pengguna</string>
|
||||||
|
<string name="settings_oss">Info Sumber Terbuka</string>
|
||||||
|
|
||||||
|
<!-- MANAGE FAVORITES -->
|
||||||
|
|
||||||
|
<string name="settings_manage_favorites">Kelola favorit</string>
|
||||||
|
<string name="settings_backup_title">Cadangkan favorit</string>
|
||||||
|
<string name="settings_backup_failed">Gagal Mengunggah</string>
|
||||||
|
<string name="settings_backup_share">Bagikan Cadangan</string>
|
||||||
|
<string name="settings_backup_file_created">Berkas cadangan dibuat</string>
|
||||||
|
<string name="settings_restore_title">Kembalikan favorit</string>
|
||||||
|
<string name="settings_restore_failed">Gagal mengembalikan</string>
|
||||||
|
<string name="settings_restore_success">%1$d entri dikembalikan</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/APP LOCK ACTIVITY -->
|
||||||
|
|
||||||
|
<string name="settings_lock_none">Tidak ada</string>
|
||||||
|
<string name="settings_lock_pattern">Pola</string>
|
||||||
|
<string name="settings_lock_password">Kata Sandi</string>
|
||||||
|
<string name="settings_lock_biometrics">Biometrik</string>
|
||||||
|
<string name="settings_lock_fingerprint">Sidik Jari</string>
|
||||||
|
<string name="settings_lock_fingerprint_without_lock">Sidik Jari hanya bisa dipakai jika jenis kunci lain juga aktif</string>
|
||||||
|
|
||||||
|
<string name="settings_lock_enabled">Aktif</string>
|
||||||
|
<string name="settings_lock_confirm">Masukkan kunci yang sama sekali lagi untuk mengkonfirmasi</string>
|
||||||
|
<string name="settings_lock_remove_message">Hilangkan kunci?</string>
|
||||||
|
<string name="settings_lock_wrong_confirm">Kunci berbeda dari sebelumnya, silahkan coba lagi.</string>
|
||||||
|
|
||||||
|
<string name="settings_lock_fingerprint_prompt">Kunci Sidik Jari Pupil™</string>
|
||||||
|
<string name="settings_lock_fingerprint_prompt_subtitle">Kami perlu bukti bahwa anda memang ras terkuat dibumi.</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/DEFAULT QUERY DIALOG -->
|
||||||
|
|
||||||
|
<string name="default_query_dialog_title">Kelola kueri pencarian default</string>
|
||||||
|
<string name="default_query_dialog_language">Bahasa: </string>
|
||||||
|
<string name="default_query_dialog_filter_BL">Jangan tampilkan BL</string>
|
||||||
|
<string name="default_query_dialog_filter_guro">Jangan tampilkan Guro</string>
|
||||||
|
<string name="default_query_dialog_filter_loli">Saya bukan pedo</string>
|
||||||
|
<string name="default_query_dialog_language_selector_none">Apapun</string>
|
||||||
|
<string name="settings_mirror_title">Mirror</string>
|
||||||
|
|
||||||
|
<!-- PROXY DIALOG -->
|
||||||
|
<string name="proxy_dialog_type">Jenis</string>
|
||||||
|
<string name="proxy_dialog_addr_hint">Alamat</string>
|
||||||
|
<string name="proxy_dialog_username_hint">Nama Pengguna</string>
|
||||||
|
<string name="proxy_dialog_password_hint">Kata Sandi</string>
|
||||||
|
<string name="proxy_dialog_error">Nilai salah</string>
|
||||||
|
<string name="proxy_dialog_server">Server</string>
|
||||||
|
|
||||||
|
<!-- IMPORT OLD GALLERIES -->
|
||||||
|
<string name="import_old_galleries_folder_not_readable">Folder ini tidak bisa dibaca</string>
|
||||||
|
<string name="import_old_galleries_notification">Mengimpor galeri lama…</string>
|
||||||
|
<string name="import_old_galleries_notification_done">Impor selesai</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -1,165 +1,202 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="galleryblock_language">言語: %1$s</string>
|
|
||||||
<string name="galleryblock_series">シリーズ: %1$s</string>
|
|
||||||
<string name="galleryblock_type">タイプ: %1$s</string>
|
|
||||||
<string name="main_no_result">結果なし</string>
|
|
||||||
<string name="search_hint">ギャラリー検索</string>
|
|
||||||
<string name="settings_clear_cache">キャッシュクリア</string>
|
|
||||||
<string name="settings_clear_cache_alert_message">キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?</string>
|
|
||||||
<string name="settings_storage_usage">%s使用中</string>
|
|
||||||
<string name="settings_storage_usage_loading">ストレージ使用量読み込み中…</string>
|
|
||||||
<string name="settings_default_query">デフォルトキーワード</string>
|
|
||||||
<string name="settings_galleries_per_page">一回にロードするギャラリー数</string>
|
|
||||||
<string name="settings_search_title">検索設定</string>
|
|
||||||
<string name="settings_title">設定</string>
|
|
||||||
<string name="update_notification_description">アップデートダウンロード中</string>
|
|
||||||
<string name="update_title">新しいアップデートがあります</string>
|
|
||||||
<string name="warning">注意</string>
|
<string name="warning">注意</string>
|
||||||
<string name="settings_miscellaneous_title">その他</string>
|
<string name="error">エラー</string>
|
||||||
<string name="settings_mirror_title">ミラーサーバー</string>
|
<string name="ignore">無視</string>
|
||||||
<string name="settings_clear_history">履歴を削除</string>
|
<string name="unlimited">制限なし</string>
|
||||||
<string name="settings_clear_history_alert_message">履歴を削除しますか?</string>
|
|
||||||
<string name="settings_clear_history_summary">履歴数: %1$d</string>
|
<string name="copied_to_clipboard">クリップボードにコピーしました</string>
|
||||||
<string name="main_drawer_history">履歴</string>
|
|
||||||
<string name="notification_denied">通知を無効にするとバックグラウンドダウンロード及びアプリのアップデート機能が使用不可になります。</string>
|
|
||||||
<string name="main_drawer_home">トップ</string>
|
|
||||||
<string name="update_release_note"># リリースノート(v%1$s)\n%2$s</string>
|
|
||||||
<string name="settings_security_mode_title">セキュリティーモード</string>
|
|
||||||
<string name="settings_security_mode_summary">アプリ履歴でアプリの画面を表示しない</string>
|
|
||||||
<string name="reader_go_to_page">移動</string>
|
|
||||||
<string name="default_query_dialog_language_selector_none">非選択</string>
|
|
||||||
<string name="default_query_dialog_filter_BL">BLフィルター</string>
|
|
||||||
<string name="default_query_dialog_filter_guro">グロフィルター</string>
|
|
||||||
<string name="default_query_dialog_language">"言語: "</string>
|
|
||||||
<string name="default_query_dialog_title">デフォルトキーワード設定</string>
|
|
||||||
<string name="main_drawer_group_contact_title">お問い合わせ先</string>
|
|
||||||
<string name="main_drawer_group_contact_homepage">ホームページ</string>
|
|
||||||
<string name="main_drawer_group_contact_help">ヘルプ</string>
|
|
||||||
<string name="main_drawer_group_contact_github">Github</string>
|
|
||||||
<string name="main_drawer_group_contact_email">メールを送る</string>
|
|
||||||
<string name="reader_fab_fullscreen">フルスクリーン</string>
|
|
||||||
<string name="channel_download">ダウンロード</string>
|
<string name="channel_download">ダウンロード</string>
|
||||||
<string name="channel_download_description">ダウンロードの進行を通知</string>
|
<string name="channel_download_description">ダウンロードの進行を通知</string>
|
||||||
<string name="reader_fab_download">バックグラウンドダウンロード</string>
|
<string name="channel_downloader">ダウンローダ</string>
|
||||||
<string name="reader_notification_text">ダウンロード中…</string>
|
<string name="channel_downloader_description">ダウンローダの状態を表示</string>
|
||||||
<string name="reader_notification_complete">ダウンロード完了</string>
|
<string name="channel_update">アップデート</string>
|
||||||
<string name="reader_fab_download_cancel">バックグラウンドダウンロード中止</string>
|
<string name="channel_update_description">アップデートの進行状況を表示</string>
|
||||||
|
<string name="channel_transfer">転送</string>
|
||||||
|
<string name="channel_transfer_description">他の機器へのデータ転送の進行状況を表示</string>
|
||||||
|
|
||||||
|
<string name="unable_to_connect">hitomi.laに接続できません</string>
|
||||||
|
<string name="lock_corrupted">ロックファイルが破損されています。Pupilを再インストールしてください。</string>
|
||||||
|
<string name="main_no_result">結果なし</string>
|
||||||
|
<string name="unaccessible_download_folder">アンドロイド11以上では、現在のダウンロードフォルダに外部アプリからアクセスできません。ダウンロードフォルダを変更しますか?</string>
|
||||||
|
<string name="notification_denied">通知を無効にすると、バックグラウンドでのダウンロードとアプリのアップデート機能が使用不可になります。</string>
|
||||||
|
|
||||||
|
<string name="main_drawer_home">トップ</string>
|
||||||
|
<string name="main_drawer_history">履歴</string>
|
||||||
<string name="main_drawer_downloads">ダウンロード</string>
|
<string name="main_drawer_downloads">ダウンロード</string>
|
||||||
|
<string name="main_drawer_favorite">ブックマーク</string>
|
||||||
|
<string name="main_drawer_group_contact_title">お問い合わせ先</string>
|
||||||
|
<string name="main_drawer_group_contact_help">ヘルプ</string>
|
||||||
|
<string name="main_drawer_group_contact_homepage">ホームページ</string>
|
||||||
|
<string name="main_drawer_group_contact_github">Github</string>
|
||||||
|
<string name="main_drawer_group_contact_email">メールを送る</string>
|
||||||
|
<string name="main_drawer_grouop_contact_discord">ディスコード</string>
|
||||||
|
|
||||||
|
<string name="main_menu_thin">簡単モード</string>
|
||||||
|
|
||||||
|
<string name="main_menu_sort">並び替え</string>
|
||||||
|
<string name="main_menu_sort_date_added">新しい順</string>
|
||||||
|
<string name="main_menu_sort_date_published">新しい順(発売日)</string>
|
||||||
|
<string name="main_menu_sort_popular_today">人気順(日間)</string>
|
||||||
|
<string name="main_menu_sort_popular_week">人気順(週間)</string>
|
||||||
|
<string name="main_menu_sort_popular_month">人気順(月間)</string>
|
||||||
|
<string name="main_menu_sort_popular_year">人気順(年間)</string>
|
||||||
|
<string name="main_menu_sort_random">ランダム</string>
|
||||||
|
|
||||||
<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="channel_transfer">転送</string>
|
<string name="main_open_gallery_by_id">IDで作品を開く</string>
|
||||||
<string name="unable_to_connect">hitomi.laに接続できません</string>
|
|
||||||
<string name="main_move_to_page">%1$dページへ移動</string>
|
|
||||||
<string name="settings_clear_downloads">ダウンロード削除</string>
|
|
||||||
<string name="settings_clear_downloads_alert_message">ダウンロードしたギャラリーを全て削除します。\n実行しますか?</string>
|
|
||||||
<string name="settings_mirror_summary">ミラーサーバからイメージをロード</string>
|
|
||||||
<string name="main_drawer_favorite">ブックマーク</string>
|
|
||||||
<string name="main_open_gallery_by_id">ギャラリー番号で見る</string>
|
|
||||||
<string name="reader_failed_to_find_gallery">エラーが発生しました</string>
|
<string name="reader_failed_to_find_gallery">エラーが発生しました</string>
|
||||||
<string name="settings_storage">ストレージ</string>
|
<string name="main_fab_random">ランダムに作品を開く</string>
|
||||||
<string name="main_drawer_grouop_contact_discord">ディスコード</string>
|
<string name="main_fab_cancel">すべてのダウンロードをキャンセル</string>
|
||||||
<string name="settings_app_lock">アプリロック</string>
|
|
||||||
<string name="settings_app_lock_type">アップロックの種類</string>
|
<string name="main_move_to_page">%1$dページへ移動</string>
|
||||||
<string name="settings_app_version_title">バージョン(アップデート確認)</string>
|
|
||||||
<string name="settings_lock_biometrics">生体認識</string>
|
<string name="main_download">ダウンロード</string>
|
||||||
<string name="settings_lock_confirm">ロック確認のためもう一回入力してください。</string>
|
<string name="main_delete">削除</string>
|
||||||
<string name="settings_lock_enabled">有効</string>
|
|
||||||
<string name="settings_lock_fingerprint">指紋</string>
|
<string name="update_title">最新版あり</string>
|
||||||
<string name="settings_lock_password">パスワード</string>
|
<string name="update_download_completed">ダウンロードが完了しました</string>
|
||||||
<string name="settings_lock_pattern">パターン</string>
|
<string name="update_download_completed_description">ここをクリックして更新</string>
|
||||||
<string name="settings_lock_wrong_confirm">ロックが一致しません。やり直してください。</string>
|
<string name="update_notification_description">最新版をダウンロード中…</string>
|
||||||
<string name="settings_lock_none">なし</string>
|
<string name="update_release_note"># 更新履歴(v%1$s)\n%2$s</string>
|
||||||
<string name="settings_lock_remove_message">ロックを無効にしますか?</string>
|
|
||||||
<string name="reader_loading">ロード中</string>
|
<string name="search_hint">作品を検索</string>
|
||||||
<string name="main_menu_sort">ソート</string>
|
<string name="search_all">すべての作品を対象に検索</string>
|
||||||
<string name="main_menu_sort_newest">投稿日時順</string>
|
<string name="search_show_histories">履歴を見る</string>
|
||||||
<string name="main_menu_sort_popular">人気順</string>
|
<string name="search_show_tags">お気に入りのタグを見る</string>
|
||||||
<string name="ignore">無視</string>
|
|
||||||
<string name="lock_corrupted">ロックファイルが破損されています。Pupilを再再インストールしてください。</string>
|
<string name="gallery_details">作品情報</string>
|
||||||
<string name="settings_dark_mode_title">ダークモード</string>
|
<string name="gallery_thumbnails">サムネイル</string>
|
||||||
<string name="settings_dark_mode_summary">夜にシコりたい方々へ</string>
|
<string name="gallery_related">おすすめ</string>
|
||||||
<string name="gallery_details">ギャラリー情報</string>
|
<string name="gallery_artists">作者</string>
|
||||||
<string name="gallery_artists">アーティスト</string>
|
|
||||||
<string name="gallery_characters">キャラクター</string>
|
|
||||||
<string name="gallery_groups">グループ</string>
|
<string name="gallery_groups">グループ</string>
|
||||||
<string name="gallery_language">言語</string>
|
<string name="gallery_language">言語</string>
|
||||||
<string name="gallery_series">シリーズ</string>
|
<string name="gallery_series">シリーズ</string>
|
||||||
|
<string name="gallery_characters">キャラクター</string>
|
||||||
<string name="gallery_tags">タグ</string>
|
<string name="gallery_tags">タグ</string>
|
||||||
<string name="gallery_thumbnails">サムネイル</string>
|
|
||||||
<string name="gallery_related">おすすめ</string>
|
<string name="galleryblock_series">シリーズ: %1$s</string>
|
||||||
<string name="settings_nomedia_title">イメージを隠す</string>
|
<string name="galleryblock_type">タイプ: %1$s</string>
|
||||||
<string name="main_delete">削除</string>
|
<string name="galleryblock_language">言語: %1$s</string>
|
||||||
<string name="main_download">ダウンロード</string>
|
|
||||||
<string name="settings_backup_title">ブックマークバックアップ</string>
|
<!-- READER -->
|
||||||
<string name="settings_restore_title">ブックマーク復元</string>
|
<string name="reader_loading">読込中</string>
|
||||||
<string name="settings_backup_file_created">バックアップファイルを作成しました</string>
|
<string name="reader_go_to_page">移動</string>
|
||||||
<string name="settings_restore_failed">復元に失敗しました</string>
|
<string name="reader_fab_fullscreen">全画面</string>
|
||||||
<string name="settings_restore_success">%1$d項目を復元しました</string>
|
<string name="reader_fab_retry">再試行</string>
|
||||||
<string name="settings_download_folder">ダウンロード場所</string>
|
<string name="reader_fab_auto">まばたき検知スクロール</string>
|
||||||
<string name="settings_download_folder_internal">内部ストレージ</string>
|
<string name="reader_fab_auto_cancel">まばたき検知を中止</string>
|
||||||
<string name="settings_download_folder_removable">外部SDカード</string>
|
<string name="reader_fab_download">バックグラウンドでダウンロード</string>
|
||||||
<string name="settings_download_folder_available">%s 使用可能</string>
|
<string name="reader_fab_download_cancel">バックグラウンドダウンロード中止</string>
|
||||||
<string name="update_download_completed">ダウンロードが完了しました</string>
|
<string name="reader_notification_text">ダウンロード中…</string>
|
||||||
<string name="update_download_completed_description">ここをクリックしてアップデートを行えます</string>
|
<string name="reader_notification_complete">ダウンロード完了</string>
|
||||||
<string name="settings_beta">ベータチャンネルでアップデートを受信</string>
|
|
||||||
|
<string name="camera_denied">カメラ権限が拒否されているため、まばたき検知使用できません</string>
|
||||||
|
<string name="no_camera">この機器には前面カメラが装着されていません</string>
|
||||||
|
|
||||||
|
<string name="downloader_running">ダウンローダー起動中</string>
|
||||||
|
|
||||||
|
<string name="settings_title">設定</string>
|
||||||
|
|
||||||
|
<string name="settings_app_version_title">バージョン(クリックで更新確認)</string>
|
||||||
<string name="settings_app_version_description">v%s</string>
|
<string name="settings_app_version_description">v%s</string>
|
||||||
<string name="settings_low_quality">低解像度イメージ</string>
|
<string name="settings_beta">ベータ版チャンネルでアップデート</string>
|
||||||
<string name="settings_low_quality_summary">ロード速度とデータ使用料を改善するため低解像度イメージをロード</string>
|
|
||||||
|
<string name="settings_search_title">検索設定</string>
|
||||||
|
<string name="settings_galleries_per_page">一度に読み込む作品数</string>
|
||||||
|
<string name="settings_default_query">検索語句の初期値</string>
|
||||||
|
|
||||||
|
<string name="settings_storage">保存領域</string>
|
||||||
|
|
||||||
|
<string name="settings_manage_storage">保存領域の管理</string>
|
||||||
|
<string name="settings_storage_usage">%s使用中</string>
|
||||||
|
<string name="settings_storage_usage_loading">保存領域の使用量を算出中…</string>
|
||||||
|
<string name="settings_clear_cache">キャッシュを削除</string>
|
||||||
|
<string name="settings_clear_cache_alert_message">キャッシュを削除すると画像の読込に時間がかかります。実行しますか?</string>
|
||||||
|
<string name="settings_recover_downloads">ダウンロードデータベースを再構築</string>
|
||||||
|
<string name="settings_clear_downloads">ダウンロード済みを削除</string>
|
||||||
|
<string name="settings_clear_downloads_alert_message">ダウンロードした作品をすべて削除します。\n実行しますか?</string>
|
||||||
|
<string name="settings_clear_history">履歴を削除</string>
|
||||||
|
<string name="settings_clear_history_alert_message">履歴を削除しますか?</string>
|
||||||
|
<string name="settings_clear_history_summary">履歴数: %1$d</string>
|
||||||
|
|
||||||
|
<string name="settings_download_folder_name">フォルダ名パターン</string>
|
||||||
|
<string name="settings_invalid_download_folder_name">フォルダ名に使用できない文字が含まれています</string>
|
||||||
|
<string name="settings_download_folder_name_message">変数 %s は対応する値に置換されます\n\n%s</string>
|
||||||
|
<string name="settings_download_folder">ダウンロード場所</string>
|
||||||
|
<string name="settings_download_folder_removable">取り外し可能メディア</string>
|
||||||
|
<string name="settings_download_folder_internal">内部の保存領域</string>
|
||||||
|
<string name="settings_download_folder_available">%s 使用可能</string>
|
||||||
<string name="settings_download_folder_custom">手動で設定</string>
|
<string name="settings_download_folder_custom">手動で設定</string>
|
||||||
<string name="settings_download_folder_not_writable">このフォルダにアクセスできません。他のフォルダを選択してください。</string>
|
<string name="settings_download_folder_not_writable">このフォルダにアクセスできません。他のフォルダを選択してください。</string>
|
||||||
|
<string name="settings_cache_limit">キャッシュサイズ制限</string>
|
||||||
|
<string name="settings_nomedia_title">画像を隠す</string>
|
||||||
|
<string name="settings_low_quality">低解像度の画像</string>
|
||||||
|
<string name="settings_low_quality_summary">読込速度とデータ使用料を改善するため低解像度の画像を読み込む</string>
|
||||||
|
<string name="settings_transfer_data">他の機器にデータを転送</string>
|
||||||
|
|
||||||
|
<string name="settings_app_lock">アプリをロック</string>
|
||||||
|
<string name="settings_app_lock_type">アップをロックする方法</string>
|
||||||
|
|
||||||
|
<string name="settings_networking">ネットワーク</string>
|
||||||
|
<string name="settings_mirror_summary">ミラーサーバから画像を読み込む</string>
|
||||||
<string name="settings_proxy_title">プロクシ</string>
|
<string name="settings_proxy_title">プロクシ</string>
|
||||||
|
<string name="settings_max_concurrent_download">並列ダウンロード</string>
|
||||||
|
|
||||||
|
<string name="settings_miscellaneous_title">その他</string>
|
||||||
|
<string name="settings_tag_translation">タグの言語</string>
|
||||||
|
<string name="settings_tag_translation_message">Githubにて翻訳に参加できます</string>
|
||||||
|
<string name="settings_rtl">綴じ方向を左にする</string>
|
||||||
|
<string name="settings_security_mode_title">セキュリティーモード</string>
|
||||||
|
<string name="settings_security_mode_summary">アプリ履歴でアプリの画面を表示しない</string>
|
||||||
|
<string name="settings_dark_mode_title">ダークモード</string>
|
||||||
|
<string name="settings_dark_mode_summary">夜にシコりたい方々へ</string>
|
||||||
|
<string name="settings_import_old_galleries">旧ギャラリーインポート</string>
|
||||||
|
<string name="settings_user_id">ユーザーID</string>
|
||||||
|
<string name="settings_oss">オープンソースライセンス</string>
|
||||||
|
|
||||||
|
<string name="settings_manage_favorites">ブックマーク管理</string>
|
||||||
|
<string name="settings_backup_title">ブックマークをバックアップ</string>
|
||||||
|
<string name="settings_backup_failed">エラーが発生しました</string>
|
||||||
|
<string name="settings_backup_share">バックアップ共有</string>
|
||||||
|
<string name="settings_backup_file_created">バックアップファイルを作成しました</string>
|
||||||
|
<string name="settings_restore_title">ブックマーク復元</string>
|
||||||
|
<string name="settings_restore_failed">復元に失敗しました</string>
|
||||||
|
<string name="settings_restore_success">%1$d項目を復元しました</string>
|
||||||
|
|
||||||
|
<string name="settings_lock_confirm">ロック確認のためもう一回入力してください。</string>
|
||||||
|
<string name="settings_lock_enabled">有効</string>
|
||||||
|
<string name="settings_lock_none">なし</string>
|
||||||
|
<string name="settings_lock_pattern">パターン</string>
|
||||||
|
<string name="settings_lock_password">パスワード</string>
|
||||||
|
<string name="settings_lock_biometrics">生体認証</string>
|
||||||
|
<string name="settings_lock_fingerprint">指紋</string>
|
||||||
|
<string name="settings_lock_fingerprint_without_lock">予備のロックが設定されていないと指紋ロックは使用できません</string>
|
||||||
|
<string name="settings_lock_fingerprint_prompt">Pupil 指紋ロック™</string>
|
||||||
|
<string name="settings_lock_remove_message">ロックを無効にしますか?</string>
|
||||||
|
<string name="settings_lock_wrong_confirm">ロックが一致しません。やり直してください。</string>
|
||||||
|
|
||||||
|
<string name="default_query_dialog_title">検索語句の初期値を設定</string>
|
||||||
|
<string name="default_query_dialog_language">"言語: "</string>
|
||||||
|
<string name="default_query_dialog_filter_BL">BLフィルター</string>
|
||||||
|
<string name="default_query_dialog_filter_guro">グロフィルター</string>
|
||||||
|
<string name="default_query_dialog_filter_loli">登場人物を全て18歳以上にする</string>
|
||||||
|
<string name="default_query_dialog_language_selector_none">非選択</string>
|
||||||
|
<string name="settings_mirror_title">ミラーサーバー</string>
|
||||||
|
|
||||||
|
<string name="proxy_dialog_type">プロクシの種類</string>
|
||||||
|
<string name="proxy_dialog_addr_hint">サーバーアドレス</string>
|
||||||
|
<string name="proxy_dialog_port_hint">ポート番号</string>
|
||||||
<string name="proxy_dialog_username_hint">ID</string>
|
<string name="proxy_dialog_username_hint">ID</string>
|
||||||
<string name="proxy_dialog_type">プロクシタイプ</string>
|
|
||||||
<string name="proxy_dialog_port_hint">ポート</string>
|
|
||||||
<string name="proxy_dialog_password_hint">パスワード</string>
|
<string name="proxy_dialog_password_hint">パスワード</string>
|
||||||
<string name="proxy_dialog_error">エラー</string>
|
<string name="proxy_dialog_error">エラー</string>
|
||||||
<string name="proxy_dialog_addr_hint">サーバーアドレス</string>
|
|
||||||
<string name="proxy_dialog_server">サーバー</string>
|
<string name="proxy_dialog_server">サーバー</string>
|
||||||
<string name="main_menu_thin">簡単モード</string>
|
|
||||||
<string name="main_fab_cancel">すべてのダウンロードキャンセル</string>
|
|
||||||
<string name="channel_update">アップデート</string>
|
|
||||||
<string name="channel_update_description">アップデートの進行状態を表示</string>
|
|
||||||
<string name="settings_import_old_galleries">旧ギャラリーインポート</string>
|
|
||||||
<string name="import_old_galleries_folder_not_readable">フォルダを読めません</string>
|
<string name="import_old_galleries_folder_not_readable">フォルダを読めません</string>
|
||||||
<string name="import_old_galleries_notification">旧ギャラリーインポート中…</string>
|
<string name="import_old_galleries_notification">旧ギャラリーインポート中…</string>
|
||||||
<string name="import_old_galleries_notification_done">インポート完了</string>
|
<string name="import_old_galleries_notification_done">インポート完了</string>
|
||||||
<string name="main_fab_random">ランダムギャラリーを開く</string>
|
|
||||||
<string name="settings_lock_fingerprint_without_lock">予備のロックが設定されていないと指紋ロックは使用できません</string>
|
|
||||||
<string name="settings_lock_fingerprint_prompt">Pupil指紋ロック™</string>
|
|
||||||
<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="settings_user_id">ユーザーID</string>
|
</resources>
|
||||||
<string name="copied_to_clipboard">クリップボードにコピーしました</string>
|
|
||||||
<string name="reader_fab_retry">リトライ</string>
|
|
||||||
<string name="reader_fab_auto">まばたき検知スクロール</string>
|
|
||||||
<string name="search_all">全てのギャラリーを対象に検索</string>
|
|
||||||
<string name="settings_rtl">綴じ方向を左にする</string>
|
|
||||||
<string name="settings_manage_favorites">ブックマーク管理</string>
|
|
||||||
<string name="settings_backup_failed">エラーが発生しました</string>
|
|
||||||
<string name="settings_backup_share">バックアップ共有</string>
|
|
||||||
<string name="channel_downloader">ダウンローダ</string>
|
|
||||||
<string name="channel_downloader_description">ダウンローダの状態を表示</string>
|
|
||||||
<string name="downloader_running">ダウンローダー起動中</string>
|
|
||||||
<string name="settings_download_folder_name">フォルダ名パターン</string>
|
|
||||||
<string name="settings_invalid_download_folder_name">フォルダ名に使用できない文字が含まれています</string>
|
|
||||||
<string name="settings_download_folder_name_message">%sに含まれている文字列を対応する変数に置換します\n\n%s</string>
|
|
||||||
<string name="settings_manage_storage">ストレージ管理</string>
|
|
||||||
<string name="settings_oss">オープンソースライセンス</string>
|
|
||||||
<string name="search_show_tags">お気に入りのタグを見る</string>
|
|
||||||
<string name="search_show_histories">履歴を見る</string>
|
|
||||||
<string name="reader_fab_auto_cancel">まばたき検知を中止</string>
|
|
||||||
<string name="camera_denied">カメラ権限が拒否されているため、まばたき検知使用できません</string>
|
|
||||||
<string name="no_camera">この機器には前面カメラが装着されていません</string>
|
|
||||||
<string name="error">エラー</string>
|
|
||||||
<string name="settings_cache_limit">キャッシュサイズ制限</string>
|
|
||||||
<string name="unlimited">制限なし</string>
|
|
||||||
<string name="settings_tag_translation">タグ言語</string>
|
|
||||||
<string name="settings_tag_translation_message">Githubにて翻訳に参加できます</string>
|
|
||||||
<string name="settings_max_concurrent_download">並列ダウンロード</string>
|
|
||||||
<string name="unaccessible_download_folder">アンドロイド11以上では外部からのアプリ内部空間接近が不可能です。ダウンロードフォルダを変更しますか?</string>
|
|
||||||
<string name="settings_networking">ネットワーク</string>
|
|
||||||
<string name="settings_recover_downloads">ダウンロードデータベースを再構築</string>
|
|
||||||
<string name="settings_transfer_data">他の機器にデータを転送</string>
|
|
||||||
<string name="channel_transfer_description">他の機器へのデータ転送の進捗度を表示</string>
|
|
||||||
</resources>
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
<string name="reader_notification_complete">다운로드 완료</string>
|
<string name="reader_notification_complete">다운로드 완료</string>
|
||||||
<string name="reader_fab_download_cancel">백그라운드 다운로드 취소</string>
|
<string name="reader_fab_download_cancel">백그라운드 다운로드 취소</string>
|
||||||
<string name="main_drawer_downloads">다운로드</string>
|
<string name="main_drawer_downloads">다운로드</string>
|
||||||
|
<string name="main_menu_sort_random">무작위</string>
|
||||||
<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="channel_transfer">전송</string>
|
<string name="channel_transfer">전송</string>
|
||||||
@@ -71,8 +72,6 @@
|
|||||||
<string name="settings_lock_remove_message">잠금을 해제할까요?</string>
|
<string name="settings_lock_remove_message">잠금을 해제할까요?</string>
|
||||||
<string name="reader_loading">로딩중</string>
|
<string name="reader_loading">로딩중</string>
|
||||||
<string name="main_menu_sort">정렬</string>
|
<string name="main_menu_sort">정렬</string>
|
||||||
<string name="main_menu_sort_popular">인기순</string>
|
|
||||||
<string name="main_menu_sort_newest">시간순</string>
|
|
||||||
<string name="ignore">무시</string>
|
<string name="ignore">무시</string>
|
||||||
<string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string>
|
<string name="lock_corrupted">잠금 파일이 손상되었습니다! 앱을 재설치 해 주시기 바랍니다.</string>
|
||||||
<string name="settings_dark_mode_title">다크 모드</string>
|
<string name="settings_dark_mode_title">다크 모드</string>
|
||||||
@@ -162,4 +161,10 @@
|
|||||||
<string name="settings_recover_downloads">다운로드 데이터베이스 복구</string>
|
<string name="settings_recover_downloads">다운로드 데이터베이스 복구</string>
|
||||||
<string name="settings_transfer_data">다른 기기에 데이터 전송</string>
|
<string name="settings_transfer_data">다른 기기에 데이터 전송</string>
|
||||||
<string name="channel_transfer_description">다른 기기에 데이터 전송 시 상태 표시</string>
|
<string name="channel_transfer_description">다른 기기에 데이터 전송 시 상태 표시</string>
|
||||||
|
<string name="main_menu_sort_date_added">추가일</string>
|
||||||
|
<string name="main_menu_sort_date_published">발매일</string>
|
||||||
|
<string name="main_menu_sort_popular_today">인기순 (오늘)</string>
|
||||||
|
<string name="main_menu_sort_popular_week">인기순 (이번 주)</string>
|
||||||
|
<string name="main_menu_sort_popular_month">인기순 (이번 달)</string>
|
||||||
|
<string name="main_menu_sort_popular_year">인기순 (이번 해)</string>
|
||||||
</resources>
|
</resources>
|
||||||
20
app/src/main/res/values-v35/styles.xml
Normal file
20
app/src/main/res/values-v35/styles.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="NoActionBarAppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
26
app/src/main/res/values-zh-rTW/arrays.xml
Normal file
26
app/src/main/res/values-zh-rTW/arrays.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?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>
|
||||||
|
<string-array name="proxy_type">
|
||||||
|
<item>直接連線</item>
|
||||||
|
<item>HTTP</item>
|
||||||
|
<item>SOCKS</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
236
app/src/main/res/values-zh-rTW/strings.xml
Normal file
236
app/src/main/res/values-zh-rTW/strings.xml
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Translate needed down here -->
|
||||||
|
|
||||||
|
<string name="warning">警告</string>
|
||||||
|
<string name="error">錯誤</string>
|
||||||
|
|
||||||
|
<string name="ignore">忽略</string>
|
||||||
|
|
||||||
|
<string name="unlimited">無限制</string>
|
||||||
|
|
||||||
|
<string name="copied_to_clipboard">已複製到剪貼簿</string>
|
||||||
|
|
||||||
|
<string name="channel_download">下載</string>
|
||||||
|
<string name="channel_download_description">展示下載狀態</string>
|
||||||
|
|
||||||
|
<string name="channel_downloader">下載器</string>
|
||||||
|
<string name="channel_downloader_description">顯示下載器狀態</string>
|
||||||
|
|
||||||
|
<string name="channel_update">更新</string>
|
||||||
|
<string name="channel_update_description">顯示更新狀態</string>
|
||||||
|
|
||||||
|
<string name="channel_transfer">轉送</string>
|
||||||
|
<string name="channel_transfer_description">顯示轉送數據到其他裝置的進度</string>
|
||||||
|
|
||||||
|
<string name="unable_to_connect">沒法與hitomi.la建立連線</string>
|
||||||
|
|
||||||
|
<string name="lock_corrupted">檔案鎖發生錯誤!請重裝Pupil</string>
|
||||||
|
|
||||||
|
<string name="main_no_result">空無一物</string>
|
||||||
|
|
||||||
|
<string name="unaccessible_download_folder">從Android11及以後,默認的下載資料夾沒法被外界的應用訪問。是否要改變下載目的地?</string>
|
||||||
|
|
||||||
|
<string name="notification_denied">在背景下載需要通知權限。如果拒絕此權限則應用內更新和背景下載功能都會被停用。</string>
|
||||||
|
|
||||||
|
<string name="main_drawer_home">首頁</string>
|
||||||
|
<string name="main_drawer_history">歷史記錄</string>
|
||||||
|
<string name="main_drawer_downloads">下載</string>
|
||||||
|
<string name="main_drawer_favorite">收藏</string>
|
||||||
|
<string name="main_drawer_group_contact_title">聯絡</string>
|
||||||
|
<string name="main_drawer_group_contact_help">幫助</string>
|
||||||
|
<string name="main_drawer_group_contact_homepage">造訪主頁</string>
|
||||||
|
<string name="main_drawer_group_contact_github">造訪GitHub</string>
|
||||||
|
<string name="main_drawer_group_contact_email">給我傳電郵!</string>
|
||||||
|
<string name="main_drawer_grouop_contact_discord">Discord</string>
|
||||||
|
|
||||||
|
<string name="main_menu_thin">緊緻模式</string>
|
||||||
|
|
||||||
|
<string name="main_menu_sort">排序</string>
|
||||||
|
<string name="main_menu_sort_newest">最近新增</string>
|
||||||
|
<string name="main_menu_sort_popular">最有人氣</string>
|
||||||
|
|
||||||
|
<string name="main_jump_title">跳到頁面</string>
|
||||||
|
<string name="main_jump_message">現在頁面: %1$d\n最大頁面: %2$d</string>
|
||||||
|
<string name="main_open_gallery_by_id">以ID開啟相簿</string>
|
||||||
|
<string name="reader_failed_to_find_gallery">無法開啟相簿</string>
|
||||||
|
<string name="main_fab_random">隨機相簿</string>
|
||||||
|
<string name="main_fab_cancel">取消所有下載作業</string>
|
||||||
|
|
||||||
|
<string name="main_move_to_page">移動到第 %1$d 頁</string>
|
||||||
|
|
||||||
|
<string name="main_download">下載</string>
|
||||||
|
<string name="main_delete">刪除</string>
|
||||||
|
|
||||||
|
<string name="update_title">有可用更新</string>
|
||||||
|
<string name="update_download_completed">下載已完成</string>
|
||||||
|
<string name="update_download_completed_description">按此更新</string>
|
||||||
|
<string name="update_notification_description">正下載更新…</string>
|
||||||
|
<string name="update_release_note"># Release附註(v%1$s)\n%2$s</string>
|
||||||
|
|
||||||
|
<string name="search_hint">搜尋相簿</string>
|
||||||
|
<string name="search_all">搜尋所有相簿</string>
|
||||||
|
<string name="search_show_histories">顯示歷史</string>
|
||||||
|
<string name="search_show_tags">顯示收藏的標籤</string>
|
||||||
|
|
||||||
|
<string name="gallery_details">詳情</string>
|
||||||
|
<string name="gallery_thumbnails">縮圖</string>
|
||||||
|
<string name="gallery_related">有關聯的相簿</string>
|
||||||
|
<string name="gallery_artists">藝術家</string>
|
||||||
|
<string name="gallery_groups">團體</string>
|
||||||
|
<string name="gallery_language">語言</string>
|
||||||
|
<string name="gallery_series">系列</string>
|
||||||
|
<string name="gallery_characters">角色</string>
|
||||||
|
<string name="gallery_tags">標籤</string>
|
||||||
|
|
||||||
|
<string name="galleryblock_series">系列: %1$s</string>
|
||||||
|
<string name="galleryblock_type">類別: %1$s</string>
|
||||||
|
<string name="galleryblock_language">語言: %1$s</string>
|
||||||
|
<string name="galleryblock_pagecount" translatable="false">%dP</string>
|
||||||
|
|
||||||
|
<!-- READER -->
|
||||||
|
|
||||||
|
<string name="reader_loading">加載中</string>
|
||||||
|
<string name="reader_go_to_page">轉到頁面</string>
|
||||||
|
<string name="reader_fab_fullscreen">全螢幕</string>>
|
||||||
|
<string name="reader_fab_retry">重試</string>
|
||||||
|
<string name="reader_fab_auto">眨眼捲動</string>
|
||||||
|
<string name="reader_fab_auto_cancel">眨眼停止捲動</string>
|
||||||
|
<string name="reader_fab_download">背景下載</string>
|
||||||
|
<string name="reader_fab_download_cancel">取消背景下載</string>
|
||||||
|
<string name="reader_notification_text">正下載…</string>
|
||||||
|
<string name="reader_notification_complete">下載完成</string>
|
||||||
|
|
||||||
|
<string name="camera_denied">權限遭到拒絕,眨眼捲動無法運作。</string>
|
||||||
|
<string name="no_camera">這臺裝置沒有前置攝像頭。</string>
|
||||||
|
|
||||||
|
<!-- DOWNLOADER -->
|
||||||
|
<string name="downloader_running">下載器運行中…</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS -->
|
||||||
|
|
||||||
|
<string name="settings_title">設定</string>
|
||||||
|
|
||||||
|
<string name="settings_app_version_title">應用版本(點選以更新)</string>
|
||||||
|
<string name="settings_app_version_description">v%s</string>
|
||||||
|
<string name="settings_beta">從Beta頻道更新</string>
|
||||||
|
|
||||||
|
<!-- SEARCH -->
|
||||||
|
|
||||||
|
<string name="settings_search_title">搜尋設定</string>
|
||||||
|
<string name="settings_galleries_per_page">每頁展示相簿數量</string>
|
||||||
|
<string name="settings_default_query">預設查詢</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/STORAGE -->
|
||||||
|
|
||||||
|
<string name="settings_storage">存儲位置</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/STORAGE / MANAGE STORAGE -->
|
||||||
|
|
||||||
|
<string name="settings_manage_storage">管理存儲位置</string>
|
||||||
|
<string name="settings_storage_usage">現正使用 %s</string>
|
||||||
|
<string name="settings_storage_usage_loading">計算存儲用量…</string>
|
||||||
|
<string name="settings_clear_cache">清除快取</string>
|
||||||
|
<string name="settings_clear_cache_alert_message">快取清除後會影響影像載入速度,確定嗎?</string>
|
||||||
|
<string name="settings_recover_downloads">重建下載資料庫</string>
|
||||||
|
<string name="settings_clear_downloads">清除下載</string>
|
||||||
|
<string name="settings_clear_downloads_alert_message">這會刪掉所有下載相簿。\n你要繼續嗎?</string>
|
||||||
|
<string name="settings_clear_history">清除歷史記錄</string>
|
||||||
|
<string name="settings_clear_history_alert_message">你想清除歷史記錄嗎?</string>
|
||||||
|
<string name="settings_clear_history_summary">有 %1$d 條歷史記錄</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/STORAGE / MISCELLANEOUS -->
|
||||||
|
|
||||||
|
<string name="settings_download_folder_name">資料夾命名模式</string>
|
||||||
|
<string name="settings_invalid_download_folder_name">有無效的字元!</string>
|
||||||
|
<string name="settings_download_folder_name_message">%s 會被替換成對應的字元\n\n%s</string>
|
||||||
|
<string name="settings_download_folder">下載資料夾</string>
|
||||||
|
<string name="settings_download_folder_removable">行動儲存</string>
|
||||||
|
<string name="settings_download_folder_internal">內置儲存</string>
|
||||||
|
<string name="settings_download_folder_available">%s 可用</string>
|
||||||
|
<string name="settings_download_folder_custom">自訂位置</string>
|
||||||
|
<string name="settings_download_folder_not_writable">此資料夾為只讀。請重新選擇另一個。</string>
|
||||||
|
<string name="settings_cache_limit">快取上限</string>
|
||||||
|
<string name="settings_nomedia_title">在相簿中隱藏影像</string>
|
||||||
|
<string name="settings_low_quality">低質影像</string>
|
||||||
|
<string name="settings_low_quality_summary">載入低質影像以改善載入速度和流量用量。</string>
|
||||||
|
<string name="settings_transfer_data">轉移數據到其他裝置</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/APP LOCK -->
|
||||||
|
|
||||||
|
<string name="settings_app_lock">應用鎖</string>
|
||||||
|
<string name="settings_app_lock_type">鎖定方式</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/NETWORKING -->
|
||||||
|
<string name="settings_networking">網路</string>
|
||||||
|
<string name="settings_mirror_summary">從映像站載入影像</string>
|
||||||
|
<string name="settings_proxy_title">代理</string>
|
||||||
|
<string name="settings_max_concurrent_download">並發下載數</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/MISCELLANEOUS -->
|
||||||
|
|
||||||
|
<string name="settings_miscellaneous_title">雜項</string>
|
||||||
|
<string name="settings_tag_translation">標籤語言</string>
|
||||||
|
<string name="settings_tag_translation_message">在GitHub上貢獻翻譯</string>
|
||||||
|
<string name="settings_rtl">從右至左翻頁</string>
|
||||||
|
<string name="settings_security_mode_title">啟用安全模式</string>
|
||||||
|
<string name="settings_security_mode_summary">啟用安全模式以在最近應用程式列表中隱藏螢幕內容</string>
|
||||||
|
<string name="settings_dark_mode_title">暗黑模式</string>
|
||||||
|
<string name="settings_dark_mode_summary">不要讓光亮瞎了你的眼!</string>
|
||||||
|
<string name="settings_import_old_galleries">匯入以前的相簿</string>
|
||||||
|
<string name="settings_user_id">用戶ID</string>
|
||||||
|
<string name="settings_oss">開放原始碼提示</string>
|
||||||
|
|
||||||
|
<!-- MANAGE FAVORITES -->
|
||||||
|
|
||||||
|
<string name="settings_manage_favorites">管理收藏夾</string>
|
||||||
|
<string name="settings_backup_title">備份收藏夾</string>
|
||||||
|
<string name="settings_backup_failed">上載失敗</string>
|
||||||
|
<string name="settings_backup_share">分享備份</string>
|
||||||
|
<string name="settings_backup_file_created">備份檔已創建</string>
|
||||||
|
<string name="settings_restore_title">還原收藏夾</string>
|
||||||
|
<string name="settings_restore_failed">還原失敗</string>
|
||||||
|
<string name="settings_restore_success">%1$d 個條目已恢復</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/APP LOCK ACTIVITY -->
|
||||||
|
|
||||||
|
<string name="settings_lock_none">無</string>
|
||||||
|
<string name="settings_lock_pattern">圖形</string>
|
||||||
|
<string name="settings_lock_pin" translatable="false">PIN</string>
|
||||||
|
<string name="settings_lock_password">密碼</string>
|
||||||
|
<string name="settings_lock_biometrics">生物辨識</string>
|
||||||
|
<string name="settings_lock_fingerprint">指紋辨識</string>
|
||||||
|
<string name="settings_lock_fingerprint_without_lock">只有在啟用其他鎖定方式後才能啟用指紋。</string>
|
||||||
|
<string name="settings_lock_fingerprint_prompt">Pupil Fingerprint Lock™</string>
|
||||||
|
<string name="settings_lock_enabled">已啟用</string>
|
||||||
|
<string name="settings_lock_confirm">重複一次以確認</string>
|
||||||
|
<string name="settings_lock_remove_message">你想要移除鎖定嗎?</string>
|
||||||
|
<string name="settings_lock_wrong_confirm">兩次不匹配,請重試。</string>
|
||||||
|
|
||||||
|
<!-- SETTINGS/DEFAULT QUERY DIALOG -->
|
||||||
|
|
||||||
|
<string name="default_query_dialog_title">設定預設查詢</string>
|
||||||
|
<string name="default_query_dialog_language">語言: </string>
|
||||||
|
<string name="default_query_dialog_filter_BL">過濾 BL</string>
|
||||||
|
<string name="default_query_dialog_filter_guro">過濾 Guro</string>
|
||||||
|
<string name="default_query_dialog_filter_loli">合法18+模式</string>
|
||||||
|
<string name="default_query_dialog_language_selector_none">任何的</string>
|
||||||
|
<string name="settings_mirror_title">映像站</string>
|
||||||
|
|
||||||
|
<!-- PROXY DIALOG -->
|
||||||
|
<string name="proxy_dialog_type">類型</string>
|
||||||
|
<string name="proxy_dialog_addr_hint">位址</string>
|
||||||
|
<string name="proxy_dialog_port_hint">埠</string>
|
||||||
|
<string name="proxy_dialog_username_hint">使用者名稱</string>
|
||||||
|
<string name="proxy_dialog_password_hint">密碼</string>
|
||||||
|
<string name="proxy_dialog_error">錯誤的值</string>
|
||||||
|
<string name="proxy_dialog_server">伺服器</string>
|
||||||
|
|
||||||
|
<!-- IMPORT OLD GALLERIES -->
|
||||||
|
<string name="import_old_galleries_folder_not_readable">無法讀取該資料夾</string>
|
||||||
|
<string name="import_old_galleries_notification">匯入舊的相簿…</string>
|
||||||
|
<string name="import_old_galleries_notification_text" translatable="false">%1$d/%2$d</string>
|
||||||
|
<string name="import_old_galleries_notification_done">匯入完畢</string>
|
||||||
|
<string name="settings_lock_fingerprint_prompt_subtitle">喔靠,重試一次吧</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -70,8 +70,13 @@
|
|||||||
<string name="main_menu_thin">Thin Mode</string>
|
<string name="main_menu_thin">Thin Mode</string>
|
||||||
|
|
||||||
<string name="main_menu_sort">Sort</string>
|
<string name="main_menu_sort">Sort</string>
|
||||||
<string name="main_menu_sort_newest">Newest</string>
|
<string name="main_menu_sort_date_added">Date Added</string>
|
||||||
<string name="main_menu_sort_popular">Popular</string>
|
<string name="main_menu_sort_date_published">Date Published</string>
|
||||||
|
<string name="main_menu_sort_popular_today">Popular: Today</string>
|
||||||
|
<string name="main_menu_sort_popular_week">Popular: Week</string>
|
||||||
|
<string name="main_menu_sort_popular_month">Popular: Month</string>
|
||||||
|
<string name="main_menu_sort_popular_year">Popular: Year</string>
|
||||||
|
<string name="main_menu_sort_random">Random</string>
|
||||||
|
|
||||||
<string name="main_jump_title">Jump to page</string>
|
<string name="main_jump_title">Jump to page</string>
|
||||||
<string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string>
|
<string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string>
|
||||||
|
|||||||
35
build.gradle
35
build.gradle
@@ -1,35 +0,0 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
|
||||||
|
|
||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
|
||||||
classpath "com.google.gms:google-services:4.3.15"
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
|
||||||
// in the individual module build.gradle files
|
|
||||||
classpath "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
|
|
||||||
classpath "com.google.firebase:perf-plugin:1.4.2"
|
|
||||||
classpath "com.google.android.gms:oss-licenses-plugin:0.10.6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
|
||||||
maven { url "https://jitpack.io" }
|
|
||||||
maven { url "https://guardian.github.io/maven/repo-releases/" }
|
|
||||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task clean(type: Delete) {
|
|
||||||
delete rootProject.buildDir
|
|
||||||
}
|
|
||||||
11
build.gradle.kts
Normal file
11
build.gradle.kts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application) apply false
|
||||||
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
|
alias(libs.plugins.kotlinx.serialization) apply false
|
||||||
|
alias(libs.plugins.gms.oss.licenses) apply false
|
||||||
|
alias(libs.plugins.gms.google.services) apply false
|
||||||
|
alias(libs.plugins.firebase.crashlytics) apply false
|
||||||
|
alias(libs.plugins.firebase.perf) apply false
|
||||||
|
}
|
||||||
@@ -20,4 +20,6 @@ kotlin.code.style=official
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
|
||||||
kotlin_version=1.9.0
|
kotlin_version=2.1.10
|
||||||
|
android.nonTransitiveRClass=false
|
||||||
|
android.nonFinalResIds=false
|
||||||
114
gradle/libs.versions.toml
Normal file
114
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
[versions]
|
||||||
|
agp = "8.8.1"
|
||||||
|
kotlin = "2.0.0"
|
||||||
|
activityKtx = "1.10.0"
|
||||||
|
appcompat = "1.7.0"
|
||||||
|
bigimageviewer = "1.8.1"
|
||||||
|
biometric = "1.1.0"
|
||||||
|
constraintlayout = "2.2.0"
|
||||||
|
core = "3.1.0"
|
||||||
|
coreKtx = "1.15.0"
|
||||||
|
documentfilex = "0.7.2"
|
||||||
|
espressoCore = "3.6.1"
|
||||||
|
fab = "1.6.4"
|
||||||
|
firebaseBom = "33.9.0"
|
||||||
|
firebaseCrashlyticsGradle = "3.0.3"
|
||||||
|
floatingsearchview = "1.1.7"
|
||||||
|
fragmentKtx = "1.8.5"
|
||||||
|
googleServices = "4.4.2"
|
||||||
|
gradle = "8.8.0"
|
||||||
|
gridlayout = "1.0.0"
|
||||||
|
imagepipelineOkhttp3 = "2.6.0"
|
||||||
|
jsoup = "1.18.3"
|
||||||
|
junit = "4.13.2"
|
||||||
|
junitVersion = "1.2.1"
|
||||||
|
kotlinAndroidExtensions = "2.1.10"
|
||||||
|
kotlinGradlePlugin = "2.1.10"
|
||||||
|
kotlinSerialization = "2.1.10"
|
||||||
|
kotlinxCoroutinesAndroid = "1.8.0"
|
||||||
|
kotlinxCoroutinesTest = "1.6.1"
|
||||||
|
kotlinxDatetime = "0.4.0"
|
||||||
|
kotlinxSerializationJson = "1.5.1"
|
||||||
|
ktorNetwork = "2.3.10"
|
||||||
|
library = "1.2.0"
|
||||||
|
libraryVersion = "3.2"
|
||||||
|
material = "1.12.0"
|
||||||
|
okhttp = "3.12.12"
|
||||||
|
ossLicensesPlugin = "0.10.6"
|
||||||
|
patternlockview = "1.0.0"
|
||||||
|
perfPlugin = "1.4.2"
|
||||||
|
playServicesMlkitFaceDetection = "17.1.0"
|
||||||
|
playServicesOssLicenses = "17.1.0"
|
||||||
|
preferenceKtx = "1.2.1"
|
||||||
|
recyclerview = "1.4.0"
|
||||||
|
rules = "1.6.1"
|
||||||
|
runner = "1.6.2"
|
||||||
|
skyfishjyLibrary = "1.0.1"
|
||||||
|
dotsindicator = "5.1.0"
|
||||||
|
workRuntimeKtx = "2.10.0"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
|
||||||
|
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||||
|
bigimageviewer = { module = "com.github.piasy:BigImageViewer", version.ref = "bigimageviewer" }
|
||||||
|
biometric = { module = "androidx.biometric:biometric", version.ref = "biometric" }
|
||||||
|
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" }
|
||||||
|
core = { module = "ru.noties.markwon:core", version.ref = "core" }
|
||||||
|
core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
|
||||||
|
dirchooser-library = { module = "net.rdrei.android.dirchooser:library", version.ref = "libraryVersion" }
|
||||||
|
documentfilex = { module = "xyz.quaver:documentfilex", version.ref = "documentfilex" }
|
||||||
|
espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" }
|
||||||
|
ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" }
|
||||||
|
fab = { module = "com.github.clans:fab", version.ref = "fab" }
|
||||||
|
firebase-analytics-ktx = { module = "com.google.firebase:firebase-analytics-ktx" }
|
||||||
|
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
|
||||||
|
firebase-crashlytics-gradle = { module = "com.google.firebase:firebase-crashlytics-gradle", version.ref = "firebaseCrashlyticsGradle" }
|
||||||
|
firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx" }
|
||||||
|
firebase-perf-ktx = { module = "com.google.firebase:firebase-perf-ktx" }
|
||||||
|
floatingsearchview = { module = "xyz.quaver:floatingsearchview", version.ref = "floatingsearchview" }
|
||||||
|
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" }
|
||||||
|
frescoimageloader = { module = "com.github.piasy:FrescoImageLoader", version.ref = "bigimageviewer" }
|
||||||
|
frescoimageviewfactory = { module = "com.github.piasy:FrescoImageViewFactory", version.ref = "bigimageviewer" }
|
||||||
|
google-services = { module = "com.google.gms:google-services", version.ref = "googleServices" }
|
||||||
|
gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
|
||||||
|
gridlayout = { module = "androidx.gridlayout:gridlayout", version.ref = "gridlayout" }
|
||||||
|
imagepipeline-okhttp3 = { module = "com.facebook.fresco:imagepipeline-okhttp3", version.ref = "imagepipelineOkhttp3" }
|
||||||
|
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
|
||||||
|
junit = { module = "junit:junit", version.ref = "junit" }
|
||||||
|
kotlin-android-extensions = { module = "org.jetbrains.kotlin:kotlin-android-extensions", version.ref = "kotlinAndroidExtensions" }
|
||||||
|
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinGradlePlugin" }
|
||||||
|
kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlinSerialization" }
|
||||||
|
kotlin-stdlib-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8" }
|
||||||
|
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
|
||||||
|
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" }
|
||||||
|
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
|
||||||
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||||
|
ktor-network = { module = "io.ktor:ktor-network", version.ref = "ktorNetwork" }
|
||||||
|
library = { module = "com.daimajia.swipelayout:library", version.ref = "library" }
|
||||||
|
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||||
|
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||||
|
oss-licenses-plugin = { module = "com.google.android.gms:oss-licenses-plugin", version.ref = "ossLicensesPlugin" }
|
||||||
|
patternlockview = { module = "com.github.aritraroy:PatternLockView", version = "master-SNAPSHOT" }
|
||||||
|
perf-plugin = { module = "com.google.firebase:perf-plugin", version.ref = "perfPlugin" }
|
||||||
|
play-services-mlkit-face-detection = { module = "com.google.android.gms:play-services-mlkit-face-detection", version.ref = "playServicesMlkitFaceDetection" }
|
||||||
|
play-services-oss-licenses = { module = "com.google.android.gms:play-services-oss-licenses", version.ref = "playServicesOssLicenses" }
|
||||||
|
preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceKtx" }
|
||||||
|
recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
|
||||||
|
ripplebackground-library = { module = "com.skyfishjy.ripplebackground:library", version.ref = "skyfishjyLibrary" }
|
||||||
|
rules = { module = "androidx.test:rules", version.ref = "rules" }
|
||||||
|
runner = { module = "androidx.test:runner", version.ref = "runner" }
|
||||||
|
dotsindicator = { module = "com.tbuonomo:dotsindicator", version.ref = "dotsindicator" }
|
||||||
|
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
|
||||||
|
recyclerview-fastscroller = { module = "com.quiph.ui:recyclerviewfastscroller", version = "1.0.0" }
|
||||||
|
pinlockview = { module = "com.github.aritraroy:pinlockview", version = "2.1.0" }
|
||||||
|
androidx-compose-runtime = { module = "androidx.compose.runtime:runtime", version = "1.7.8" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
|
gms-oss-licenses = { id = "com.google.android.gms.oss-licenses-plugin", version.ref = "ossLicensesPlugin" }
|
||||||
|
gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" }
|
||||||
|
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsGradle" }
|
||||||
|
firebase-perf = { id = "com.google.firebase.firebase-perf", version.ref = "perfPlugin" }
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
|||||||
#Tue Oct 13 22:37:11 KST 2020
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
|
||||||
|
|||||||
297
gradlew
vendored
Normal file → Executable file
297
gradlew
vendored
Normal file → Executable file
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -15,80 +15,116 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# This is normally unused
|
||||||
APP_BASE_NAME=`basename "$0"`
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -97,87 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC2039,SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
194
gradlew.bat
vendored
194
gradlew.bat
vendored
@@ -1,100 +1,94 @@
|
|||||||
@rem
|
@rem
|
||||||
@rem Copyright 2015 the original author or authors.
|
@rem Copyright 2015 the original author or authors.
|
||||||
@rem
|
@rem
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
@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 not use this file except in compliance with the License.
|
||||||
@rem You may obtain a copy of the License at
|
@rem You may obtain a copy of the License at
|
||||||
@rem
|
@rem
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
@rem
|
@rem
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
@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 distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
@if "%DEBUG%" == "" @echo off
|
@rem
|
||||||
@rem ##########################################################################
|
|
||||||
@rem
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem Gradle startup script for Windows
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem ##########################################################################
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
set DIRNAME=%~dp0
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
|
||||||
set APP_BASE_NAME=%~n0
|
set DIRNAME=%~dp0
|
||||||
set APP_HOME=%DIRNAME%
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
set APP_BASE_NAME=%~n0
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
|
||||||
|
@rem Find java.exe
|
||||||
echo.
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
echo.
|
set JAVA_EXE=java.exe
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
echo location of your Java installation.
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
goto fail
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
:findJavaFromJavaHome
|
echo. 1>&2
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
goto fail
|
||||||
|
|
||||||
echo.
|
:findJavaFromJavaHome
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
echo.
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
goto fail
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
:init
|
echo. 1>&2
|
||||||
@rem Get command-line arguments, handling Windows variants
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
goto fail
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
:execute
|
||||||
set CMD_LINE_ARGS=
|
@rem Setup the command line
|
||||||
set _SKIP=2
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
@rem Execute Gradle
|
||||||
set CMD_LINE_ARGS=%*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:execute
|
:end
|
||||||
@rem Setup the command line
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
:fail
|
||||||
@rem Execute Gradle
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
:end
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
@rem End local scope for the variables with windows NT shell
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:fail
|
:mainEnd
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
:omega
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
include ':app'
|
|
||||||
32
settings.gradle.kts
Normal file
32
settings.gradle.kts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google {
|
||||||
|
content {
|
||||||
|
includeGroupByRegex("com\\.android.*")
|
||||||
|
includeGroupByRegex("com\\.google.*")
|
||||||
|
includeGroupByRegex("androidx.*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
resolutionStrategy {
|
||||||
|
eachPlugin {
|
||||||
|
if (requested.id.id == "com.google.android.gms.oss-licenses-plugin") {
|
||||||
|
useModule("com.google.android.gms:oss-licenses-plugin:${requested.version}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
maven { url = uri("https://jitpack.io") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "Pupil"
|
||||||
|
include(":app")
|
||||||
|
|
||||||
Reference in New Issue
Block a user