Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0eb6d40d7e | ||
|
|
f7b9260a2b | ||
|
|
7df09aa74b | ||
|
|
b3ce36f81c | ||
|
|
27dded11c1 | ||
|
|
758210658e | ||
|
|
830d490822 | ||
|
|
236ce2c189 | ||
|
|
7f3f17d08e | ||
|
|
72937cdd42 | ||
|
|
9c878f5e44 | ||
|
|
db928a168f | ||
|
|
a6d5336608 |
@@ -19,7 +19,7 @@ android {
|
|||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 70
|
versionCode = 70
|
||||||
versionName = "5.3.18"
|
versionName = "5.3.22"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
@@ -69,22 +69,7 @@ dependencies {
|
|||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
implementation(libs.kotlinx.datetime)
|
implementation(libs.kotlinx.datetime)
|
||||||
|
|
||||||
implementation(platform(libs.compose.bom))
|
implementation(libs.androidx.compose.runtime)
|
||||||
androidTestImplementation(platform(libs.compose.bom))
|
|
||||||
implementation(libs.compose.material3)
|
|
||||||
implementation(libs.compose.ui)
|
|
||||||
implementation(libs.compose.ui.tooling.preview)
|
|
||||||
debugImplementation(libs.compose.ui.tooling)
|
|
||||||
androidTestImplementation(libs.compose.ui.test)
|
|
||||||
debugImplementation(libs.compose.ui.test.manifest)
|
|
||||||
implementation(libs.compose.icons)
|
|
||||||
implementation(libs.compose.adaptive)
|
|
||||||
implementation(libs.compose.activity)
|
|
||||||
implementation(libs.compose.lifecycle.viewmodel)
|
|
||||||
implementation(libs.compose.livedata)
|
|
||||||
implementation(libs.compose.navigation)
|
|
||||||
|
|
||||||
implementation(libs.accompanist.adaptive)
|
|
||||||
|
|
||||||
implementation(libs.core.ktx)
|
implementation(libs.core.ktx)
|
||||||
implementation(libs.appcompat)
|
implementation(libs.appcompat)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -12,7 +12,7 @@
|
|||||||
"filters": [],
|
"filters": [],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 70,
|
"versionCode": 70,
|
||||||
"versionName": "5.3.18",
|
"versionName": "5.3.22",
|
||||||
"outputFile": "app-release.apk"
|
"outputFile": "app-release.apk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -5,34 +5,22 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="23"/>
|
||||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="23"/>
|
||||||
android:maxSdkVersion="23" />
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
android:maxSdkVersion="23" />
|
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
<uses-permission
|
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation"
|
||||||
android:name="android.permission.NEARBY_WIFI_DEVICES"
|
|
||||||
android:usesPermissionFlags="neverForLocation"
|
|
||||||
tools:targetApi="s" />
|
tools:targetApi="s" />
|
||||||
<uses-permission
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="32"
|
||||||
android:name="android.permission.ACCESS_FINE_LOCATION"
|
|
||||||
android:maxSdkVersion="32"
|
|
||||||
tools:ignore="CoarseFineLocation" />
|
tools:ignore="CoarseFineLocation" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
android:name="android.hardware.camera"
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
android:required="false" />
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.camera.autofocus"
|
|
||||||
android:required="false" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Pupil"
|
android:name=".Pupil"
|
||||||
@@ -40,20 +28,20 @@
|
|||||||
android:fullBackupContent="true"
|
android:fullBackupContent="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="UnusedAttribute"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
tools:replace="android:theme">
|
tools:replace="android:theme"
|
||||||
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||||
android:value="face" />
|
android:value="face" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
|
|
||||||
@@ -63,18 +51,15 @@
|
|||||||
|
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<service
|
<service android:name=".services.DownloadService"
|
||||||
android:name=".services.DownloadService"
|
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
|
|
||||||
<service
|
<service android:name=".services.TransferClientService"
|
||||||
android:name=".services.TransferClientService"
|
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
|
|
||||||
<service
|
<service android:name=".services.TransferServerService"
|
||||||
android:name=".services.TransferServerService"
|
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="dataSync" />
|
android:foregroundServiceType="dataSync" />
|
||||||
|
|
||||||
@@ -90,8 +75,8 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".ui.ReaderActivity"
|
android:name=".ui.ReaderActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:exported="true"
|
android:parentActivityName=".ui.MainActivity"
|
||||||
android:parentActivityName=".ui.MainComposeActivity">
|
android:exported="true">
|
||||||
<intent-filter android:autoVerify="true">
|
<intent-filter android:autoVerify="true">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
@@ -100,8 +85,8 @@
|
|||||||
|
|
||||||
<data android:scheme="http" />
|
<data android:scheme="http" />
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
<data android:host="*.hasha.in" />
|
<data android:host="*.hasha.in"/>
|
||||||
<data android:pathPrefix="/reader" />
|
<data android:pathPrefix="/reader"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter android:autoVerify="true">
|
<intent-filter android:autoVerify="true">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -111,8 +96,8 @@
|
|||||||
|
|
||||||
<data android:scheme="http" />
|
<data android:scheme="http" />
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
<data android:host="hitomi.la" />
|
<data android:host="hitomi.la"/>
|
||||||
<data android:pathPrefix="/galleries" />
|
<data android:pathPrefix="/galleries"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter android:autoVerify="true">
|
<intent-filter android:autoVerify="true">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -192,13 +177,13 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SettingsActivity"
|
android:name=".ui.SettingsActivity"
|
||||||
android:label="@string/settings_title" />
|
android:label="@string/settings_title">
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainComposeActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:exported="true"
|
android:theme="@style/NoActionBarAppTheme"
|
||||||
android:theme="@android:style/Theme.Material.Light.NoActionBar"
|
android:exported="true">
|
||||||
android:windowSoftInputMode="adjustResize">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
@@ -206,6 +191,7 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name="net.rdrei.android.dirchooser.DirectoryChooserActivity" />
|
||||||
<activity android:name=".ui.TransferActivity" />
|
<activity android:name=".ui.TransferActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
@@ -37,22 +38,22 @@ import com.google.android.gms.common.GooglePlayServicesRepairableException
|
|||||||
import com.google.android.gms.security.ProviderInstaller
|
import com.google.android.gms.security.ProviderInstaller
|
||||||
import com.google.firebase.FirebaseApp
|
import com.google.firebase.FirebaseApp
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import okhttp3.Dispatcher
|
import okhttp3.Dispatcher
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import xyz.quaver.io.FileX
|
import xyz.quaver.io.FileX
|
||||||
|
import xyz.quaver.pupil.hitomi.evaluationContext
|
||||||
|
import xyz.quaver.pupil.hitomi.readText
|
||||||
import xyz.quaver.pupil.types.Tag
|
import xyz.quaver.pupil.types.Tag
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.*
|
||||||
import xyz.quaver.pupil.util.SavedSet
|
|
||||||
import xyz.quaver.pupil.util.getProxyInfo
|
|
||||||
import xyz.quaver.pupil.util.preferences
|
|
||||||
import xyz.quaver.pupil.util.proxyInfo
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
import java.util.UUID
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.net.ssl.SSLContext
|
import javax.net.ssl.SSLContext
|
||||||
@@ -93,19 +94,16 @@ fun getSSLContext(context: Context): SSLContext {
|
|||||||
|
|
||||||
keyStore.setCertificateEntry("isrgrootx1", certificate)
|
keyStore.setCertificateEntry("isrgrootx1", certificate)
|
||||||
|
|
||||||
val defaultTrustManagerFactory =
|
val defaultTrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||||
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
|
||||||
defaultTrustManagerFactory.init(null as KeyStore?)
|
defaultTrustManagerFactory.init(null as KeyStore?)
|
||||||
|
|
||||||
defaultTrustManagerFactory.trustManagers.filterIsInstance<X509TrustManager>()
|
defaultTrustManagerFactory.trustManagers.filterIsInstance(X509TrustManager::class.java).forEach { trustManager ->
|
||||||
.forEach { trustManager ->
|
|
||||||
trustManager.acceptedIssuers.forEach { acceptedIssuer ->
|
trustManager.acceptedIssuers.forEach { acceptedIssuer ->
|
||||||
keyStore.setCertificateEntry(acceptedIssuer.subjectDN.name, acceptedIssuer)
|
keyStore.setCertificateEntry(acceptedIssuer.subjectDN.name, acceptedIssuer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val trustManagerFactory =
|
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||||
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
|
||||||
trustManagerFactory.init(keyStore)
|
trustManagerFactory.init(keyStore)
|
||||||
|
|
||||||
val sslContext = SSLContext.getInstance("TLS")
|
val sslContext = SSLContext.getInstance("TLS")
|
||||||
@@ -144,10 +142,7 @@ class Pupil : Application() {
|
|||||||
.proxyInfo(proxyInfo)
|
.proxyInfo(proxyInfo)
|
||||||
.addInterceptor { chain ->
|
.addInterceptor { chain ->
|
||||||
val request = chain.request().newBuilder()
|
val request = chain.request().newBuilder()
|
||||||
.addHeader(
|
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36")
|
||||||
"User-Agent",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36"
|
|
||||||
)
|
|
||||||
.header("Referer", "https://hitomi.la/")
|
.header("Referer", "https://hitomi.la/")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -183,8 +178,7 @@ class Pupil : Application() {
|
|||||||
|
|
||||||
histories = SavedSet(File(ContextCompat.getDataDir(this), "histories.json"), 0)
|
histories = SavedSet(File(ContextCompat.getDataDir(this), "histories.json"), 0)
|
||||||
favorites = SavedSet(File(ContextCompat.getDataDir(this), "favorites.json"), 0)
|
favorites = SavedSet(File(ContextCompat.getDataDir(this), "favorites.json"), 0)
|
||||||
favoriteTags =
|
favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse(""))
|
||||||
SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse(""))
|
|
||||||
searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "")
|
searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "")
|
||||||
|
|
||||||
favoriteTags.filter { it.tag.contains('_') }.forEach {
|
favoriteTags.filter { it.tag.contains('_') }.forEach {
|
||||||
@@ -215,60 +209,35 @@ class Pupil : Application() {
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
manager.createNotificationChannel(
|
manager.createNotificationChannel(NotificationChannel("download", getString(R.string.channel_download), NotificationManager.IMPORTANCE_LOW).apply {
|
||||||
NotificationChannel(
|
|
||||||
"download",
|
|
||||||
getString(R.string.channel_download),
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
).apply {
|
|
||||||
description = getString(R.string.channel_download_description)
|
description = getString(R.string.channel_download_description)
|
||||||
enableLights(false)
|
enableLights(false)
|
||||||
enableVibration(false)
|
enableVibration(false)
|
||||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
})
|
})
|
||||||
|
|
||||||
manager.createNotificationChannel(
|
manager.createNotificationChannel(NotificationChannel("downloader", getString(R.string.channel_downloader), NotificationManager.IMPORTANCE_LOW).apply {
|
||||||
NotificationChannel(
|
|
||||||
"downloader",
|
|
||||||
getString(R.string.channel_downloader),
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
).apply {
|
|
||||||
description = getString(R.string.channel_downloader_description)
|
description = getString(R.string.channel_downloader_description)
|
||||||
enableLights(false)
|
enableLights(false)
|
||||||
enableVibration(false)
|
enableVibration(false)
|
||||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
})
|
})
|
||||||
|
|
||||||
manager.createNotificationChannel(
|
manager.createNotificationChannel(NotificationChannel("update", getString(R.string.channel_update), NotificationManager.IMPORTANCE_HIGH).apply {
|
||||||
NotificationChannel(
|
|
||||||
"update",
|
|
||||||
getString(R.string.channel_update),
|
|
||||||
NotificationManager.IMPORTANCE_HIGH
|
|
||||||
).apply {
|
|
||||||
description = getString(R.string.channel_update_description)
|
description = getString(R.string.channel_update_description)
|
||||||
enableLights(true)
|
enableLights(true)
|
||||||
enableVibration(true)
|
enableVibration(true)
|
||||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
})
|
})
|
||||||
|
|
||||||
manager.createNotificationChannel(
|
manager.createNotificationChannel(NotificationChannel("import", getString(R.string.channel_update), NotificationManager.IMPORTANCE_LOW).apply {
|
||||||
NotificationChannel(
|
|
||||||
"import",
|
|
||||||
getString(R.string.channel_update),
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
).apply {
|
|
||||||
description = getString(R.string.channel_update_description)
|
description = getString(R.string.channel_update_description)
|
||||||
enableLights(false)
|
enableLights(false)
|
||||||
enableVibration(false)
|
enableVibration(false)
|
||||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
})
|
})
|
||||||
|
|
||||||
manager.createNotificationChannel(
|
manager.createNotificationChannel(NotificationChannel("transfer", getString(R.string.channel_transfer), NotificationManager.IMPORTANCE_LOW).apply {
|
||||||
NotificationChannel(
|
|
||||||
"transfer",
|
|
||||||
getString(R.string.channel_transfer),
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
).apply {
|
|
||||||
description = getString(R.string.channel_transfer_description)
|
description = getString(R.string.channel_transfer_description)
|
||||||
enableLights(false)
|
enableLights(false)
|
||||||
enableVibration(false)
|
enableVibration(false)
|
||||||
@@ -276,12 +245,10 @@ class Pupil : Application() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(
|
AppCompatDelegate.setDefaultNightMode(when (Preferences.get<Boolean>("dark_mode")) {
|
||||||
when (Preferences.get<Boolean>("dark_mode")) {
|
|
||||||
true -> AppCompatDelegate.MODE_NIGHT_YES
|
true -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
false -> AppCompatDelegate.MODE_NIGHT_NO
|
false -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
append("/")
|
||||||
|
}
|
||||||
|
append(fullPathFromHash(image.hash))
|
||||||
|
append(".")
|
||||||
|
append(ext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun urlFromUrlFromHash(galleryID: Int, image: GalleryFiles, dir: String? = null, ext: String? = null, base: String? = null) =
|
suspend fun urlFromUrlFromHash(
|
||||||
|
galleryID: Int,
|
||||||
|
image: GalleryFiles,
|
||||||
|
dir: String? = null,
|
||||||
|
ext: String? = null,
|
||||||
|
base: String? = null,
|
||||||
|
) =
|
||||||
if (base == "tn")
|
if (base == "tn")
|
||||||
urlFromUrl("https://a.hitomi.la/$dir/${realFullPathFromHash(image.hash)}.$ext", base)
|
urlFromUrl(
|
||||||
|
"https://a.gold-usergeneratedcontent.net/$dir/${realFullPathFromHash(image.hash)}.$ext",
|
||||||
|
base,
|
||||||
|
)
|
||||||
else
|
else
|
||||||
urlFromUrl(urlFromHash(galleryID, image, dir, ext), base)
|
urlFromUrl(urlFromHash(galleryID, image, dir, ext), base, dir)
|
||||||
|
|
||||||
suspend fun rewriteTnPaths(html: String) {
|
suspend fun imageUrlFromImage(galleryID: Int, image: GalleryFiles, noWebp: Boolean): String {
|
||||||
html.replace(Regex("""//tn\.hitomi\.la/[^/]+/[0-9a-f]/[0-9a-f]{2}/[0-9a-f]{64}""")) { url ->
|
return urlFromUrlFromHash(galleryID, image, "webp")
|
||||||
runBlocking {
|
|
||||||
urlFromUrl(url.value, "tn")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun imageUrlFromImage(galleryID: Int, image: GalleryFiles, noWebp: Boolean) : String {
|
|
||||||
return urlFromUrlFromHash(galleryID, image, "webp", null, "a")
|
|
||||||
// return when {
|
// return when {
|
||||||
// noWebp ->
|
// noWebp ->
|
||||||
// urlFromUrlFromHash(galleryID, image)
|
// urlFromUrlFromHash(galleryID, image)
|
||||||
|
|||||||
@@ -18,63 +18,25 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.ui
|
package xyz.quaver.pupil.ui
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.PersistableBundle
|
import android.os.PersistableBundle
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.enableEdgeToEdge
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.WindowCompat
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.util.LockManager
|
import xyz.quaver.pupil.util.LockManager
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.Preferences
|
||||||
|
import xyz.quaver.pupil.util.normalizeID
|
||||||
open class BaseComponentActivity : ComponentActivity() {
|
|
||||||
private var locked: Boolean = true
|
|
||||||
|
|
||||||
private val lockLauncher =
|
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
if (it.resultCode == RESULT_OK)
|
|
||||||
locked = false
|
|
||||||
else
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
enableEdgeToEdge()
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
||||||
|
|
||||||
locked = !LockManager(this).locks.isNullOrEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
|
|
||||||
if (Preferences["security_mode"])
|
|
||||||
window.setFlags(
|
|
||||||
WindowManager.LayoutParams.FLAG_SECURE,
|
|
||||||
WindowManager.LayoutParams.FLAG_SECURE
|
|
||||||
)
|
|
||||||
else
|
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
||||||
|
|
||||||
if (locked)
|
|
||||||
lockLauncher.launch(Intent(this, LockActivity::class.java))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class BaseActivity : AppCompatActivity() {
|
open class BaseActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private var locked: Boolean = true
|
private var locked: Boolean = true
|
||||||
|
|
||||||
private val lockLauncher =
|
private val lockLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
if (it.resultCode == Activity.RESULT_OK)
|
||||||
if (it.resultCode == RESULT_OK)
|
|
||||||
locked = false
|
locked = false
|
||||||
else
|
else
|
||||||
finish()
|
finish()
|
||||||
@@ -94,12 +56,12 @@ open class BaseActivity : AppCompatActivity() {
|
|||||||
if (Preferences["security_mode"])
|
if (Preferences["security_mode"])
|
||||||
window.setFlags(
|
window.setFlags(
|
||||||
WindowManager.LayoutParams.FLAG_SECURE,
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
WindowManager.LayoutParams.FLAG_SECURE
|
WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
)
|
|
||||||
else
|
else
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
|
||||||
if (locked)
|
if (locked)
|
||||||
lockLauncher.launch(Intent(this, LockActivity::class.java))
|
lockLauncher.launch(Intent(this, LockActivity::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package xyz.quaver.pupil.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.activity.compose.setContent
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.activity.viewModels
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import com.google.accompanist.adaptive.calculateDisplayFeatures
|
|
||||||
import xyz.quaver.pupil.ui.compose.MainApp
|
|
||||||
import xyz.quaver.pupil.ui.theme.AppTheme
|
|
||||||
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
|
|
||||||
import xyz.quaver.pupil.util.requestNotificationPermission
|
|
||||||
import xyz.quaver.pupil.util.showNotificationPermissionExplanationDialog
|
|
||||||
|
|
||||||
class MainComposeActivity : BaseComponentActivity() {
|
|
||||||
private val requestNotificationPermissionLauncher =
|
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
|
||||||
if (!isGranted) {
|
|
||||||
showNotificationPermissionExplanationDialog(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
requestNotificationPermission(this, requestNotificationPermissionLauncher, false)
|
|
||||||
|
|
||||||
val viewModel: MainViewModel by viewModels()
|
|
||||||
|
|
||||||
setContent {
|
|
||||||
val displayFeatures = calculateDisplayFeatures(this)
|
|
||||||
|
|
||||||
val uiState by viewModel.searchState.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
AppTheme {
|
|
||||||
MainApp(
|
|
||||||
uiState = uiState,
|
|
||||||
displayFeatures = displayFeatures
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package xyz.quaver.pupil.ui.compose
|
|
||||||
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
|
||||||
import androidx.window.core.layout.WindowSizeClass
|
|
||||||
import androidx.window.layout.DisplayFeature
|
|
||||||
import xyz.quaver.pupil.ui.viewmodel.SearchState
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MainApp(
|
|
||||||
uiState: SearchState,
|
|
||||||
displayFeatures: List<DisplayFeature>,
|
|
||||||
navController: NavController = rememberNavController(),
|
|
||||||
windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass,
|
|
||||||
) {
|
|
||||||
Text("Hello, World!")
|
|
||||||
}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
package xyz.quaver.pupil.ui.theme
|
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
|
|
||||||
val md_theme_light_primary = Color(0xFF006688)
|
|
||||||
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
|
|
||||||
val md_theme_light_primaryContainer = Color(0xFFC2E8FF)
|
|
||||||
val md_theme_light_onPrimaryContainer = Color(0xFF001E2B)
|
|
||||||
val md_theme_light_secondary = Color(0xFF4E616D)
|
|
||||||
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
|
|
||||||
val md_theme_light_secondaryContainer = Color(0xFFD1E5F3)
|
|
||||||
val md_theme_light_onSecondaryContainer = Color(0xFF091E28)
|
|
||||||
val md_theme_light_tertiary = Color(0xFF5F5A7D)
|
|
||||||
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
|
|
||||||
val md_theme_light_tertiaryContainer = Color(0xFFE5DEFF)
|
|
||||||
val md_theme_light_onTertiaryContainer = Color(0xFF1C1736)
|
|
||||||
val md_theme_light_error = Color(0xFFBA1A1A)
|
|
||||||
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
|
|
||||||
val md_theme_light_onError = Color(0xFFFFFFFF)
|
|
||||||
val md_theme_light_onErrorContainer = Color(0xFF410002)
|
|
||||||
val md_theme_light_background = Color(0xFFFBFCFE)
|
|
||||||
val md_theme_light_onBackground = Color(0xFF191C1E)
|
|
||||||
val md_theme_light_surface = Color(0xFFFBFCFE)
|
|
||||||
val md_theme_light_onSurface = Color(0xFF191C1E)
|
|
||||||
val md_theme_light_surfaceVariant = Color(0xFFDCE3E9)
|
|
||||||
val md_theme_light_onSurfaceVariant = Color(0xFF40484D)
|
|
||||||
val md_theme_light_outline = Color(0xFF71787D)
|
|
||||||
val md_theme_light_inverseOnSurface = Color(0xFFF0F1F3)
|
|
||||||
val md_theme_light_inverseSurface = Color(0xFF2E3133)
|
|
||||||
val md_theme_light_inversePrimary = Color(0xFF75D1FF)
|
|
||||||
val md_theme_light_shadow = Color(0xFF000000)
|
|
||||||
val md_theme_light_surfaceTint = Color(0xFF006688)
|
|
||||||
val md_theme_light_outlineVariant = Color(0xFFC0C7CD)
|
|
||||||
val md_theme_light_scrim = Color(0xFF000000)
|
|
||||||
|
|
||||||
val md_theme_dark_primary = Color(0xFF75D1FF)
|
|
||||||
val md_theme_dark_onPrimary = Color(0xFF003548)
|
|
||||||
val md_theme_dark_primaryContainer = Color(0xFF004D67)
|
|
||||||
val md_theme_dark_onPrimaryContainer = Color(0xFFC2E8FF)
|
|
||||||
val md_theme_dark_secondary = Color(0xFFB5C9D7)
|
|
||||||
val md_theme_dark_onSecondary = Color(0xFF20333D)
|
|
||||||
val md_theme_dark_secondaryContainer = Color(0xFF364954)
|
|
||||||
val md_theme_dark_onSecondaryContainer = Color(0xFFD1E5F3)
|
|
||||||
val md_theme_dark_tertiary = Color(0xFFC9C2EA)
|
|
||||||
val md_theme_dark_onTertiary = Color(0xFF312C4C)
|
|
||||||
val md_theme_dark_tertiaryContainer = Color(0xFF474364)
|
|
||||||
val md_theme_dark_onTertiaryContainer = Color(0xFFE5DEFF)
|
|
||||||
val md_theme_dark_error = Color(0xFFFFB4AB)
|
|
||||||
val md_theme_dark_errorContainer = Color(0xFF93000A)
|
|
||||||
val md_theme_dark_onError = Color(0xFF690005)
|
|
||||||
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
|
|
||||||
val md_theme_dark_background = Color(0xFF191C1E)
|
|
||||||
val md_theme_dark_onBackground = Color(0xFFE1E2E5)
|
|
||||||
val md_theme_dark_surface = Color(0xFF191C1E)
|
|
||||||
val md_theme_dark_onSurface = Color(0xFFE1E2E5)
|
|
||||||
val md_theme_dark_surfaceVariant = Color(0xFF40484D)
|
|
||||||
val md_theme_dark_onSurfaceVariant = Color(0xFFC0C7CD)
|
|
||||||
val md_theme_dark_outline = Color(0xFF8A9297)
|
|
||||||
val md_theme_dark_inverseOnSurface = Color(0xFF191C1E)
|
|
||||||
val md_theme_dark_inverseSurface = Color(0xFFE1E2E5)
|
|
||||||
val md_theme_dark_inversePrimary = Color(0xFF006688)
|
|
||||||
val md_theme_dark_shadow = Color(0xFF000000)
|
|
||||||
val md_theme_dark_surfaceTint = Color(0xFF75D1FF)
|
|
||||||
val md_theme_dark_outlineVariant = Color(0xFF40484D)
|
|
||||||
val md_theme_dark_scrim = Color(0xFF000000)
|
|
||||||
|
|
||||||
|
|
||||||
val seed = Color(0xFF4FC3F7)
|
|
||||||
|
|
||||||
val Gray50 = Color(0xFFF9FAFB)
|
|
||||||
val Gray100 = Color(0xFFF3F4F6)
|
|
||||||
val Gray200 = Color(0xFFE5E7EB)
|
|
||||||
val Gray300 = Color(0xFFD1D5DB)
|
|
||||||
val Gray400 = Color(0xFF9CA3AF)
|
|
||||||
val Gray500 = Color(0xFF6B7280)
|
|
||||||
val Gray600 = Color(0xFF4B5563)
|
|
||||||
val Gray700 = Color(0xFF374151)
|
|
||||||
val Gray800 = Color(0xFF1F2937)
|
|
||||||
val Gray900 = Color(0xFF111827)
|
|
||||||
val Red50 = Color(0xFFFEF2F2)
|
|
||||||
val Red100 = Color(0xFFFEE2E2)
|
|
||||||
val Red200 = Color(0xFFFECACA)
|
|
||||||
val Red300 = Color(0xFFFCA5A5)
|
|
||||||
val Red400 = Color(0xFFF87171)
|
|
||||||
val Red500 = Color(0xFFEF4444)
|
|
||||||
val Red600 = Color(0xFFDC2626)
|
|
||||||
val Red700 = Color(0xFFB91C1C)
|
|
||||||
val Red800 = Color(0xFF991B1B)
|
|
||||||
val Red900 = Color(0xFF7F1D1D)
|
|
||||||
val Yellow50 = Color(0xFFFFFBEB)
|
|
||||||
val Yellow100 = Color(0xFFFEF3C7)
|
|
||||||
val Yellow200 = Color(0xFFFDE68A)
|
|
||||||
val Yellow300 = Color(0xFFFCD34D)
|
|
||||||
val Yellow400 = Color(0xFFFBBF24)
|
|
||||||
val Yellow500 = Color(0xFFF59E0B)
|
|
||||||
val Yellow600 = Color(0xFFD97706)
|
|
||||||
val Yellow700 = Color(0xFFB45309)
|
|
||||||
val Yellow800 = Color(0xFF92400E)
|
|
||||||
val Yellow900 = Color(0xFF78350F)
|
|
||||||
val Green50 = Color(0xFFECFDF5)
|
|
||||||
val Green100 = Color(0xFFD1FAE5)
|
|
||||||
val Green200 = Color(0xFFA7F3D0)
|
|
||||||
val Green300 = Color(0xFF6EE7B7)
|
|
||||||
val Green400 = Color(0xFF34D399)
|
|
||||||
val Green500 = Color(0xFF10B981)
|
|
||||||
val Green600 = Color(0xFF059669)
|
|
||||||
val Green700 = Color(0xFF047857)
|
|
||||||
val Green800 = Color(0xFF065F46)
|
|
||||||
val Green900 = Color(0xFF064E3B)
|
|
||||||
val Blue50 = Color(0xFFEFF6FF)
|
|
||||||
val Blue100 = Color(0xFFDBEAFE)
|
|
||||||
val Blue200 = Color(0xFFBFDBFE)
|
|
||||||
val Blue300 = Color(0xFF93C5FD)
|
|
||||||
val Blue400 = Color(0xFF60A5FA)
|
|
||||||
val Blue500 = Color(0xFF3B82F6)
|
|
||||||
val Blue600 = Color(0xFF2563EB)
|
|
||||||
val Blue700 = Color(0xFF1D4ED8)
|
|
||||||
val Blue800 = Color(0xFF1E40AF)
|
|
||||||
val Blue900 = Color(0xFF1E3A8A)
|
|
||||||
val Indigo50 = Color(0xFFEEF2FF)
|
|
||||||
val Indigo100 = Color(0xFFE0E7FF)
|
|
||||||
val Indigo200 = Color(0xFFC7D2FE)
|
|
||||||
val Indigo300 = Color(0xFFA5B4FC)
|
|
||||||
val Indigo400 = Color(0xFF818CF8)
|
|
||||||
val Indigo500 = Color(0xFF6366F1)
|
|
||||||
val Indigo600 = Color(0xFF4F46E5)
|
|
||||||
val Indigo700 = Color(0xFF4338CA)
|
|
||||||
val Indigo800 = Color(0xFF3730A3)
|
|
||||||
val Indigo900 = Color(0xFF312E81)
|
|
||||||
val Purple50 = Color(0xFFF5F3FF)
|
|
||||||
val Purple100 = Color(0xFFEDE9FE)
|
|
||||||
val Purple200 = Color(0xFFDDD6FE)
|
|
||||||
val Purple300 = Color(0xFFC4B5FD)
|
|
||||||
val Purple400 = Color(0xFFA78BFA)
|
|
||||||
val Purple500 = Color(0xFF8B5CF6)
|
|
||||||
val Purple600 = Color(0xFF7C3AED)
|
|
||||||
val Purple700 = Color(0xFF6D28D9)
|
|
||||||
val Purple800 = Color(0xFF5B21B6)
|
|
||||||
val Purple900 = Color(0xFF4C1D95)
|
|
||||||
val Pink50 = Color(0xFFFDF2F8)
|
|
||||||
val Pink100 = Color(0xFFFCE7F3)
|
|
||||||
val Pink200 = Color(0xFFFBCFE8)
|
|
||||||
val Pink300 = Color(0xFFF9A8D4)
|
|
||||||
val Pink400 = Color(0xFFF472B6)
|
|
||||||
val Pink500 = Color(0xFFEC4899)
|
|
||||||
val Pink600 = Color(0xFFDB2777)
|
|
||||||
val Pink700 = Color(0xFFBE185D)
|
|
||||||
val Pink800 = Color(0xFF9D174D)
|
|
||||||
val Pink900 = Color(0xFF831843)
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package xyz.quaver.pupil.ui.theme
|
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.darkColorScheme
|
|
||||||
import androidx.compose.material3.dynamicDarkColorScheme
|
|
||||||
import androidx.compose.material3.dynamicLightColorScheme
|
|
||||||
import androidx.compose.material3.lightColorScheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
|
|
||||||
|
|
||||||
private val LightColors = lightColorScheme(
|
|
||||||
primary = md_theme_light_primary,
|
|
||||||
onPrimary = md_theme_light_onPrimary,
|
|
||||||
primaryContainer = md_theme_light_primaryContainer,
|
|
||||||
onPrimaryContainer = md_theme_light_onPrimaryContainer,
|
|
||||||
secondary = md_theme_light_secondary,
|
|
||||||
onSecondary = md_theme_light_onSecondary,
|
|
||||||
secondaryContainer = md_theme_light_secondaryContainer,
|
|
||||||
onSecondaryContainer = md_theme_light_onSecondaryContainer,
|
|
||||||
tertiary = md_theme_light_tertiary,
|
|
||||||
onTertiary = md_theme_light_onTertiary,
|
|
||||||
tertiaryContainer = md_theme_light_tertiaryContainer,
|
|
||||||
onTertiaryContainer = md_theme_light_onTertiaryContainer,
|
|
||||||
error = md_theme_light_error,
|
|
||||||
errorContainer = md_theme_light_errorContainer,
|
|
||||||
onError = md_theme_light_onError,
|
|
||||||
onErrorContainer = md_theme_light_onErrorContainer,
|
|
||||||
background = md_theme_light_background,
|
|
||||||
onBackground = md_theme_light_onBackground,
|
|
||||||
surface = md_theme_light_surface,
|
|
||||||
onSurface = md_theme_light_onSurface,
|
|
||||||
surfaceVariant = md_theme_light_surfaceVariant,
|
|
||||||
onSurfaceVariant = md_theme_light_onSurfaceVariant,
|
|
||||||
outline = md_theme_light_outline,
|
|
||||||
inverseOnSurface = md_theme_light_inverseOnSurface,
|
|
||||||
inverseSurface = md_theme_light_inverseSurface,
|
|
||||||
inversePrimary = md_theme_light_inversePrimary,
|
|
||||||
surfaceTint = md_theme_light_surfaceTint,
|
|
||||||
outlineVariant = md_theme_light_outlineVariant,
|
|
||||||
scrim = md_theme_light_scrim,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
private val DarkColors = darkColorScheme(
|
|
||||||
primary = md_theme_dark_primary,
|
|
||||||
onPrimary = md_theme_dark_onPrimary,
|
|
||||||
primaryContainer = md_theme_dark_primaryContainer,
|
|
||||||
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
|
|
||||||
secondary = md_theme_dark_secondary,
|
|
||||||
onSecondary = md_theme_dark_onSecondary,
|
|
||||||
secondaryContainer = md_theme_dark_secondaryContainer,
|
|
||||||
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
|
|
||||||
tertiary = md_theme_dark_tertiary,
|
|
||||||
onTertiary = md_theme_dark_onTertiary,
|
|
||||||
tertiaryContainer = md_theme_dark_tertiaryContainer,
|
|
||||||
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
|
|
||||||
error = md_theme_dark_error,
|
|
||||||
errorContainer = md_theme_dark_errorContainer,
|
|
||||||
onError = md_theme_dark_onError,
|
|
||||||
onErrorContainer = md_theme_dark_onErrorContainer,
|
|
||||||
background = md_theme_dark_background,
|
|
||||||
onBackground = md_theme_dark_onBackground,
|
|
||||||
surface = md_theme_dark_surface,
|
|
||||||
onSurface = md_theme_dark_onSurface,
|
|
||||||
surfaceVariant = md_theme_dark_surfaceVariant,
|
|
||||||
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
|
|
||||||
outline = md_theme_dark_outline,
|
|
||||||
inverseOnSurface = md_theme_dark_inverseOnSurface,
|
|
||||||
inverseSurface = md_theme_dark_inverseSurface,
|
|
||||||
inversePrimary = md_theme_dark_inversePrimary,
|
|
||||||
surfaceTint = md_theme_dark_surfaceTint,
|
|
||||||
outlineVariant = md_theme_dark_outlineVariant,
|
|
||||||
scrim = md_theme_dark_scrim,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AppTheme(
|
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
|
||||||
content: @Composable() () -> Unit,
|
|
||||||
) {
|
|
||||||
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
|
||||||
|
|
||||||
val colors = when {
|
|
||||||
dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
|
|
||||||
dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
|
|
||||||
darkTheme -> DarkColors
|
|
||||||
else -> LightColors
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialTheme(
|
|
||||||
colorScheme = colors,
|
|
||||||
content = content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package xyz.quaver.pupil.ui.viewmodel
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
|
|
||||||
class MainViewModel : ViewModel() {
|
|
||||||
private val _uiState = MutableStateFlow(SearchState())
|
|
||||||
val searchState: StateFlow<SearchState> = _uiState
|
|
||||||
}
|
|
||||||
|
|
||||||
data class SearchState(val stub: String = "")
|
|
||||||
@@ -24,26 +24,24 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.*
|
||||||
import kotlinx.serialization.json.contentOrNull
|
|
||||||
import kotlinx.serialization.json.jsonArray
|
|
||||||
import kotlinx.serialization.json.jsonObject
|
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.hitomi.GalleryBlock
|
import xyz.quaver.pupil.hitomi.GalleryBlock
|
||||||
import xyz.quaver.pupil.hitomi.GalleryInfo
|
import xyz.quaver.pupil.hitomi.GalleryInfo
|
||||||
import xyz.quaver.pupil.hitomi.imageUrlFromImage
|
import xyz.quaver.pupil.hitomi.imageUrlFromImage
|
||||||
import java.util.Locale
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
fun String.wordCapitalize(): String {
|
fun String.wordCapitalize() : String {
|
||||||
val result = ArrayList<String>()
|
val result = ArrayList<String>()
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
@@ -61,9 +59,8 @@ private val suffix = listOf(
|
|||||||
"TB" //really?
|
"TB" //really?
|
||||||
)
|
)
|
||||||
|
|
||||||
fun byteToString(byte: Long, precision: Int = 1): String {
|
fun byteToString(byte: Long, precision : Int = 1) : String {
|
||||||
var size = byte.toDouble()
|
var size = byte.toDouble(); var suffixIndex = 0
|
||||||
var suffixIndex = 0
|
|
||||||
|
|
||||||
while (size >= 1024) {
|
while (size >= 1024) {
|
||||||
size /= 1024
|
size /= 1024
|
||||||
@@ -95,7 +92,6 @@ val formatMap = mapOf<String, GalleryBlock.() -> (String)>(
|
|||||||
"-group-" to { if (groups.isNotEmpty()) groups.joinToString() else "N/A" }
|
"-group-" to { if (groups.isNotEmpty()) groups.joinToString() else "N/A" }
|
||||||
// TODO
|
// TODO
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats download folder name with given Metadata
|
* Formats download folder name with given Metadata
|
||||||
*/
|
*/
|
||||||
@@ -131,10 +127,10 @@ suspend fun GalleryInfo.getRequestBuilders(): List<Request.Builder> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun byteCount(codePoint: Int): Int = when (codePoint) {
|
fun byteCount(codePoint: Int): Int = when (codePoint) {
|
||||||
in 0..<0x80 -> 1
|
in 0 ..< 0x80 -> 1
|
||||||
in 0x80..<0x800 -> 2
|
in 0x80 ..< 0x800 -> 2
|
||||||
in 0x800..<0x10000 -> 3
|
in 0x800 ..< 0x10000 -> 3
|
||||||
in 0x10000..<0x110000 -> 4
|
in 0x10000 ..< 0x110000 -> 4
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,10 +168,7 @@ val JsonElement.content
|
|||||||
|
|
||||||
fun checkNotificationEnabled(context: Context) =
|
fun checkNotificationEnabled(context: Context) =
|
||||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
|
||||||
ContextCompat.checkSelfPermission(
|
ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
|
||||||
context,
|
|
||||||
Manifest.permission.POST_NOTIFICATIONS
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
|
|
||||||
fun showNotificationPermissionExplanationDialog(context: Context) {
|
fun showNotificationPermissionExplanationDialog(context: Context) {
|
||||||
AlertDialog.Builder(context)
|
AlertDialog.Builder(context)
|
||||||
@@ -189,16 +182,12 @@ fun requestNotificationPermission(
|
|||||||
activity: Activity,
|
activity: Activity,
|
||||||
requestPermissionLauncher: ActivityResultLauncher<String>,
|
requestPermissionLauncher: ActivityResultLauncher<String>,
|
||||||
showRationale: Boolean = true,
|
showRationale: Boolean = true,
|
||||||
ifGranted: () -> Unit = { },
|
ifGranted: () -> Unit,
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
checkNotificationEnabled(activity) -> ifGranted()
|
checkNotificationEnabled(activity) -> ifGranted()
|
||||||
showRationale && ActivityCompat.shouldShowRequestPermissionRationale(
|
showRationale && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.POST_NOTIFICATIONS) ->
|
||||||
activity,
|
|
||||||
Manifest.permission.POST_NOTIFICATIONS
|
|
||||||
) ->
|
|
||||||
showNotificationPermissionExplanationDialog(activity)
|
showNotificationPermissionExplanationDialog(activity)
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
}
|
}
|
||||||
|
|||||||
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>
|
||||||
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>
|
||||||
@@ -46,7 +46,6 @@ runner = "1.6.2"
|
|||||||
skyfishjyLibrary = "1.0.1"
|
skyfishjyLibrary = "1.0.1"
|
||||||
dotsindicator = "5.1.0"
|
dotsindicator = "5.1.0"
|
||||||
workRuntimeKtx = "2.10.0"
|
workRuntimeKtx = "2.10.0"
|
||||||
material3WindowSizeClassAndroid = "1.3.1"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
|
activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
|
||||||
@@ -102,22 +101,7 @@ dotsindicator = { module = "com.tbuonomo:dotsindicator", version.ref = "dotsindi
|
|||||||
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
|
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
|
||||||
recyclerview-fastscroller = { module = "com.quiph.ui:recyclerviewfastscroller", version = "1.0.0" }
|
recyclerview-fastscroller = { module = "com.quiph.ui:recyclerviewfastscroller", version = "1.0.0" }
|
||||||
pinlockview = { module = "com.github.aritraroy:pinlockview", version = "2.1.0" }
|
pinlockview = { module = "com.github.aritraroy:pinlockview", version = "2.1.0" }
|
||||||
|
androidx-compose-runtime = { module = "androidx.compose.runtime:runtime", version = "1.7.8" }
|
||||||
compose-bom = { module = "androidx.compose:compose-bom", version = "2025.02.00" }
|
|
||||||
compose-material3 = { module = "androidx.compose.material3:material3" }
|
|
||||||
compose-ui = { module = "androidx.compose.ui:ui" }
|
|
||||||
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
|
||||||
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
|
||||||
compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4" }
|
|
||||||
compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" }
|
|
||||||
compose-icons = { module = "androidx.compose.material:material-icons-extended" }
|
|
||||||
compose-adaptive = { module = "androidx.compose.material3.adaptive:adaptive" }
|
|
||||||
compose-activity = { module = "androidx.activity:activity-compose", version = "1.10.1" }
|
|
||||||
compose-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version = "2.8.7" }
|
|
||||||
compose-livedata = { module = "androidx.compose.runtime:runtime-livedata" }
|
|
||||||
compose-navigation = { module = "androidx.navigation:navigation-compose", version = "2.8.8" }
|
|
||||||
|
|
||||||
accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version = "0.37.2" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user