complete migration to compose
151
app/build.gradle
@@ -1,151 +0,0 @@
|
|||||||
apply plugin: "com.android.application"
|
|
||||||
apply plugin: "kotlin-android"
|
|
||||||
apply plugin: "kotlin-parcelize"
|
|
||||||
apply plugin: "kotlinx-serialization"
|
|
||||||
apply plugin: "com.google.android.gms.oss-licenses-plugin"
|
|
||||||
apply plugin: "com.google.devtools.ksp"
|
|
||||||
apply plugin: "com.google.dagger.hilt.android"
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace 'xyz.quaver.pupil'
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "xyz.quaver.pupil"
|
|
||||||
minSdkVersion 21
|
|
||||||
compileSdk 34
|
|
||||||
targetSdkVersion 34
|
|
||||||
versionCode 69
|
|
||||||
versionName "6.0.0"
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
debug {
|
|
||||||
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
|
|
||||||
compose true
|
|
||||||
buildConfig true
|
|
||||||
}
|
|
||||||
compileOptions {
|
|
||||||
coreLibraryDesugaringEnabled true
|
|
||||||
sourceCompatibility JavaVersion.VERSION_17
|
|
||||||
targetCompatibility JavaVersion.VERSION_17
|
|
||||||
}
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "17"
|
|
||||||
}
|
|
||||||
composeOptions {
|
|
||||||
kotlinCompilerExtensionVersion = "1.5.9"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
|
||||||
|
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.5.0"
|
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
|
||||||
implementation "androidx.activity:activity-ktx:1.8.2"
|
|
||||||
implementation "androidx.fragment:fragment-ktx:1.6.2"
|
|
||||||
implementation "androidx.preference:preference-ktx:1.2.1"
|
|
||||||
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
|
||||||
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.9.0"
|
|
||||||
|
|
||||||
implementation platform("androidx.compose:compose-bom:2024.03.00")
|
|
||||||
|
|
||||||
implementation "androidx.compose.material3:material3"
|
|
||||||
implementation "androidx.compose.material3:material3-window-size-class"
|
|
||||||
implementation 'androidx.compose.foundation:foundation'
|
|
||||||
implementation 'androidx.compose.ui:ui'
|
|
||||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
|
||||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
|
||||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.6.4'
|
|
||||||
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
|
||||||
implementation 'androidx.compose.material:material-icons-extended'
|
|
||||||
implementation 'androidx.activity:activity-compose:1.8.2'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0'
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.7.0'
|
|
||||||
implementation "com.google.accompanist:accompanist-adaptive:0.34.0"
|
|
||||||
implementation "androidx.navigation:navigation-compose:2.7.7"
|
|
||||||
|
|
||||||
ksp 'androidx.lifecycle:lifecycle-compiler:2.7.0'
|
|
||||||
|
|
||||||
def room_version = "2.6.1"
|
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
|
||||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
|
||||||
ksp "androidx.room:room-compiler:$room_version"
|
|
||||||
|
|
||||||
implementation "io.ktor:ktor-client-core:2.3.8"
|
|
||||||
implementation "io.ktor:ktor-client-okhttp:2.3.8"
|
|
||||||
|
|
||||||
implementation "io.coil-kt:coil-compose:2.6.0"
|
|
||||||
|
|
||||||
implementation "com.google.dagger:hilt-android:2.44"
|
|
||||||
ksp "com.google.dagger:hilt-compiler:2.44"
|
|
||||||
|
|
||||||
implementation "com.google.android.material:material:1.11.0"
|
|
||||||
|
|
||||||
implementation platform('com.google.firebase:firebase-bom:32.8.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.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:3.1.3'
|
|
||||||
|
|
||||||
//noinspection GradleDependency
|
|
||||||
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
|
||||||
|
|
||||||
implementation "com.andrognito.patternlockview:patternlockview:1.0.0"
|
|
||||||
|
|
||||||
implementation "ru.noties.markwon:core:3.1.0"
|
|
||||||
|
|
||||||
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.8.0"
|
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
|
||||||
androidTestImplementation "androidx.test:rules:1.5.0"
|
|
||||||
androidTestImplementation "androidx.test:runner:1.5.2"
|
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
|
|
||||||
}
|
|
||||||
165
app/build.gradle.kts
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import org.jetbrains.kotlin.ir.util.toIrConst
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.org.jetbrains.kotlin.android)
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.compose.compiler)
|
||||||
|
alias(libs.plugins.googleServices)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
alias(libs.plugins.hilt)
|
||||||
|
alias(libs.plugins.kotlinx.serialization)
|
||||||
|
alias(libs.plugins.crashlytics)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "xyz.quaver.pupil"
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "xyz.quaver.pupil"
|
||||||
|
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||||
|
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||||
|
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
||||||
|
versionCode = 69
|
||||||
|
versionName = "6.0.0"
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
getByName("debug") {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
isShrinkResources = false
|
||||||
|
isDebuggable = true
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
versionNameSuffix = "-DEBUG"
|
||||||
|
ext.set("enableCrashlytics", false)
|
||||||
|
ext.set("alwaysUpdateBuildId", false)
|
||||||
|
}
|
||||||
|
getByName("release") {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
coreLibraryDesugaring(libs.android.desugaring)
|
||||||
|
|
||||||
|
implementation(libs.kotlinx.serialization)
|
||||||
|
implementation(libs.kotlinx.coroutines)
|
||||||
|
implementation(libs.kotlinx.datetime)
|
||||||
|
|
||||||
|
implementation(libs.androidx.core)
|
||||||
|
implementation(libs.androidx.activity)
|
||||||
|
implementation(libs.androidx.activity.compose)
|
||||||
|
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||||
|
implementation(libs.androidx.navigation.compose)
|
||||||
|
|
||||||
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
implementation(libs.androidx.compose.material3)
|
||||||
|
implementation(libs.androidx.compose.material3.windowSizeClass)
|
||||||
|
implementation(libs.androidx.compose.foundation)
|
||||||
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||||
|
implementation(libs.androidx.compose.material.icons.extended)
|
||||||
|
|
||||||
|
implementation(libs.accompanist.adaptive)
|
||||||
|
|
||||||
|
implementation(libs.coil)
|
||||||
|
|
||||||
|
implementation(platform(libs.firebase.bom))
|
||||||
|
implementation(libs.firebase.analytics)
|
||||||
|
implementation(libs.firebase.crashlytics)
|
||||||
|
implementation(libs.firebase.perf)
|
||||||
|
|
||||||
|
implementation(libs.hilt.android)
|
||||||
|
ksp(libs.hilt.compiler)
|
||||||
|
|
||||||
|
implementation(libs.ktor.client)
|
||||||
|
implementation(libs.ktor.client.okhttp)
|
||||||
|
|
||||||
|
implementation(libs.documentFileX)
|
||||||
|
}
|
||||||
|
|
||||||
|
//dependencies {
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
// implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0"
|
||||||
|
// implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
||||||
|
// implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.5.0"
|
||||||
|
//
|
||||||
|
// implementation "androidx.appcompat:appcompat:1.7.0"
|
||||||
|
// implementation "androidx.activity:activity-ktx:1.9.0"
|
||||||
|
// implementation "androidx.fragment:fragment-ktx:1.8.1"
|
||||||
|
// implementation "androidx.preference:preference-ktx:1.2.1"
|
||||||
|
// implementation "androidx.recyclerview:recyclerview:1.3.2"
|
||||||
|
// 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.9.0"
|
||||||
|
//
|
||||||
|
// implementation platform("androidx.compose:compose-bom:2024.06.00")
|
||||||
|
//
|
||||||
|
// implementation "androidx.compose.material3:material3"
|
||||||
|
// implementation "androidx.compose.material3:material3-window-size-class"
|
||||||
|
// implementation 'androidx.compose.foundation:foundation'
|
||||||
|
// implementation 'androidx.compose.ui:ui'
|
||||||
|
// implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||||
|
// debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||||
|
// androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.6.8'
|
||||||
|
// debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
||||||
|
// implementation 'androidx.compose.material:material-icons-extended'
|
||||||
|
// implementation 'androidx.activity:activity-compose:1.9.0'
|
||||||
|
// implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.2'
|
||||||
|
// implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.8.2'
|
||||||
|
// implementation "com.google.accompanist:accompanist-adaptive:0.34.0"
|
||||||
|
// implementation "androidx.navigation:navigation-compose:2.7.7"
|
||||||
|
//
|
||||||
|
// ksp 'androidx.lifecycle:lifecycle-compiler:2.8.2'
|
||||||
|
//
|
||||||
|
// def room_version = "2.6.1"
|
||||||
|
//
|
||||||
|
// implementation "androidx.room:room-runtime:$room_version"
|
||||||
|
// annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||||
|
// ksp "androidx.room:room-compiler:$room_version"
|
||||||
|
//
|
||||||
|
// implementation "io.ktor:ktor-client-core:2.3.8"
|
||||||
|
// implementation "io.ktor:ktor-client-okhttp:2.3.8"
|
||||||
|
//
|
||||||
|
// implementation "io.coil-kt:coil-compose:2.6.0"
|
||||||
|
//
|
||||||
|
// implementation "com.google.dagger:hilt-android:2.51.1"
|
||||||
|
// ksp "com.google.dagger:hilt-compiler:2.51.1"
|
||||||
|
//
|
||||||
|
// implementation "com.google.android.material:material:1.12.0"
|
||||||
|
//
|
||||||
|
// implementation platform('com.google.firebase:firebase-bom:33.1.1')
|
||||||
|
// 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.1.0"
|
||||||
|
// implementation "com.google.android.gms:play-services-mlkit-face-detection:17.1.0"
|
||||||
|
//
|
||||||
|
// implementation "com.github.clans:fab:1.6.4"
|
||||||
|
//
|
||||||
|
// //noinspection GradleDependency
|
||||||
|
// implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
||||||
|
//
|
||||||
|
// implementation "ru.noties.markwon:core:3.1.0"
|
||||||
|
//
|
||||||
|
// 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.8.0"
|
||||||
|
// androidTestImplementation "androidx.test.ext:junit:1.2.1"
|
||||||
|
// androidTestImplementation "androidx.test:rules:1.6.1"
|
||||||
|
// androidTestImplementation "androidx.test:runner:1.6.1"
|
||||||
|
// androidTestImplementation "androidx.test.espresso:espresso-core:3.6.1"
|
||||||
|
//}
|
||||||
2
app/proguard-rules.pro
vendored
@@ -1,6 +1,6 @@
|
|||||||
# Add project specific ProGuard rules here.
|
# Add project specific ProGuard rules here.
|
||||||
# You can control the set of applied configuration files using the
|
# You can control the set of applied configuration files using the
|
||||||
# proguardFiles setting in build.gradle.
|
# proguardFiles setting in build.gradle.kts.
|
||||||
#
|
#
|
||||||
# For more details, see
|
# For more details, see
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|||||||
@@ -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" />
|
||||||
@@ -25,11 +24,8 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
tools:replace="android:theme"
|
tools:ignore="UnusedAttribute" >
|
||||||
tools:ignore="UnusedAttribute"
|
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules">
|
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||||
@@ -51,23 +47,10 @@
|
|||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name=".receiver.UpdateBroadcastReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<activity android:name=".ui.LockActivity" />
|
|
||||||
<activity
|
|
||||||
android:name=".ui.SettingsActivity"
|
|
||||||
android:label="@string/settings_title">
|
|
||||||
</activity>
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:theme="@style/NoActionBarAppTheme"
|
android:theme="@android:style/Theme.Material.Light.NoActionBar"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -26,118 +26,16 @@ 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.core.content.ContextCompat
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import coil.ImageLoader
|
|
||||||
import coil.ImageLoaderFactory
|
|
||||||
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
|
|
||||||
import com.github.piasy.biv.BigImageViewer
|
|
||||||
import com.github.piasy.biv.loader.fresco.FrescoImageLoader
|
|
||||||
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
|
|
||||||
import com.google.android.gms.common.GooglePlayServicesRepairableException
|
|
||||||
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 dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import okhttp3.Dispatcher
|
|
||||||
import okhttp3.Interceptor
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Response
|
|
||||||
import xyz.quaver.io.FileX
|
import xyz.quaver.io.FileX
|
||||||
import xyz.quaver.pupil.networking.SSLSettings
|
import java.util.UUID
|
||||||
import xyz.quaver.pupil.types.Tag
|
|
||||||
import xyz.quaver.pupil.util.*
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
typealias PupilInterceptor = (Interceptor.Chain) -> Response
|
|
||||||
|
|
||||||
val interceptors = mutableMapOf<KClass<out Any>, PupilInterceptor>()
|
|
||||||
|
|
||||||
lateinit var clientBuilder: OkHttpClient.Builder
|
|
||||||
|
|
||||||
var clientHolder: OkHttpClient? = null
|
|
||||||
val client: OkHttpClient
|
|
||||||
get() = clientHolder ?: clientBuilder.build().also {
|
|
||||||
clientHolder = it
|
|
||||||
}
|
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class Pupil : Application() {
|
class Pupil : Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
|
|
||||||
val userID = Preferences["user_id", ""].let { userID ->
|
|
||||||
userID.ifEmpty { UUID.randomUUID().toString().also { Preferences["user_id"] = it } }
|
|
||||||
}
|
|
||||||
|
|
||||||
FirebaseApp.initializeApp(this)
|
FirebaseApp.initializeApp(this)
|
||||||
FirebaseCrashlytics.getInstance().setUserId(userID)
|
|
||||||
|
|
||||||
val proxyInfo = getProxyInfo()
|
|
||||||
|
|
||||||
clientBuilder = OkHttpClient.Builder()
|
|
||||||
.readTimeout(0, TimeUnit.SECONDS)
|
|
||||||
.proxyInfo(proxyInfo)
|
|
||||||
.addInterceptor { chain ->
|
|
||||||
val request = chain.request().newBuilder()
|
|
||||||
.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")
|
|
||||||
.header("Referer", "https://hitomi.la/")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val tag = request.tag() ?: return@addInterceptor chain.proceed(request)
|
|
||||||
|
|
||||||
interceptors[tag::class]?.invoke(chain) ?: chain.proceed(request)
|
|
||||||
}.apply {
|
|
||||||
(Preferences.get<String>("max_concurrent_download").toIntOrNull() ?: 0).let {
|
|
||||||
if (it != 0)
|
|
||||||
dispatcher(Dispatcher(Executors.newFixedThreadPool(it)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Preferences.get<String>("download_folder").also {
|
|
||||||
if (it.startsWith("content://"))
|
|
||||||
contentResolver.takePersistableUriPermission(
|
|
||||||
Uri.parse(it),
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!FileX(this, it).canWrite())
|
|
||||||
throw Exception()
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Preferences.remove("download_folder")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Preferences["reset_secure", false]) {
|
|
||||||
Preferences["security_mode"] = false
|
|
||||||
Preferences["reset_secure"] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ProviderInstaller.installIfNeeded(this)
|
|
||||||
} catch (e: GooglePlayServicesRepairableException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} catch (e: GooglePlayServicesNotAvailableException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
BigImageViewer.initialize(
|
|
||||||
FrescoImageLoader.with(
|
|
||||||
this,
|
|
||||||
OkHttpImagePipelineConfigFactory
|
|
||||||
.newBuilder(this, client)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
||||||
@@ -171,11 +69,6 @@ class Pupil : Application() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(when (Preferences.get<Boolean>("dark_mode")) {
|
|
||||||
true -> AppCompatDelegate.MODE_NIGHT_YES
|
|
||||||
false -> AppCompatDelegate.MODE_NIGHT_NO
|
|
||||||
})
|
|
||||||
|
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package xyz.quaver.pupil.di
|
package xyz.quaver.pupil.di
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.google.android.datatransport.runtime.dagger.Provides
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import kotlinx.coroutines.sync.withLock
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.datetime.Clock.System.now
|
import kotlinx.datetime.Clock.System.now
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.receiver
|
|
||||||
|
|
||||||
import android.app.DownloadManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.webkit.MimeTypeMap
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import androidx.core.content.FileProvider
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class UpdateBroadcastReceiver : BroadcastReceiver() {
|
|
||||||
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
|
||||||
context ?: return
|
|
||||||
|
|
||||||
when (intent?.action) {
|
|
||||||
DownloadManager.ACTION_DOWNLOAD_COMPLETE -> {
|
|
||||||
|
|
||||||
// Validate download
|
|
||||||
val downloadID: Long = Preferences["update_download_id"]
|
|
||||||
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
|
||||||
|
|
||||||
if (intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -2) != downloadID)
|
|
||||||
return
|
|
||||||
|
|
||||||
// Get target uri
|
|
||||||
|
|
||||||
val query = DownloadManager.Query()
|
|
||||||
.setFilterById(downloadID)
|
|
||||||
|
|
||||||
val uri = downloadManager.query(query).use { cursor ->
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))?.let {
|
|
||||||
val uri = Uri.parse(it)
|
|
||||||
|
|
||||||
when (uri.scheme) {
|
|
||||||
"file" ->
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
|
||||||
FileProvider.getUriForFile(context, context.applicationContext.packageName + ".provider", File(uri.path!!))
|
|
||||||
else
|
|
||||||
uri
|
|
||||||
"content" -> uri
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
null
|
|
||||||
} ?: return
|
|
||||||
|
|
||||||
// Build Notification
|
|
||||||
|
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
|
||||||
|
|
||||||
val pendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), Intent(Intent.ACTION_VIEW).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
setDataAndType(uri, MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk"))
|
|
||||||
}, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_IMMUTABLE else 0)
|
|
||||||
|
|
||||||
val notification = NotificationCompat.Builder(context, "update")
|
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
||||||
.setContentTitle(context.getText(R.string.update_download_completed))
|
|
||||||
.setContentText(context.getText(R.string.update_download_completed_description))
|
|
||||||
.setContentIntent(pendingIntent)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
notificationManager.notify(R.id.notification_id_update, notification)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.PersistableBundle
|
|
||||||
import android.view.WindowManager
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.annotation.CallSuper
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.util.LockManager
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
import xyz.quaver.pupil.util.normalizeID
|
|
||||||
|
|
||||||
open class BaseActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
private var locked: Boolean = true
|
|
||||||
|
|
||||||
private val lockLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
if (it.resultCode == Activity.RESULT_OK)
|
|
||||||
locked = false
|
|
||||||
else
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
|
||||||
super.onCreate(savedInstanceState, persistentState)
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2019 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.animation.Animation
|
|
||||||
import android.view.animation.AnimationUtils
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.biometric.BiometricManager
|
|
||||||
import androidx.biometric.BiometricPrompt
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import com.andrognito.patternlockview.PatternLockView
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.databinding.LockActivityBinding
|
|
||||||
import xyz.quaver.pupil.ui.fragment.PINLockFragment
|
|
||||||
import xyz.quaver.pupil.ui.fragment.PatternLockFragment
|
|
||||||
import xyz.quaver.pupil.util.Lock
|
|
||||||
import xyz.quaver.pupil.util.LockManager
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
|
|
||||||
private var lastUnlocked = 0L
|
|
||||||
class LockActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
private lateinit var lockManager: LockManager
|
|
||||||
private var mode: String? = null
|
|
||||||
|
|
||||||
private lateinit var binding: LockActivityBinding
|
|
||||||
|
|
||||||
private val patternLockFragment = PatternLockFragment().apply {
|
|
||||||
var lastPass = ""
|
|
||||||
onPatternDrawn = {
|
|
||||||
when(mode) {
|
|
||||||
null -> {
|
|
||||||
val result = lockManager.check(it)
|
|
||||||
|
|
||||||
if (result == true) {
|
|
||||||
lastUnlocked = System.currentTimeMillis()
|
|
||||||
setResult(Activity.RESULT_OK)
|
|
||||||
finish()
|
|
||||||
} else
|
|
||||||
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
|
||||||
}
|
|
||||||
"add_lock" -> {
|
|
||||||
if (lastPass.isEmpty()) {
|
|
||||||
lastPass = it
|
|
||||||
|
|
||||||
Snackbar.make(view!!, R.string.settings_lock_confirm, Snackbar.LENGTH_LONG).show()
|
|
||||||
} else {
|
|
||||||
if (lastPass == it) {
|
|
||||||
LockManager(context!!).add(Lock.generate(Lock.Type.PATTERN, it))
|
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
|
||||||
lastPass = ""
|
|
||||||
|
|
||||||
Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val pinLockFragment = PINLockFragment().apply {
|
|
||||||
var lastPass = ""
|
|
||||||
onPINEntered = {
|
|
||||||
when(mode) {
|
|
||||||
null -> {
|
|
||||||
val result = lockManager.check(it)
|
|
||||||
|
|
||||||
if (result == true) {
|
|
||||||
lastUnlocked = System.currentTimeMillis()
|
|
||||||
setResult(Activity.RESULT_OK)
|
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
binding.indicatorDots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply {
|
|
||||||
setAnimationListener(object: Animation.AnimationListener {
|
|
||||||
override fun onAnimationEnd(animation: Animation?) {
|
|
||||||
binding.pinLockView.resetPinLockView()
|
|
||||||
binding.pinLockView.isEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAnimationStart(animation: Animation?) {
|
|
||||||
binding.pinLockView.isEnabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAnimationRepeat(animation: Animation?) {
|
|
||||||
// Do Nothing
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"add_lock" -> {
|
|
||||||
if (lastPass.isEmpty()) {
|
|
||||||
lastPass = it
|
|
||||||
|
|
||||||
binding.pinLockView.resetPinLockView()
|
|
||||||
Snackbar.make(view!!, R.string.settings_lock_confirm, Snackbar.LENGTH_LONG).show()
|
|
||||||
} else {
|
|
||||||
if (lastPass == it) {
|
|
||||||
LockManager(context!!).add(Lock.generate(Lock.Type.PIN, it))
|
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
binding.indicatorDots.startAnimation(AnimationUtils.loadAnimation(context, R.anim.shake).apply {
|
|
||||||
setAnimationListener(object: Animation.AnimationListener {
|
|
||||||
override fun onAnimationEnd(animation: Animation?) {
|
|
||||||
binding.pinLockView.resetPinLockView()
|
|
||||||
binding.pinLockView.isEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAnimationStart(animation: Animation?) {
|
|
||||||
binding.pinLockView.isEnabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAnimationRepeat(animation: Animation?) {
|
|
||||||
// Do Nothing
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
lastPass = ""
|
|
||||||
|
|
||||||
Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showBiometricPrompt() {
|
|
||||||
val promptInfo = BiometricPrompt.PromptInfo.Builder()
|
|
||||||
.setTitle(getText(R.string.settings_lock_fingerprint_prompt))
|
|
||||||
.setSubtitle(getText(R.string.settings_lock_fingerprint_prompt_subtitle))
|
|
||||||
.setNegativeButtonText(getText(android.R.string.cancel))
|
|
||||||
.setConfirmationRequired(false)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val biometricPrompt = BiometricPrompt(this, ContextCompat.getMainExecutor(this),
|
|
||||||
object : BiometricPrompt.AuthenticationCallback() {
|
|
||||||
override fun onAuthenticationSucceeded(
|
|
||||||
result: BiometricPrompt.AuthenticationResult) {
|
|
||||||
super.onAuthenticationSucceeded(result)
|
|
||||||
lastUnlocked = System.currentTimeMillis()
|
|
||||||
setResult(RESULT_OK)
|
|
||||||
finish()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Displays the "log in" prompt.
|
|
||||||
biometricPrompt.authenticate(promptInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
binding = LockActivityBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
lockManager = try {
|
|
||||||
LockManager(this)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
AlertDialog.Builder(this).apply {
|
|
||||||
setTitle(R.string.warning)
|
|
||||||
setMessage(R.string.lock_corrupted)
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}.show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mode = intent.getStringExtra("mode")
|
|
||||||
val force = intent.getBooleanExtra("force", false)
|
|
||||||
|
|
||||||
when(mode) {
|
|
||||||
null -> {
|
|
||||||
if (lockManager.isEmpty()) {
|
|
||||||
setResult(RESULT_OK)
|
|
||||||
finish()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (System.currentTimeMillis() - lastUnlocked < 5*60*1000 && !force) {
|
|
||||||
lastUnlocked = System.currentTimeMillis()
|
|
||||||
setResult(RESULT_OK)
|
|
||||||
finish()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Preferences["lock_fingerprint"]
|
|
||||||
&& BiometricManager.from(this).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS
|
|
||||||
) {
|
|
||||||
binding.fingerprintBtn.apply {
|
|
||||||
isEnabled = true
|
|
||||||
setOnClickListener {
|
|
||||||
showBiometricPrompt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showBiometricPrompt()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.patternBtn.apply {
|
|
||||||
isEnabled = lockManager.contains(Lock.Type.PATTERN)
|
|
||||||
setOnClickListener {
|
|
||||||
supportFragmentManager.beginTransaction().replace(
|
|
||||||
R.id.lock_content, patternLockFragment
|
|
||||||
).commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.pinBtn.apply {
|
|
||||||
isEnabled = lockManager.contains(Lock.Type.PIN)
|
|
||||||
setOnClickListener {
|
|
||||||
supportFragmentManager.beginTransaction().replace(
|
|
||||||
R.id.lock_content, pinLockFragment
|
|
||||||
).commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.passwordBtn.isEnabled = false
|
|
||||||
|
|
||||||
when (lockManager.locks!!.first().type) {
|
|
||||||
Lock.Type.PIN -> {
|
|
||||||
|
|
||||||
supportFragmentManager.beginTransaction().add(
|
|
||||||
R.id.lock_content, pinLockFragment
|
|
||||||
).commit()
|
|
||||||
}
|
|
||||||
Lock.Type.PATTERN -> {
|
|
||||||
supportFragmentManager.beginTransaction().add(
|
|
||||||
R.id.lock_content, patternLockFragment
|
|
||||||
).commit()
|
|
||||||
}
|
|
||||||
else -> return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"add_lock" -> {
|
|
||||||
binding.patternBtn.isEnabled = false
|
|
||||||
binding.pinBtn.isEnabled = false
|
|
||||||
binding.fingerprintBtn.isEnabled = false
|
|
||||||
binding.passwordBtn.isEnabled = false
|
|
||||||
|
|
||||||
when(intent.getStringExtra("type")!!) {
|
|
||||||
"pattern" -> {
|
|
||||||
binding.patternBtn.isEnabled = true
|
|
||||||
supportFragmentManager.beginTransaction().add(
|
|
||||||
R.id.lock_content, patternLockFragment
|
|
||||||
).commit()
|
|
||||||
}
|
|
||||||
"pin" -> {
|
|
||||||
binding.pinBtn.isEnabled = true
|
|
||||||
supportFragmentManager.beginTransaction().add(
|
|
||||||
R.id.lock_content, pinLockFragment
|
|
||||||
).commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
package xyz.quaver.pupil.ui
|
package xyz.quaver.pupil.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
@@ -33,7 +34,7 @@ import xyz.quaver.pupil.ui.composable.MainApp
|
|||||||
import xyz.quaver.pupil.ui.theme.AppTheme
|
import xyz.quaver.pupil.ui.theme.AppTheme
|
||||||
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
|
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
|
||||||
|
|
||||||
class MainActivity : BaseActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2019 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.MenuItem
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.ui.fragment.SettingsFragment
|
|
||||||
|
|
||||||
class SettingsActivity : BaseActivity() {
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(R.layout.settings_activity)
|
|
||||||
supportFragmentManager
|
|
||||||
.beginTransaction()
|
|
||||||
.replace(R.id.settings, SettingsFragment())
|
|
||||||
.commit()
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
when (item.itemId) {
|
|
||||||
android.R.id.home -> onBackPressed()
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,6 +21,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.foundation.text.InlineTextContent
|
import androidx.compose.foundation.text.InlineTextContent
|
||||||
import androidx.compose.foundation.text.appendInlineContent
|
import androidx.compose.foundation.text.appendInlineContent
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.BrokenImage
|
||||||
import androidx.compose.material.icons.filled.QuestionMark
|
import androidx.compose.material.icons.filled.QuestionMark
|
||||||
import androidx.compose.material.icons.filled.StarOutline
|
import androidx.compose.material.icons.filled.StarOutline
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
@@ -352,9 +353,9 @@ fun DetailedGalleryInfoHeader(galleryInfo: GalleryInfo, thumbnailUrl: String?) {
|
|||||||
.clip(RoundedCornerShape(8.dp)),
|
.clip(RoundedCornerShape(8.dp)),
|
||||||
loading = { CircularProgressIndicator(Modifier.align(Alignment.Center)) },
|
loading = { CircularProgressIndicator(Modifier.align(Alignment.Center)) },
|
||||||
error = {
|
error = {
|
||||||
Image(
|
Icon(
|
||||||
painter = painterResource(R.drawable.thumbnail),
|
Icons.Default.BrokenImage,
|
||||||
contentDescription = null
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
contentDescription = "Thumbnail"
|
contentDescription = "Thumbnail"
|
||||||
@@ -408,9 +409,9 @@ fun DetailedGalleryInfoHeader(galleryInfo: GalleryInfo, thumbnailUrl: String?) {
|
|||||||
.clip(RoundedCornerShape(8.dp)),
|
.clip(RoundedCornerShape(8.dp)),
|
||||||
loading = { CircularProgressIndicator(Modifier.align(Alignment.Center)) },
|
loading = { CircularProgressIndicator(Modifier.align(Alignment.Center)) },
|
||||||
error = {
|
error = {
|
||||||
Image(
|
Icon(
|
||||||
painter = painterResource(R.drawable.thumbnail),
|
Icons.Default.BrokenImage,
|
||||||
contentDescription = null
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
contentDescription = "Thumbnail"
|
contentDescription = "Thumbnail"
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import kotlinx.coroutines.launch
|
|||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.networking.GalleryInfo
|
import xyz.quaver.pupil.networking.GalleryInfo
|
||||||
import xyz.quaver.pupil.networking.SearchQuery
|
import xyz.quaver.pupil.networking.SearchQuery
|
||||||
import xyz.quaver.pupil.ui.SettingsActivity
|
|
||||||
import xyz.quaver.pupil.ui.viewmodel.SearchState
|
import xyz.quaver.pupil.ui.viewmodel.SearchState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -286,11 +285,11 @@ fun MainContent(
|
|||||||
composable(MainDestination.Favorites.route) {
|
composable(MainDestination.Favorites.route) {
|
||||||
NotImplemented()
|
NotImplemented()
|
||||||
}
|
}
|
||||||
activity(MainDestination.Settings.route) {
|
composable(MainDestination.Settings.route) {
|
||||||
activityClass = SettingsActivity::class
|
NotImplemented()
|
||||||
}
|
}
|
||||||
activity(MainDestination.ImageViewer.commonRoute) {
|
composable(MainDestination.ImageViewer.commonRoute) {
|
||||||
// argument("galleryID") { type = NavType.IntType }
|
NotImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,171 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.dialog
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.widget.ArrayAdapter
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.databinding.DefaultQueryDialogBinding
|
|
||||||
import xyz.quaver.pupil.types.Tags
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
|
|
||||||
class DefaultQueryDialog : DialogFragment() {
|
|
||||||
|
|
||||||
private val languages: Map<String, String> by lazy {
|
|
||||||
requireContext().resources.getStringArray(R.array.languages).map {
|
|
||||||
it.split("|").let { split ->
|
|
||||||
Pair(split[0], split[1])
|
|
||||||
}
|
|
||||||
}.toMap()
|
|
||||||
}
|
|
||||||
private val reverseLanguages: Map<String, String> by lazy {
|
|
||||||
languages.entries.associate { (k, v) -> v to k }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val excludeBL = "-male:yaoi"
|
|
||||||
private val excludeGuro = listOf("-female:guro", "-male:guro")
|
|
||||||
private val excludeLoli = listOf("-female:loli", "-male:shota")
|
|
||||||
|
|
||||||
var onPositiveButtonClickListener : ((Tags) -> (Unit))? = null
|
|
||||||
|
|
||||||
private var _binding: DefaultQueryDialogBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
private fun initView() {
|
|
||||||
val tags = Tags.parse(
|
|
||||||
Preferences["default_query"]
|
|
||||||
)
|
|
||||||
|
|
||||||
with(binding.languageSelector) {
|
|
||||||
adapter =
|
|
||||||
ArrayAdapter(
|
|
||||||
context,
|
|
||||||
android.R.layout.simple_spinner_dropdown_item,
|
|
||||||
arrayListOf(
|
|
||||||
context.getString(R.string.default_query_dialog_language_selector_none)
|
|
||||||
).apply {
|
|
||||||
addAll(languages.values)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (tags.any { it.area == "language" && !it.isNegative }) {
|
|
||||||
val tag = languages[tags.first { it.area == "language" }.tag]
|
|
||||||
if (tag != null) {
|
|
||||||
setSelection(
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
(adapter as ArrayAdapter<String>).getPosition(tag)
|
|
||||||
)
|
|
||||||
tags.removeByArea("language", false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with(binding.BLCheckbox) {
|
|
||||||
isChecked = tags.contains(excludeBL)
|
|
||||||
if (tags.contains(excludeBL))
|
|
||||||
tags.remove(excludeBL)
|
|
||||||
}
|
|
||||||
|
|
||||||
with(binding.guroCheckbox) {
|
|
||||||
isChecked = excludeGuro.all { tags.contains(it) }
|
|
||||||
if (excludeGuro.all { tags.contains(it) })
|
|
||||||
excludeGuro.forEach {
|
|
||||||
tags.remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with(binding.loliCheckbox) {
|
|
||||||
isChecked = excludeLoli.all { tags.contains(it) }
|
|
||||||
if (excludeLoli.all { tags.contains(it) })
|
|
||||||
excludeLoli.forEach {
|
|
||||||
tags.remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with(binding.edittext) {
|
|
||||||
setText(tags.toString(), android.widget.TextView.BufferType.EDITABLE)
|
|
||||||
addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun beforeTextChanged(
|
|
||||||
s: CharSequence?,
|
|
||||||
start: Int,
|
|
||||||
count: Int,
|
|
||||||
after: Int
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
|
||||||
|
|
||||||
override fun afterTextChanged(s: Editable?) {
|
|
||||||
s ?: return
|
|
||||||
|
|
||||||
if (s.any { it.isUpperCase() })
|
|
||||||
s.replace(
|
|
||||||
0,
|
|
||||||
s.length,
|
|
||||||
s.toString().toLowerCase(java.util.Locale.getDefault())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
_binding = DefaultQueryDialogBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
initView()
|
|
||||||
|
|
||||||
return AlertDialog.Builder(requireContext()).apply {
|
|
||||||
setTitle(R.string.default_query_dialog_title)
|
|
||||||
setView(binding.root)
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
val newTags = Tags.parse(binding.edittext.text.toString())
|
|
||||||
|
|
||||||
with(binding.languageSelector) {
|
|
||||||
if (selectedItemPosition != 0)
|
|
||||||
newTags.add("language:${reverseLanguages[selectedItem]}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (binding.BLCheckbox.isChecked)
|
|
||||||
newTags.add(excludeBL)
|
|
||||||
|
|
||||||
if (binding.guroCheckbox.isChecked)
|
|
||||||
excludeGuro.forEach { tag ->
|
|
||||||
newTags.add(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (binding.loliCheckbox.isChecked)
|
|
||||||
excludeLoli.forEach { tag ->
|
|
||||||
newTags.add(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
onPositiveButtonClickListener?.invoke(newTags)
|
|
||||||
}
|
|
||||||
}.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.dialog
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.widget.addTextChangedListener
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.databinding.DownloadFolderNameDialogBinding
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
import xyz.quaver.pupil.util.downloader.Cache
|
|
||||||
import xyz.quaver.pupil.util.formatDownloadFolder
|
|
||||||
import xyz.quaver.pupil.util.formatDownloadFolderTest
|
|
||||||
import xyz.quaver.pupil.util.formatMap
|
|
||||||
|
|
||||||
class DownloadFolderNameDialogFragment : DialogFragment() {
|
|
||||||
|
|
||||||
private var _binding: DownloadFolderNameDialogBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
_binding = DownloadFolderNameDialogBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
initView()
|
|
||||||
|
|
||||||
return Dialog(requireContext()).apply {
|
|
||||||
setContentView(binding.root)
|
|
||||||
window?.attributes?.width = ViewGroup.LayoutParams.MATCH_PARENT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initView() {
|
|
||||||
val galleryID = Cache.instances.let { if (it.size == 0) 1199708 else it.keys.elementAt((0 until it.size).random()) }
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val galleryBlock = Cache.getInstance(requireContext(), galleryID).getGalleryBlock()
|
|
||||||
|
|
||||||
binding.message.text = getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolder() ?: "")
|
|
||||||
binding.edittext.addTextChangedListener {
|
|
||||||
binding.message.text = requireContext().getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolderTest(it.toString()) ?: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.edittext.setText(Preferences["download_folder_name", "[-id-] -title-"])
|
|
||||||
binding.okButton.setOnClickListener {
|
|
||||||
val newValue = binding.edittext.text.toString()
|
|
||||||
|
|
||||||
if ((newValue as? String)?.contains("/") != false) {
|
|
||||||
Snackbar.make(binding.root, R.string.settings_invalid_download_folder_name, Snackbar.LENGTH_SHORT).show()
|
|
||||||
return@setOnClickListener
|
|
||||||
}
|
|
||||||
|
|
||||||
Preferences["download_folder_name"] = binding.edittext.text.toString()
|
|
||||||
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.dialog
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import xyz.quaver.io.FileX
|
|
||||||
import xyz.quaver.io.util.toFile
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.databinding.DownloadLocationDialogBinding
|
|
||||||
import xyz.quaver.pupil.databinding.DownloadLocationItemBinding
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
import xyz.quaver.pupil.util.byteToString
|
|
||||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class DownloadLocationDialogFragment : DialogFragment() {
|
|
||||||
|
|
||||||
private var _binding: DownloadLocationDialogBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
private val entries = mutableMapOf<File?, DownloadLocationItemBinding>()
|
|
||||||
|
|
||||||
private val requestDownloadFolderLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
if (it.resultCode == Activity.RESULT_OK) {
|
|
||||||
val context = context ?: return@registerForActivityResult
|
|
||||||
val dialog = dialog ?: return@registerForActivityResult
|
|
||||||
|
|
||||||
it.data?.data?.also { uri ->
|
|
||||||
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
|
|
||||||
context.contentResolver.takePersistableUriPermission(uri, takeFlags)
|
|
||||||
|
|
||||||
if (kotlin.runCatching { FileX(context, uri).canWrite() }.getOrDefault(false)) {
|
|
||||||
entries[null]?.locationAvailable?.text = uri.toFile(context)?.canonicalPath
|
|
||||||
Preferences["download_folder"] = uri.toString()
|
|
||||||
} else {
|
|
||||||
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[null]!!.locationAvailable.text = downloadFolder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val downloadFolder = DownloadManager.getInstance(context ?: return@registerForActivityResult).downloadFolder.canonicalPath
|
|
||||||
val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder }
|
|
||||||
if (key == null)
|
|
||||||
entries[null]!!.locationAvailable.text = downloadFolder
|
|
||||||
else {
|
|
||||||
entries[null]!!.button.isChecked = false
|
|
||||||
entries[key]!!.button.isChecked = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initView() {
|
|
||||||
val externalFilesDirs = ContextCompat.getExternalFilesDirs(requireContext(), null)
|
|
||||||
|
|
||||||
externalFilesDirs.forEachIndexed { index, dir ->
|
|
||||||
dir ?: return@forEachIndexed
|
|
||||||
|
|
||||||
DownloadLocationItemBinding.inflate(layoutInflater, binding.root, true).apply {
|
|
||||||
locationType.text = requireContext().getString(when (index) {
|
|
||||||
0 -> R.string.settings_download_folder_internal
|
|
||||||
else -> R.string.settings_download_folder_removable
|
|
||||||
})
|
|
||||||
locationAvailable.text = requireContext().getString(
|
|
||||||
R.string.settings_download_folder_available,
|
|
||||||
byteToString(dir.freeSpace)
|
|
||||||
)
|
|
||||||
root.setOnClickListener {
|
|
||||||
entries.values.forEach { entry ->
|
|
||||||
entry.button.isChecked = false
|
|
||||||
}
|
|
||||||
button.performClick()
|
|
||||||
Preferences["download_folder"] = dir.toUri().toString()
|
|
||||||
}
|
|
||||||
entries[dir] = this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadLocationItemBinding.inflate(layoutInflater, binding.root, true).apply {
|
|
||||||
locationType.text = requireContext().getString(R.string.settings_download_folder_custom)
|
|
||||||
root.setOnClickListener {
|
|
||||||
entries.values.forEach { entry ->
|
|
||||||
entry.button.isChecked = false
|
|
||||||
}
|
|
||||||
button.performClick()
|
|
||||||
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
|
||||||
putExtra("android.content.extra.SHOW_ADVANCED", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestDownloadFolderLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
entries[null] = this
|
|
||||||
}
|
|
||||||
|
|
||||||
val downloadFolder = DownloadManager.getInstance(requireContext()).downloadFolder.canonicalPath
|
|
||||||
val key = entries.keys.firstOrNull { it?.canonicalPath == downloadFolder }
|
|
||||||
entries[key]!!.button.isChecked = true
|
|
||||||
if (key == null) entries[key]!!.locationAvailable.text = downloadFolder
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
_binding = DownloadLocationDialogBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
initView()
|
|
||||||
|
|
||||||
return AlertDialog.Builder(requireContext()).apply {
|
|
||||||
setTitle(R.string.settings_download_folder)
|
|
||||||
setView(binding.root)
|
|
||||||
setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ ->
|
|
||||||
if (Preferences["download_folder", ""].isEmpty())
|
|
||||||
Preferences["download_folder"] = context.getExternalFilesDir(null)?.toUri()?.toString() ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
isCancelable = false
|
|
||||||
}.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.dialog
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.AdapterView
|
|
||||||
import android.widget.ArrayAdapter
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.client
|
|
||||||
import xyz.quaver.pupil.clientBuilder
|
|
||||||
import xyz.quaver.pupil.clientHolder
|
|
||||||
import xyz.quaver.pupil.databinding.ProxyDialogBinding
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
import xyz.quaver.pupil.util.ProxyInfo
|
|
||||||
import xyz.quaver.pupil.util.getProxyInfo
|
|
||||||
import xyz.quaver.pupil.util.proxyInfo
|
|
||||||
import java.net.Proxy
|
|
||||||
|
|
||||||
class ProxyDialogFragment : DialogFragment() {
|
|
||||||
|
|
||||||
private var _binding: ProxyDialogBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
_binding = ProxyDialogBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
initView()
|
|
||||||
|
|
||||||
return AlertDialog.Builder(requireContext()).apply {
|
|
||||||
setView(binding.root)
|
|
||||||
}.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initView() {
|
|
||||||
val proxyInfo = getProxyInfo()
|
|
||||||
|
|
||||||
val enabler = { enable: Boolean ->
|
|
||||||
binding.addr.isEnabled = enable
|
|
||||||
binding.port.isEnabled = enable
|
|
||||||
binding.username.isEnabled = enable
|
|
||||||
binding.password.isEnabled = enable
|
|
||||||
|
|
||||||
if (!enable) {
|
|
||||||
binding.addr.text = null
|
|
||||||
binding.port.text = null
|
|
||||||
binding.username.text = null
|
|
||||||
binding.password.text = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with(binding.typeSelector) {
|
|
||||||
adapter = ArrayAdapter(
|
|
||||||
context,
|
|
||||||
android.R.layout.simple_spinner_dropdown_item,
|
|
||||||
context.resources.getStringArray(R.array.proxy_type)
|
|
||||||
)
|
|
||||||
|
|
||||||
setSelection(proxyInfo.type.ordinal)
|
|
||||||
|
|
||||||
onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
|
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
|
||||||
enabler.invoke(position != 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.addr.setText(proxyInfo.host)
|
|
||||||
binding.port.setText(proxyInfo.port?.toString())
|
|
||||||
binding.username.setText(proxyInfo.username)
|
|
||||||
binding.password.setText(proxyInfo.password)
|
|
||||||
|
|
||||||
enabler.invoke(proxyInfo.type != Proxy.Type.DIRECT)
|
|
||||||
|
|
||||||
binding.cancelButton.setOnClickListener {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.okButton.setOnClickListener {
|
|
||||||
val type = Proxy.Type.values()[binding.typeSelector.selectedItemPosition]
|
|
||||||
val addr = binding.addr.text?.toString()
|
|
||||||
val port = binding.port.text?.toString()?.toIntOrNull()
|
|
||||||
val username = binding.username.text?.toString()
|
|
||||||
val password = binding.password.text?.toString()
|
|
||||||
|
|
||||||
if (type != Proxy.Type.DIRECT) {
|
|
||||||
if (addr == null || addr.isEmpty())
|
|
||||||
binding.addr.error = requireContext().getText(R.string.proxy_dialog_error)
|
|
||||||
if (port == null)
|
|
||||||
binding.port.error = requireContext().getText(R.string.proxy_dialog_error)
|
|
||||||
|
|
||||||
if (addr == null || addr.isEmpty() || port == null)
|
|
||||||
return@setOnClickListener
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyInfo(type, addr, port, username, password).let {
|
|
||||||
Preferences["proxy"] = Json.encodeToString(it)
|
|
||||||
|
|
||||||
clientBuilder
|
|
||||||
.proxyInfo(it)
|
|
||||||
clientHolder = null
|
|
||||||
client
|
|
||||||
}
|
|
||||||
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.fragment
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.ui.LockActivity
|
|
||||||
import xyz.quaver.pupil.util.Lock
|
|
||||||
import xyz.quaver.pupil.util.LockManager
|
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
|
|
||||||
class LockSettingsFragment : PreferenceFragmentCompat() {
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
|
|
||||||
val lockManager = LockManager(requireContext())
|
|
||||||
|
|
||||||
findPreference<Preference>("lock_pattern")?.summary =
|
|
||||||
if (lockManager.contains(Lock.Type.PATTERN))
|
|
||||||
getString(R.string.settings_lock_enabled)
|
|
||||||
else
|
|
||||||
""
|
|
||||||
|
|
||||||
findPreference<Preference>("lock_pin")?.summary =
|
|
||||||
if (lockManager.contains(Lock.Type.PIN))
|
|
||||||
getString(R.string.settings_lock_enabled)
|
|
||||||
else
|
|
||||||
""
|
|
||||||
|
|
||||||
if (lockManager.isEmpty()) {
|
|
||||||
(findPreference<Preference>("lock_fingerprint") as SwitchPreferenceCompat).isChecked = false
|
|
||||||
|
|
||||||
Preferences["lock_fingerprint"] = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.lock_preferences, rootKey)
|
|
||||||
|
|
||||||
with(findPreference<Preference>("lock_pattern")) {
|
|
||||||
this!!
|
|
||||||
|
|
||||||
if (LockManager(requireContext()).contains(Lock.Type.PATTERN))
|
|
||||||
summary = getString(R.string.settings_lock_enabled)
|
|
||||||
|
|
||||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
val lockManager = LockManager(requireContext())
|
|
||||||
|
|
||||||
if (lockManager.contains(Lock.Type.PATTERN)) {
|
|
||||||
AlertDialog.Builder(requireContext()).apply {
|
|
||||||
setTitle(R.string.warning)
|
|
||||||
setMessage(R.string.settings_lock_remove_message)
|
|
||||||
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
lockManager.remove(Lock.Type.PATTERN)
|
|
||||||
onResume()
|
|
||||||
}
|
|
||||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
|
||||||
}.show()
|
|
||||||
} else {
|
|
||||||
val intent = Intent(requireContext(), LockActivity::class.java).apply {
|
|
||||||
putExtra("mode", "add_lock")
|
|
||||||
putExtra("type", "pattern")
|
|
||||||
}
|
|
||||||
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with(findPreference<Preference>("lock_pin")) {
|
|
||||||
this!!
|
|
||||||
|
|
||||||
if (LockManager(requireContext()).contains(Lock.Type.PIN))
|
|
||||||
summary = getString(R.string.settings_lock_enabled)
|
|
||||||
|
|
||||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
val lockManager = LockManager(requireContext())
|
|
||||||
|
|
||||||
if (lockManager.contains(Lock.Type.PIN)) {
|
|
||||||
AlertDialog.Builder(requireContext()).apply {
|
|
||||||
setTitle(R.string.warning)
|
|
||||||
setMessage(R.string.settings_lock_remove_message)
|
|
||||||
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
lockManager.remove(Lock.Type.PIN)
|
|
||||||
onResume()
|
|
||||||
}
|
|
||||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
|
||||||
}.show()
|
|
||||||
} else {
|
|
||||||
val intent = Intent(requireContext(), LockActivity::class.java).apply {
|
|
||||||
putExtra("mode", "add_lock")
|
|
||||||
putExtra("type", "pin")
|
|
||||||
}
|
|
||||||
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
with(findPreference<Preference>("lock_fingerprint")) {
|
|
||||||
this!!
|
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
this as SwitchPreferenceCompat
|
|
||||||
|
|
||||||
if (newValue == true && LockManager(requireContext()).isEmpty()) {
|
|
||||||
isChecked = false
|
|
||||||
|
|
||||||
Toast.makeText(requireContext(), R.string.settings_lock_fingerprint_without_lock, Toast.LENGTH_SHORT).show()
|
|
||||||
} else
|
|
||||||
isChecked = newValue as Boolean
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.fragment
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.graphics.PorterDuff
|
|
||||||
import android.graphics.PorterDuffColorFilter
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.TextView
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.content.FileProvider
|
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import kotlinx.coroutines.MainScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.datetime.LocalDate
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
|
||||||
import kotlinx.serialization.json.decodeFromJsonElement
|
|
||||||
import okhttp3.*
|
|
||||||
import xyz.quaver.io.FileX
|
|
||||||
import xyz.quaver.io.util.readText
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.client
|
|
||||||
import xyz.quaver.pupil.favoriteTags
|
|
||||||
import xyz.quaver.pupil.favorites
|
|
||||||
import xyz.quaver.pupil.types.Tag
|
|
||||||
import xyz.quaver.pupil.util.get
|
|
||||||
import xyz.quaver.pupil.util.restore
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class ManageFavoritesFragment : PreferenceFragmentCompat() {
|
|
||||||
|
|
||||||
private val requestBackupFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode != Activity.RESULT_OK) {
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
val uri = result.data?.data ?: return@registerForActivityResult
|
|
||||||
val context = context ?: return@registerForActivityResult
|
|
||||||
val view = view ?: return@registerForActivityResult
|
|
||||||
|
|
||||||
val backupData = runCatching {
|
|
||||||
FileX(context, uri).readText()?.let { Json.parseToJsonElement(it) }
|
|
||||||
}.getOrNull() ?: run{
|
|
||||||
Snackbar.make(view, context.getString(R.string.error), Toast.LENGTH_LONG).show()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
val newFavorites = backupData["favorites"]?.let { Json.decodeFromJsonElement<List<Int>>(it) }.orEmpty()
|
|
||||||
val newFavoriteTags = backupData["favorite_tags"]?.let { Json.decodeFromJsonElement<List<Tag>>(it) }.orEmpty()
|
|
||||||
|
|
||||||
favorites.addAll(newFavorites)
|
|
||||||
favoriteTags.addAll(newFavoriteTags)
|
|
||||||
|
|
||||||
Snackbar.make(view, context.getString(R.string.settings_restore_success, newFavorites.size + newFavoriteTags.size), Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.manage_favorites_preferences, rootKey)
|
|
||||||
|
|
||||||
initPreferences()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initPreferences() {
|
|
||||||
val context = context ?: return
|
|
||||||
|
|
||||||
findPreference<Preference>("backup")?.setOnPreferenceClickListener {
|
|
||||||
val favorites = runCatching {
|
|
||||||
Json.parseToJsonElement(File(ContextCompat.getDataDir(context), "favorites.json").readText())
|
|
||||||
}.getOrNull()
|
|
||||||
val favoriteTags = kotlin.runCatching {
|
|
||||||
Json.parseToJsonElement(File(ContextCompat.getDataDir(context), "favorites_tags.json").readText())
|
|
||||||
}.getOrNull()
|
|
||||||
|
|
||||||
val favoriteJson = buildJsonObject {
|
|
||||||
favorites?.let {
|
|
||||||
put("favorites", it)
|
|
||||||
}
|
|
||||||
favoriteTags?.let {
|
|
||||||
put("favorite_tags", it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val backupFile = File(context.filesDir, "pupil-backup.json").also {
|
|
||||||
it.writeText(favoriteJson.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent(Intent.ACTION_SEND).apply {
|
|
||||||
val uri = FileProvider.getUriForFile(context, "${context.packageName}.provider", backupFile)
|
|
||||||
setDataAndType(uri, "application/json")
|
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
putExtra(Intent.EXTRA_STREAM, uri)
|
|
||||||
}.let {
|
|
||||||
context.startActivity(Intent.createChooser(it, getString(R.string.settings_backup_share)))
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>("restore")?.setOnPreferenceClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
type = "*/*"
|
|
||||||
}
|
|
||||||
|
|
||||||
requestBackupFileLauncher.launch(intent)
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.fragment
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.andrognito.pinlockview.PinLockListener
|
|
||||||
import xyz.quaver.pupil.databinding.PinLockFragmentBinding
|
|
||||||
|
|
||||||
class PINLockFragment : Fragment() {
|
|
||||||
|
|
||||||
private var _binding: PinLockFragmentBinding? = null
|
|
||||||
val binding get() = _binding!!
|
|
||||||
|
|
||||||
var onPINEntered: ((String) -> Unit)? = null
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
|
||||||
_binding = PinLockFragmentBinding.inflate(inflater, container, false)
|
|
||||||
|
|
||||||
binding.pinLockView.attachIndicatorDots(binding.indicatorDots)
|
|
||||||
binding.pinLockView.setPinLockListener(object: PinLockListener {
|
|
||||||
override fun onComplete(p0: String?) {
|
|
||||||
onPINEntered?.invoke(p0 ?: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onEmpty() {}
|
|
||||||
override fun onPinChange(p0: Int, p1: String?) {}
|
|
||||||
})
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2019 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.fragment
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.andrognito.patternlockview.PatternLockView
|
|
||||||
import com.andrognito.patternlockview.listener.PatternLockViewListener
|
|
||||||
import com.andrognito.patternlockview.utils.PatternLockUtils
|
|
||||||
import xyz.quaver.pupil.databinding.PatternLockFragmentBinding
|
|
||||||
|
|
||||||
class PatternLockFragment : Fragment() {
|
|
||||||
|
|
||||||
private var _binding: PatternLockFragmentBinding? = null
|
|
||||||
val binding get() = _binding!!
|
|
||||||
|
|
||||||
var onPatternDrawn: ((String) -> Unit)? = null
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
_binding = PatternLockFragmentBinding.inflate(inflater, container, false)
|
|
||||||
binding.patternLockView.addPatternLockListener(object: PatternLockViewListener {
|
|
||||||
override fun onComplete(pattern: MutableList<PatternLockView.Dot>?) {
|
|
||||||
val password = PatternLockUtils.patternToMD5(binding.patternLockView, pattern)
|
|
||||||
onPatternDrawn?.invoke(password)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {}
|
|
||||||
override fun onProgress(progressPattern: MutableList<PatternLockView.Dot>?) {}
|
|
||||||
override fun onStarted() {}
|
|
||||||
})
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.ui.fragment
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.*
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.preference.*
|
|
||||||
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import okhttp3.Dispatcher
|
|
||||||
import xyz.quaver.io.FileX
|
|
||||||
import xyz.quaver.io.util.getChild
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.client
|
|
||||||
import xyz.quaver.pupil.clientBuilder
|
|
||||||
import xyz.quaver.pupil.clientHolder
|
|
||||||
import xyz.quaver.pupil.types.SendLogException
|
|
||||||
import xyz.quaver.pupil.ui.LockActivity
|
|
||||||
import xyz.quaver.pupil.ui.SettingsActivity
|
|
||||||
import xyz.quaver.pupil.ui.dialog.*
|
|
||||||
import xyz.quaver.pupil.util.*
|
|
||||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
class SettingsFragment :
|
|
||||||
PreferenceFragmentCompat(),
|
|
||||||
Preference.OnPreferenceClickListener,
|
|
||||||
Preference.OnPreferenceChangeListener,
|
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
|
|
||||||
private val lockLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
if (it.resultCode == Activity.RESULT_OK) {
|
|
||||||
parentFragmentManager
|
|
||||||
.beginTransaction()
|
|
||||||
.replace(R.id.settings, LockSettingsFragment())
|
|
||||||
.addToBackStack("Lock")
|
|
||||||
.commitAllowingStateLoss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
|
|
||||||
val lockManager = LockManager(requireContext())
|
|
||||||
|
|
||||||
findPreference<Preference>("app_lock")?.summary = if (lockManager.locks.isNullOrEmpty()) {
|
|
||||||
getString(R.string.settings_lock_none)
|
|
||||||
} else {
|
|
||||||
lockManager.locks?.joinToString(", ") {
|
|
||||||
when(it.type) {
|
|
||||||
Lock.Type.PATTERN -> getString(R.string.settings_lock_pattern)
|
|
||||||
Lock.Type.PIN -> getString(R.string.settings_lock_pin)
|
|
||||||
Lock.Type.PASSWORD -> getString(R.string.settings_lock_password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPreferenceClick(preference: Preference): Boolean {
|
|
||||||
with (preference) {
|
|
||||||
when (key) {
|
|
||||||
"app_version" -> {
|
|
||||||
checkUpdate(activity as SettingsActivity, true)
|
|
||||||
}
|
|
||||||
"download_folder" -> {
|
|
||||||
DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog")
|
|
||||||
}
|
|
||||||
"default_query" -> {
|
|
||||||
DefaultQueryDialog().apply {
|
|
||||||
onPositiveButtonClickListener = { newTags ->
|
|
||||||
Preferences["default_query"] = newTags.toString()
|
|
||||||
summary = newTags.toString()
|
|
||||||
}
|
|
||||||
}.show(parentFragmentManager, "Default Query Dialog")
|
|
||||||
}
|
|
||||||
"app_lock" -> {
|
|
||||||
val intent = Intent(requireContext(), LockActivity::class.java).apply {
|
|
||||||
putExtra("force", true)
|
|
||||||
}
|
|
||||||
lockLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
"proxy" -> {
|
|
||||||
ProxyDialogFragment().show(parentFragmentManager, "Proxy Dialog")
|
|
||||||
}
|
|
||||||
"user_id" -> {
|
|
||||||
FirebaseCrashlytics.getInstance().recordException(SendLogException())
|
|
||||||
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
|
|
||||||
ClipData.newPlainText("user_id", Preferences.get<String>("user_id"))
|
|
||||||
)
|
|
||||||
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
|
|
||||||
with (preference) {
|
|
||||||
when (key) {
|
|
||||||
"tag_translation" -> {
|
|
||||||
updateTranslations()
|
|
||||||
}
|
|
||||||
"nomedia" -> {
|
|
||||||
val create = (newValue as? Boolean) ?: return false
|
|
||||||
|
|
||||||
return kotlin.runCatching {
|
|
||||||
val nomedia = DownloadManager.getInstance(context).downloadFolder.getChild(".nomedia")
|
|
||||||
|
|
||||||
if (create)
|
|
||||||
nomedia.createNewFile()
|
|
||||||
else
|
|
||||||
nomedia.delete()
|
|
||||||
}.getOrDefault(false)
|
|
||||||
}
|
|
||||||
"dark_mode" -> {
|
|
||||||
AppCompatDelegate.setDefaultNightMode(when (newValue as Boolean) {
|
|
||||||
true -> AppCompatDelegate.MODE_NIGHT_YES
|
|
||||||
false -> AppCompatDelegate.MODE_NIGHT_NO
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else -> return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
|
||||||
key ?: return
|
|
||||||
|
|
||||||
with(findPreference<Preference>(key)) {
|
|
||||||
this ?: return
|
|
||||||
|
|
||||||
when (key) {
|
|
||||||
"proxy" -> {
|
|
||||||
summary = context.let { getProxyInfo().type.name }
|
|
||||||
}
|
|
||||||
"download_folder" -> {
|
|
||||||
summary = FileX(context, Preferences.get<String>("download_folder")).canonicalPath
|
|
||||||
}
|
|
||||||
"download_folder_name" -> {
|
|
||||||
summary = Preferences["download_folder_name", "[-id-] -title-"]
|
|
||||||
}
|
|
||||||
"max_concurrent_download" -> {
|
|
||||||
val newValue = Preferences.get<String>(key).toIntOrNull() ?: 0
|
|
||||||
|
|
||||||
if (newValue == 0)
|
|
||||||
clientBuilder.dispatcher(Dispatcher())
|
|
||||||
else
|
|
||||||
clientBuilder.dispatcher((Dispatcher(Executors.newFixedThreadPool(newValue))))
|
|
||||||
|
|
||||||
clientHolder = null
|
|
||||||
client
|
|
||||||
}
|
|
||||||
else -> return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
|
||||||
|
|
||||||
Preferences.registerOnSharedPreferenceChangeListener(this)
|
|
||||||
|
|
||||||
initPreferences()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
Preferences.unregisterOnSharedPreferenceChangeListener(this)
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initPreferences() {
|
|
||||||
for (i in 0 until preferenceScreen.preferenceCount) {
|
|
||||||
|
|
||||||
preferenceScreen.getPreference(i).run {
|
|
||||||
if (this is PreferenceCategory)
|
|
||||||
(0 until preferenceCount).map { getPreference(it) }
|
|
||||||
else
|
|
||||||
listOf(this)
|
|
||||||
}.forEach { preference ->
|
|
||||||
with (preference) with@{
|
|
||||||
|
|
||||||
when (key) {
|
|
||||||
"app_version" -> {
|
|
||||||
val manager = requireContext().packageManager
|
|
||||||
val info = manager.getPackageInfo(requireContext().packageName, 0)
|
|
||||||
summary = requireContext().getString(R.string.settings_app_version_description, info.versionName)
|
|
||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"download_folder_name" -> {
|
|
||||||
summary = Preferences["download_folder_name", "[-id-] -title-"]
|
|
||||||
|
|
||||||
setOnPreferenceClickListener {
|
|
||||||
DownloadFolderNameDialogFragment().show(requireActivity().supportFragmentManager, "Download Location Dialog")
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"download_folder" -> {
|
|
||||||
summary = FileX(context, Preferences.get<String>("download_folder")).canonicalPath
|
|
||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"nomedia" -> {
|
|
||||||
(this as SwitchPreferenceCompat).isChecked = kotlin.runCatching {
|
|
||||||
DownloadManager.getInstance(context).downloadFolder.getChild(".nomedia").exists()
|
|
||||||
}.getOrDefault(false)
|
|
||||||
|
|
||||||
onPreferenceChangeListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"default_query" -> {
|
|
||||||
summary = Preferences.get<String>("default_query")
|
|
||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"app_lock" -> {
|
|
||||||
val lockManager = LockManager(requireContext())
|
|
||||||
summary =
|
|
||||||
if (lockManager.locks.isNullOrEmpty()) {
|
|
||||||
getString(R.string.settings_lock_none)
|
|
||||||
} else {
|
|
||||||
lockManager.locks?.joinToString(", ") {
|
|
||||||
when (it.type) {
|
|
||||||
Lock.Type.PATTERN -> getString(R.string.settings_lock_pattern)
|
|
||||||
Lock.Type.PIN -> getString(R.string.settings_lock_pin)
|
|
||||||
Lock.Type.PASSWORD -> getString(R.string.settings_lock_password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"proxy" -> {
|
|
||||||
summary = getProxyInfo().type.name
|
|
||||||
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"tag_translation" -> {
|
|
||||||
this as ListPreference
|
|
||||||
|
|
||||||
isEnabled = false
|
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
kotlin.runCatching {
|
|
||||||
val languages = getAvailableLanguages().distinct().toTypedArray()
|
|
||||||
|
|
||||||
entries = languages.map { Locale(it).let { loc -> loc.getDisplayLanguage(loc) } }.toTypedArray()
|
|
||||||
entryValues = languages
|
|
||||||
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onPreferenceChangeListener = this@SettingsFragment
|
|
||||||
|
|
||||||
}
|
|
||||||
"dark_mode" -> {
|
|
||||||
onPreferenceChangeListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"old_import_galleries" -> {
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"user_id" -> {
|
|
||||||
summary = Preferences.get<String>("user_id")
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"oss" -> {
|
|
||||||
setOnPreferenceClickListener {
|
|
||||||
context.startActivity(Intent(context, OssLicensesMenuActivity::class.java))
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
package xyz.quaver.pupil.ui.reader
|
|
||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
|
|
||||||
class ItemClickSupport(private val recyclerView: RecyclerView) {
|
|
||||||
|
|
||||||
var onItemClickListener: ((RecyclerView, Int, View) -> Unit)? = null
|
|
||||||
var onItemLongClickListener: ((RecyclerView, Int, View) -> Boolean)? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
recyclerView.apply {
|
|
||||||
setTag(R.id.item_click_support, this)
|
|
||||||
addOnChildAttachStateChangeListener(object: RecyclerView.OnChildAttachStateChangeListener {
|
|
||||||
override fun onChildViewAttachedToWindow(view: View) {
|
|
||||||
onItemClickListener?.let { listener ->
|
|
||||||
view.setOnClickListener {
|
|
||||||
recyclerView.getChildViewHolder(view).let { holder ->
|
|
||||||
listener.invoke(recyclerView, holder.adapterPosition, view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onItemLongClickListener?.let { listener ->
|
|
||||||
view.setOnLongClickListener {
|
|
||||||
recyclerView.getChildViewHolder(view).let { holder ->
|
|
||||||
listener.invoke(recyclerView, holder.adapterPosition, view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onChildViewDetachedFromWindow(view: View) {
|
|
||||||
// Do Nothing
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun detach() {
|
|
||||||
recyclerView.apply {
|
|
||||||
clearOnChildAttachStateChangeListeners()
|
|
||||||
setTag(R.id.item_click_support, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun addTo(view: RecyclerView) = view.let { removeFrom(it); ItemClickSupport(it) }
|
|
||||||
fun removeFrom(view: RecyclerView) = (view.tag as? ItemClickSupport)?.detach()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
lateinit var preferences: SharedPreferences
|
|
||||||
|
|
||||||
object Preferences: SharedPreferences by preferences {
|
|
||||||
|
|
||||||
val defMap = mapOf(
|
|
||||||
String::class to "",
|
|
||||||
Int::class to -1,
|
|
||||||
Long::class to -1L,
|
|
||||||
Boolean::class to false,
|
|
||||||
Set::class to emptySet<Any>()
|
|
||||||
)
|
|
||||||
|
|
||||||
operator fun set(key: String, value: String) = edit().putString(key, value).apply()
|
|
||||||
operator fun set(key: String, value: Int) = edit().putInt(key, value).apply()
|
|
||||||
operator fun set(key: String, value: Long) = edit().putLong(key, value).apply()
|
|
||||||
operator fun set(key: String, value: Boolean) = edit().putBoolean(key, value).apply()
|
|
||||||
operator fun set(key: String, value: Set<String>) = edit().putStringSet(key, value).apply()
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
inline operator fun <reified T: Any> get(key: String, defaultVal: T = defMap[T::class] as T): T = (all[key] as? T) ?: defaultVal
|
|
||||||
|
|
||||||
fun remove(key: String) {
|
|
||||||
edit().remove(key).apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
|
||||||
import kotlinx.serialization.KSerializer
|
|
||||||
import kotlinx.serialization.builtins.ListSerializer
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.serializer
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class SavedSet <T: Any> (private val file: File, private val any: T, private val set: MutableSet<T> = mutableSetOf()) : MutableSet<T> by set {
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
val serializer: KSerializer<List<T>>
|
|
||||||
get() = ListSerializer(serializer(any::class.java) as KSerializer<T>)
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.parentFile?.mkdirs()
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
load()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun load() {
|
|
||||||
set.clear()
|
|
||||||
kotlin.runCatching {
|
|
||||||
Json.decodeFromString(serializer, file.readText())
|
|
||||||
}.onSuccess {
|
|
||||||
set.addAll(it)
|
|
||||||
}.onFailure {
|
|
||||||
FirebaseCrashlytics.getInstance().recordException(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
fun save() {
|
|
||||||
file.writeText(Json.encodeToString(serializer, set.toList()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
override fun add(element: T): Boolean {
|
|
||||||
set.remove(element)
|
|
||||||
|
|
||||||
return set.add(element).also {
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
override fun addAll(elements: Collection<T>): Boolean {
|
|
||||||
set.removeAll(elements)
|
|
||||||
|
|
||||||
return set.addAll(elements).also {
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
override fun remove(element: T): Boolean {
|
|
||||||
return set.remove(element).also {
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
override fun clear() {
|
|
||||||
set.clear()
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("DEPRECATION", "Recycle")
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.ImageFormat
|
|
||||||
import android.graphics.SurfaceTexture
|
|
||||||
import android.hardware.Camera
|
|
||||||
import android.view.Surface
|
|
||||||
import android.view.WindowManager
|
|
||||||
import com.google.android.gms.tasks.Task
|
|
||||||
import com.google.mlkit.vision.common.InputImage
|
|
||||||
import com.google.mlkit.vision.face.Face
|
|
||||||
import com.google.mlkit.vision.face.FaceDetection
|
|
||||||
import com.google.mlkit.vision.face.FaceDetectorOptions
|
|
||||||
|
|
||||||
/** Check if this device has a camera */
|
|
||||||
private fun Context.checkCameraHardware() =
|
|
||||||
this.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
|
||||||
|
|
||||||
private fun openFrontCamera() : Pair<Camera?, Int> {
|
|
||||||
var camera: Camera? = null
|
|
||||||
var cameraID: Int = -1
|
|
||||||
|
|
||||||
val cameraInfo = Camera.CameraInfo()
|
|
||||||
|
|
||||||
for (i in 0 until Camera.getNumberOfCameras()) {
|
|
||||||
Camera.getCameraInfo(i, cameraInfo)
|
|
||||||
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
|
|
||||||
runCatching { Camera.open(i) }.getOrNull()?.let { camera = it; cameraID = i }
|
|
||||||
|
|
||||||
if (camera != null) break
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pair(camera, cameraID)
|
|
||||||
}
|
|
||||||
|
|
||||||
val orientations = mapOf(
|
|
||||||
Surface.ROTATION_0 to 0,
|
|
||||||
Surface.ROTATION_90 to 90,
|
|
||||||
Surface.ROTATION_180 to 180,
|
|
||||||
Surface.ROTATION_270 to 270,
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getRotation(context: Context, cameraID: Int): Int {
|
|
||||||
val cameraRotation = Camera.CameraInfo().also { Camera.getCameraInfo(cameraID, it) }.orientation
|
|
||||||
val rotation = orientations[(context.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.rotation] ?: error("")
|
|
||||||
|
|
||||||
return (cameraRotation + rotation) % 360
|
|
||||||
}
|
|
||||||
|
|
||||||
var camera: Camera? = null
|
|
||||||
var surfaceTexture: SurfaceTexture? = null
|
|
||||||
private val detector = FaceDetection.getClient(
|
|
||||||
FaceDetectorOptions.Builder()
|
|
||||||
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
private var process: Task<List<Face>>? = null
|
|
||||||
|
|
||||||
fun startCamera(context: Context, callback: (List<Face>) -> Unit) {
|
|
||||||
if (camera != null) closeCamera()
|
|
||||||
|
|
||||||
val cameraID = openFrontCamera().let { (cam, cameraID) ->
|
|
||||||
cam ?: return
|
|
||||||
camera = cam
|
|
||||||
cameraID
|
|
||||||
}
|
|
||||||
|
|
||||||
with (camera!!) {
|
|
||||||
parameters = parameters.apply {
|
|
||||||
setPreviewSize(640, 480)
|
|
||||||
previewFormat = ImageFormat.NV21
|
|
||||||
}
|
|
||||||
|
|
||||||
setPreviewTexture(surfaceTexture ?: SurfaceTexture(0).also {
|
|
||||||
surfaceTexture = it
|
|
||||||
})
|
|
||||||
startPreview()
|
|
||||||
setPreviewCallback { bytes, _ ->
|
|
||||||
if (process?.isComplete == false)
|
|
||||||
return@setPreviewCallback
|
|
||||||
|
|
||||||
val rotation = getRotation(context, cameraID)
|
|
||||||
|
|
||||||
val image = InputImage.fromByteArray(bytes, 640, 480, rotation, InputImage.IMAGE_FORMAT_NV21)
|
|
||||||
process = detector.process(image)
|
|
||||||
.addOnSuccessListener(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun closeCamera() {
|
|
||||||
camera?.setPreviewCallback(null)
|
|
||||||
camera?.stopPreview()
|
|
||||||
surfaceTexture?.release()
|
|
||||||
surfaceTexture = null
|
|
||||||
camera?.release()
|
|
||||||
camera = null
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2019 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import xyz.quaver.pupil.histories
|
|
||||||
import xyz.quaver.pupil.util.downloader.Cache
|
|
||||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
val mutex = Mutex()
|
|
||||||
fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
if (mutex.isLocked) return@launch
|
|
||||||
|
|
||||||
mutex.withLock {
|
|
||||||
val cacheFolder = File(context.cacheDir, "imageCache")
|
|
||||||
val downloadManager = DownloadManager.getInstance(context)
|
|
||||||
|
|
||||||
val limit = (Preferences.get<String>("cache_limit").toLongOrNull() ?: 0L)*1024*1024*1024
|
|
||||||
|
|
||||||
if (limit == 0L) return@withLock
|
|
||||||
|
|
||||||
val cacheSize = {
|
|
||||||
var size = 0L
|
|
||||||
|
|
||||||
cacheFolder.walk().forEach {
|
|
||||||
size += it.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
size
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheSize.invoke() > limit)
|
|
||||||
while (cacheSize.invoke() > limit/2) {
|
|
||||||
val caches = cacheFolder.list() ?: return@withLock
|
|
||||||
|
|
||||||
synchronized(histories) {
|
|
||||||
(histories.firstOrNull {
|
|
||||||
caches.contains(it.toString()) && !downloadManager.isDownloading(it)
|
|
||||||
} ?: return@withLock).let {
|
|
||||||
Cache.delete(context, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2019 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.ContextWrapper
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import java.io.File
|
|
||||||
import java.security.MessageDigest
|
|
||||||
|
|
||||||
fun hash(password: String): String {
|
|
||||||
val bytes = password.toByteArray()
|
|
||||||
val md = MessageDigest.getInstance("SHA-256")
|
|
||||||
|
|
||||||
return md.digest(bytes).fold("") { str, it -> str + "%02x".format(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ret1: SHA-256 Hash
|
|
||||||
// Ret2: Hash salt
|
|
||||||
fun hashWithSalt(password: String): Pair<String, String> {
|
|
||||||
val salt = (0 until 12).map { source.random() }.joinToString()
|
|
||||||
|
|
||||||
return Pair(hash(password+salt), salt)
|
|
||||||
}
|
|
||||||
|
|
||||||
const val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Lock(val type: Type, val hash: String, val salt: String) {
|
|
||||||
|
|
||||||
enum class Type {
|
|
||||||
PATTERN,
|
|
||||||
PIN,
|
|
||||||
PASSWORD
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun generate(type: Type, password: String): Lock {
|
|
||||||
val (hash, salt) = hashWithSalt(password)
|
|
||||||
return Lock(type, hash, salt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun match(password: String): Boolean {
|
|
||||||
return hash(password+salt) == hash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LockManager(base: Context): ContextWrapper(base) {
|
|
||||||
|
|
||||||
var locks: ArrayList<Lock>? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
load()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun load() {
|
|
||||||
val lock = File(ContextCompat.getDataDir(this), "lock.json")
|
|
||||||
|
|
||||||
if (!lock.exists()) {
|
|
||||||
lock.createNewFile()
|
|
||||||
lock.writeText("[]")
|
|
||||||
}
|
|
||||||
|
|
||||||
locks = Json.decodeFromString(lock.readText())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun save() {
|
|
||||||
val lock = File(ContextCompat.getDataDir(this), "lock.json")
|
|
||||||
|
|
||||||
if (!lock.exists())
|
|
||||||
lock.createNewFile()
|
|
||||||
|
|
||||||
lock.writeText(Json.encodeToString(locks?.toList() ?: listOf()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun add(lock: Lock) {
|
|
||||||
remove(lock.type)
|
|
||||||
locks?.add(lock)
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remove(type: Lock.Type) {
|
|
||||||
locks?.removeAll { it.type == type }
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun check(password: String): Boolean? {
|
|
||||||
return locks?.any {
|
|
||||||
it.match(password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isEmpty(): Boolean {
|
|
||||||
return locks.isNullOrEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isNotEmpty(): Boolean = !isEmpty()
|
|
||||||
|
|
||||||
fun contains(type: Lock.Type): Boolean {
|
|
||||||
return locks?.any { it.type == type } ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2019 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import kotlinx.serialization.json.*
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import xyz.quaver.pupil.R
|
|
||||||
import xyz.quaver.pupil.hitomi.GalleryBlock
|
|
||||||
import xyz.quaver.pupil.hitomi.GalleryInfo
|
|
||||||
import xyz.quaver.pupil.hitomi.imageUrlFromImage
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
|
||||||
fun String.wordCapitalize() : String {
|
|
||||||
val result = ArrayList<String>()
|
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
for (word in this.split(" "))
|
|
||||||
result.add(word.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() })
|
|
||||||
|
|
||||||
return result.joinToString(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val suffix = listOf(
|
|
||||||
"B",
|
|
||||||
"kB",
|
|
||||||
"MB",
|
|
||||||
"GB",
|
|
||||||
"TB" //really?
|
|
||||||
)
|
|
||||||
|
|
||||||
fun byteToString(byte: Long, precision : Int = 1) : String {
|
|
||||||
var size = byte.toDouble(); var suffixIndex = 0
|
|
||||||
|
|
||||||
while (size >= 1024) {
|
|
||||||
size /= 1024
|
|
||||||
suffixIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
return "%.${precision}f ${suffix[suffixIndex]}".format(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert android generated ID to requestCode
|
|
||||||
* to prevent java.lang.IllegalArgumentException: Can only use lower 16 bits for requestCode
|
|
||||||
*
|
|
||||||
* https://stackoverflow.com/questions/38072322/generate-16-bit-unique-ids-in-android-for-startactivityforresult
|
|
||||||
*/
|
|
||||||
fun Int.normalizeID() = this.and(0xFFFF)
|
|
||||||
|
|
||||||
fun OkHttpClient.Builder.proxyInfo(proxyInfo: ProxyInfo) = this.apply {
|
|
||||||
proxy(proxyInfo.proxy())
|
|
||||||
proxyInfo.authenticator()?.let {
|
|
||||||
proxyAuthenticator(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val formatMap = mapOf<String, GalleryBlock.() -> (String)>(
|
|
||||||
"-id-" to { id.toString() },
|
|
||||||
"-title-" to { title },
|
|
||||||
"-artist-" to { if (artists.isNotEmpty()) artists.joinToString() else "N/A" },
|
|
||||||
"-group-" to { if (groups.isNotEmpty()) groups.joinToString() else "N/A" }
|
|
||||||
// TODO
|
|
||||||
)
|
|
||||||
/**
|
|
||||||
* Formats download folder name with given Metadata
|
|
||||||
*/
|
|
||||||
fun GalleryBlock.formatDownloadFolder(): String =
|
|
||||||
Preferences["download_folder_name", "[-id-] -title-"].let {
|
|
||||||
formatMap.entries.fold(it) { str, (k, v) ->
|
|
||||||
str.replace(k, v.invoke(this), true)
|
|
||||||
}
|
|
||||||
}.replace(Regex("""[*\\|"?><:/]"""), "").ellipsize(127)
|
|
||||||
|
|
||||||
fun GalleryBlock.formatDownloadFolderTest(format: String): String =
|
|
||||||
format.let {
|
|
||||||
formatMap.entries.fold(it) { str, (k, v) ->
|
|
||||||
str.replace(k, v.invoke(this), true)
|
|
||||||
}
|
|
||||||
}.replace(Regex("""[*\\|"?><:/]"""), "").ellipsize(127)
|
|
||||||
|
|
||||||
suspend fun GalleryInfo.getRequestBuilders(): List<Request.Builder> {
|
|
||||||
val galleryID = this.id.toIntOrNull() ?: 0
|
|
||||||
return this.files.map {
|
|
||||||
Request.Builder()
|
|
||||||
.url(
|
|
||||||
runCatching {
|
|
||||||
imageUrlFromImage(galleryID, it, false)
|
|
||||||
}
|
|
||||||
.onFailure {
|
|
||||||
FirebaseCrashlytics.getInstance().recordException(it)
|
|
||||||
}
|
|
||||||
.getOrDefault("https://a/")
|
|
||||||
)
|
|
||||||
.header("Referer", "https://hitomi.la/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun String.ellipsize(n: Int): String =
|
|
||||||
if (this.length > n)
|
|
||||||
this.slice(0 until n) + "…"
|
|
||||||
else
|
|
||||||
this
|
|
||||||
|
|
||||||
operator fun JsonElement.get(index: Int) =
|
|
||||||
this.jsonArray[index]
|
|
||||||
|
|
||||||
operator fun JsonElement.get(tag: String) =
|
|
||||||
this.jsonObject[tag]
|
|
||||||
|
|
||||||
fun JsonElement.getOrNull(tag: String) = kotlin.runCatching {
|
|
||||||
this.jsonObject.getOrDefault(tag, null)
|
|
||||||
}.getOrNull()
|
|
||||||
|
|
||||||
val JsonElement.content
|
|
||||||
get() = this.jsonPrimitive.contentOrNull
|
|
||||||
|
|
||||||
fun checkNotificationEnabled(context: Context) =
|
|
||||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
|
|
||||||
ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
|
|
||||||
|
|
||||||
fun showNotificationPermissionExplanationDialog(context: Context) {
|
|
||||||
AlertDialog.Builder(context)
|
|
||||||
.setTitle(R.string.warning)
|
|
||||||
.setMessage(R.string.notification_denied)
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun requestNotificationPermission(
|
|
||||||
activity: Activity,
|
|
||||||
requestPermissionLauncher: ActivityResultLauncher<String>,
|
|
||||||
showRationale: Boolean = true,
|
|
||||||
ifGranted: () -> Unit,
|
|
||||||
) {
|
|
||||||
when {
|
|
||||||
checkNotificationEnabled(activity) -> ifGranted()
|
|
||||||
showRationale && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.POST_NOTIFICATIONS) ->
|
|
||||||
showNotificationPermissionExplanationDialog(activity)
|
|
||||||
else ->
|
|
||||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import okhttp3.Authenticator
|
|
||||||
import okhttp3.Credentials
|
|
||||||
import java.net.InetSocketAddress
|
|
||||||
import java.net.Proxy
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ProxyInfo(
|
|
||||||
val type: Proxy.Type,
|
|
||||||
val host: String? = null,
|
|
||||||
val port: Int? = null,
|
|
||||||
val username: String? = null,
|
|
||||||
val password: String? = null
|
|
||||||
) {
|
|
||||||
fun proxy() : Proxy {
|
|
||||||
return if (host.isNullOrBlank() || port == null)
|
|
||||||
return Proxy.NO_PROXY
|
|
||||||
else
|
|
||||||
Proxy(type, InetSocketAddress.createUnresolved(host, port))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun authenticator(): Authenticator? = if (username.isNullOrBlank() || password.isNullOrBlank()) null else
|
|
||||||
Authenticator { _, response ->
|
|
||||||
val credential = Credentials.basic(username, password)
|
|
||||||
|
|
||||||
response.request.newBuilder()
|
|
||||||
.header("Proxy-Authorization", credential)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getProxyInfo(): ProxyInfo =
|
|
||||||
Json.decodeFromString(Preferences["proxy", Json.encodeToString(ProxyInfo(Proxy.Type.DIRECT))])
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2020 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.jsonArray
|
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
|
||||||
import okhttp3.Request
|
|
||||||
import xyz.quaver.pupil.client
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
private val filesURL = "https://api.github.com/repos/tom5079/Pupil/git/trees/tags"
|
|
||||||
private val contentURL = "https://raw.githubusercontent.com/tom5079/Pupil/tags/"
|
|
||||||
|
|
||||||
var translations: Map<String, String> = run {
|
|
||||||
updateTranslations()
|
|
||||||
emptyMap()
|
|
||||||
}
|
|
||||||
private set
|
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
fun updateTranslations() = CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
translations = emptyMap()
|
|
||||||
kotlin.runCatching {
|
|
||||||
translations = Json.decodeFromString<Map<String, String>>(client.newCall(
|
|
||||||
Request.Builder()
|
|
||||||
.url(contentURL + "${Preferences["tag_translation", ""].let { if (it.isEmpty()) Locale.getDefault().language else it }}.json")
|
|
||||||
.build()
|
|
||||||
).execute().also { if (it.code != 200) return@launch }.body?.use { it.string() } ?: return@launch).filterValues { it.isNotEmpty() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAvailableLanguages(): List<String> {
|
|
||||||
val languages = Locale.getISOLanguages()
|
|
||||||
|
|
||||||
val json = Json.parseToJsonElement(client.newCall(
|
|
||||||
Request.Builder()
|
|
||||||
.url(filesURL)
|
|
||||||
.build()
|
|
||||||
).execute().also { if (it.code != 200) throw IOException() }.body?.use { it.string() } ?: return emptyList())
|
|
||||||
|
|
||||||
return listOf("en") + (json["tree"]?.jsonArray?.mapNotNull {
|
|
||||||
val name = it["path"]?.jsonPrimitive?.content?.takeWhile { c -> c != '.' }
|
|
||||||
|
|
||||||
languages.firstOrNull { code -> code.equals(name, ignoreCase = true) }
|
|
||||||
} ?: emptyList())
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
/*
|
|
||||||
* Pupil, Hitomi.la viewer for Android
|
|
||||||
* Copyright (C) 2019 tom5079
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package xyz.quaver.pupil.util
|
|
||||||
|
|
||||||
import android.app.DownloadManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
|
||||||
import android.webkit.URLUtil
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.serialization.json.*
|
|
||||||
import okhttp3.Call
|
|
||||||
import okhttp3.Callback
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import ru.noties.markwon.Markwon
|
|
||||||
import xyz.quaver.pupil.*
|
|
||||||
import xyz.quaver.pupil.types.Tag
|
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.net.URL
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
fun getReleases(url: String) : JsonArray {
|
|
||||||
return try {
|
|
||||||
URL(url).readText().let {
|
|
||||||
Json.parseToJsonElement(it).jsonArray
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
JsonArray(emptyList())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkUpdate(url: String) : JsonObject? {
|
|
||||||
val releases = getReleases(url)
|
|
||||||
|
|
||||||
if (releases.isEmpty())
|
|
||||||
return null
|
|
||||||
|
|
||||||
return releases.firstOrNull {
|
|
||||||
Preferences["beta"] || it.jsonObject["prerelease"]?.jsonPrimitive?.booleanOrNull == false
|
|
||||||
}?.let {
|
|
||||||
if (it.jsonObject["tag_name"]?.jsonPrimitive?.contentOrNull == BuildConfig.VERSION_NAME)
|
|
||||||
null
|
|
||||||
else
|
|
||||||
it.jsonObject
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getApkUrl(releases: JsonObject) : String? {
|
|
||||||
return releases["assets"]?.jsonArray?.firstOrNull {
|
|
||||||
Regex("Pupil-v.+\\.apk").matches(it.jsonObject["name"]?.jsonPrimitive?.contentOrNull ?: "")
|
|
||||||
}.let {
|
|
||||||
it?.jsonObject?.get("browser_download_url")?.jsonPrimitive?.contentOrNull
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkUpdate(context: Context, force: Boolean = false) {
|
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
val ignoreUpdateUntil = preferences.getLong("ignore_update_until", 0)
|
|
||||||
|
|
||||||
if (!force && ignoreUpdateUntil > System.currentTimeMillis())
|
|
||||||
return
|
|
||||||
|
|
||||||
fun extractReleaseNote(update: JsonObject, locale: Locale) : String {
|
|
||||||
val markdown = update["body"]!!.jsonPrimitive.content
|
|
||||||
|
|
||||||
val target = when(locale.language) {
|
|
||||||
"ko" -> "한국어"
|
|
||||||
"ja" -> "日本語"
|
|
||||||
else -> "English"
|
|
||||||
}
|
|
||||||
|
|
||||||
val releaseNote = Regex("^# Release Note.+$")
|
|
||||||
val language = Regex("^## $target$")
|
|
||||||
val end = Regex("^#.+$")
|
|
||||||
|
|
||||||
var releaseNoteFlag = false
|
|
||||||
var languageFlag = false
|
|
||||||
|
|
||||||
val result = StringBuilder()
|
|
||||||
|
|
||||||
for(line in markdown.lines()) {
|
|
||||||
if (releaseNote.matches(line)) {
|
|
||||||
releaseNoteFlag = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (releaseNoteFlag) {
|
|
||||||
if (language.matches(line)) {
|
|
||||||
languageFlag = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (languageFlag) {
|
|
||||||
if (end.matches(line))
|
|
||||||
break
|
|
||||||
|
|
||||||
result.append(line+"\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.getString(R.string.update_release_note, update["tag_name"]?.jsonPrimitive?.contentOrNull, result.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Default).launch {
|
|
||||||
val update =
|
|
||||||
checkUpdate(context.getString(R.string.release_url)) ?: return@launch
|
|
||||||
|
|
||||||
val url = getApkUrl(update) ?: return@launch
|
|
||||||
|
|
||||||
val dialog = AlertDialog.Builder(context).apply {
|
|
||||||
setTitle(R.string.update_title)
|
|
||||||
val msg = extractReleaseNote(update, Locale.getDefault())
|
|
||||||
setMessage(Markwon.create(context).toMarkdown(msg))
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
if (!checkNotificationEnabled(context)) {
|
|
||||||
showNotificationPermissionExplanationDialog(context)
|
|
||||||
return@setPositiveButton
|
|
||||||
}
|
|
||||||
|
|
||||||
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
|
||||||
|
|
||||||
//Cancel any download queued before
|
|
||||||
|
|
||||||
val id: Long = Preferences["update_download_id"]
|
|
||||||
|
|
||||||
if (id != -1L)
|
|
||||||
downloadManager.remove(id)
|
|
||||||
|
|
||||||
val target = File(context.getExternalFilesDir(null), "Pupil.apk").also {
|
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
val request = DownloadManager.Request(Uri.parse(url))
|
|
||||||
.setTitle(context.getText(R.string.update_notification_description))
|
|
||||||
.setDestinationUri(Uri.fromFile(target))
|
|
||||||
|
|
||||||
downloadManager.enqueue(request).also {
|
|
||||||
Preferences["update_download_id"] = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setNegativeButton(if (force) android.R.string.cancel else R.string.ignore) { _, _ ->
|
|
||||||
if (!force)
|
|
||||||
preferences.edit()
|
|
||||||
.putLong("ignore_update_until", System.currentTimeMillis() + 86400000)
|
|
||||||
.apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
dialog.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun restore(url: String, onFailure: ((Throwable) -> Unit)? = null, onSuccess: ((Int) -> Unit)? = null) {
|
|
||||||
if (!URLUtil.isValidUrl(url)) {
|
|
||||||
onFailure?.invoke(IllegalArgumentException())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val request = Request.Builder()
|
|
||||||
.url(url)
|
|
||||||
.get()
|
|
||||||
.build()
|
|
||||||
|
|
||||||
client.newCall(request).enqueue(object: Callback {
|
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
|
||||||
onFailure?.invoke(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
|
||||||
kotlin.runCatching {
|
|
||||||
val data = Json.parseToJsonElement(response.also { if (it.code != 200) throw IOException() }.body.use { it?.string() } ?: "[]")
|
|
||||||
|
|
||||||
when (data) {
|
|
||||||
is JsonArray -> favorites.addAll(data.map { it.jsonPrimitive.int })
|
|
||||||
is JsonObject -> {
|
|
||||||
val newFavorites = data["favorites"]?.let { Json.decodeFromJsonElement<List<Int>>(it) }.orEmpty()
|
|
||||||
val newFavoriteTags = data["favorite_tags"]?.let { Json.decodeFromJsonElement<List<Tag>>(it) }.orEmpty()
|
|
||||||
|
|
||||||
favorites.addAll(newFavorites)
|
|
||||||
favoriteTags.addAll(newFavoriteTags)
|
|
||||||
|
|
||||||
onSuccess?.invoke(favorites.size + favoriteTags.size)
|
|
||||||
}
|
|
||||||
else -> error("data is neither JsonArray or JsonObject")
|
|
||||||
}
|
|
||||||
}.onFailure { onFailure?.invoke(it) }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<translate xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:duration="300"
|
|
||||||
android:fromXDelta="0"
|
|
||||||
android:interpolator="@anim/shake_cycle"
|
|
||||||
android:toXDelta="10" />
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:cycles="3" />
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_enabled="false" android:color="@android:color/darker_gray"/>
|
|
||||||
<item android:color="@color/colorPrimary"/>
|
|
||||||
</selector>
|
|
||||||
|
Before Width: | Height: | Size: 145 B |
|
Before Width: | Height: | Size: 255 B |
|
Before Width: | Height: | Size: 99 B |
|
Before Width: | Height: | Size: 183 B |
|
Before Width: | Height: | Size: 125 B |
|
Before Width: | Height: | Size: 311 B |
|
Before Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 432 B |
|
Before Width: | Height: | Size: 605 B |
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/account_group.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12 5.5A3.5 3.5 0 0 1 15.5 9a3.5 3.5 0 0 1-3.5 3.5A3.5 3.5 0 0 1 8.5 9 3.5 3.5 0 0 1 12 5.5M5 8c0.56 0 1.08 0.15 1.53 0.42-0.15 1.43 0.27 2.85 1.13 3.96C7.16 13.34 6.16 14 5 14a3 3 0 0 1-3-3 3 3 0 0 1 3-3m14 0a3 3 0 0 1 3 3 3 3 0 0 1-3 3c-1.16 0-2.16-0.66-2.66-1.62 0.86-1.11 1.28-2.53 1.13-3.96C17.92 8.15 18.44 8 19 8M5.5 18.25c0-2.07 2.91-3.75 6.5-3.75s6.5 1.68 6.5 3.75V20h-13v-1.75M0 20v-1.5c0-1.39 1.89-2.56 4.45-2.9-0.59 0.68-0.95 1.62-0.95 2.65V20H0m24 0h-3.5v-1.75c0-1.03-0.36-1.97-0.95-2.65 2.56 0.34 4.45 1.51 4.45 2.9V20z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/account_star.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M15 14c-2.67 0-8 1.33-8 4v2h16v-2c0-2.67-5.33-4-8-4m0-2a4 4 0 0 0 4-4 4 4 0 0 0-4-4 4 4 0 0 0-4 4 4 4 0 0 0 4 4M5 13.28l2.45 1.49-0.65-2.81L9 10.08 6.11 9.83 5 7.19 3.87 9.83 1 10.08l2.18 1.88-0.68 2.81L5 13.28z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" xmlns:tools="http://schemas.android.com/tools" tools:ignore="newApi">
|
|
||||||
<aapt:attr name="android:drawable">
|
|
||||||
<vector android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:name="path" android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z" android:fillColor="@color/material_orange_500"/>
|
|
||||||
<clip-path android:name="clip" android:pathData="M 2 21 L 2 21 L 22 21 L 22 21 Z"/>
|
|
||||||
<path android:name="path_1" android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z" android:fillColor="@color/material_orange_500"/>
|
|
||||||
</vector>
|
|
||||||
</aapt:attr>
|
|
||||||
<target android:name="clip">
|
|
||||||
<aapt:attr name="android:animation">
|
|
||||||
<objectAnimator android:propertyName="pathData" android:duration="500" android:valueFrom="M 2 21 L 2 21 L 22 21 L 22 21 Z" android:valueTo="M 2 2 L 2 21 L 22 21 L 22 2 Z" android:valueType="pathType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</aapt:attr>
|
|
||||||
</target>
|
|
||||||
</animated-vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/backspace_outline.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#000" android:pathData="M19 15.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59M22 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H7c-0.69 0-1.23-0.36-1.59-0.89L0 12l5.41-8.12C5.77 3.35 6.31 3 7 3h15m0 2H7l-4.72 7L7 19h15V5z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/book_open.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M13 12h7v1.5h-7m0-4h7V11h-7m0 3.5h7V16h-7m8-12H3a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 15h-9V6h9"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/brush.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M20.71 4.63l-1.34-1.34c-0.37-0.39-1.02-0.39-1.41 0L9 12.25 11.75 15l8.96-8.96c0.39-0.39 0.39-1.04 0-1.41M7 14a3 3 0 0 0-3 3c0 1.31-1.16 2-2 2 0.92 1.22 2.5 2 4 2a4 4 0 0 0 4-4 3 3 0 0 0-3-3z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/close.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/delete.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M19 4h-3.5l-1-1h-5l-1 1H5v2h14M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7H6v12z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<shape
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:shape="oval">
|
|
||||||
|
|
||||||
<solid
|
|
||||||
android:color="@color/colorAccent"/>
|
|
||||||
|
|
||||||
<size
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"/>
|
|
||||||
</shape>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<!-- drawable/eye.xml -->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:height="24dp"
|
|
||||||
android:width="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ 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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="22dp"
|
|
||||||
android:height="15dp"
|
|
||||||
android:viewportWidth="22"
|
|
||||||
android:viewportHeight="15">
|
|
||||||
<path
|
|
||||||
android:pathData="M21.61,5.4C14.21,13.39 7.16,13.37 0.43,5.32"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="?attr/colorControlNormal"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M1.32,9.8L3.03,7.8"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="?attr/colorControlNormal"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M5.14,12.37L6.16,10.37"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="?attr/colorControlNormal"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M16.27,12.37L15.25,10.37"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="?attr/colorControlNormal"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M18.78,7.8L20.49,9.8"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="?attr/colorControlNormal"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<!-- drawable/eye_off.xml -->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:height="24dp"
|
|
||||||
android:width="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<!-- drawable/eye_off.xml -->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:height="24dp"
|
|
||||||
android:width="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<!-- drawable/eye.xml -->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:height="24dp"
|
|
||||||
android:width="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/fingerprint.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M17.81 4.47c-0.08 0-0.16-0.02-0.23-0.06C15.66 3.42 14 3 12 3c-1.97 0-3.85 0.47-5.56 1.41C6.2 4.54 5.9 4.45 5.76 4.21c-0.13-0.24-0.04-0.55 0.2-0.68C7.82 2.5 9.86 2 12 2s4 0.47 6.04 1.5c0.25 0.15 0.34 0.45 0.21 0.69-0.09 0.18-0.25 0.28-0.44 0.28M3.5 9.72c-0.1 0-0.2-0.03-0.29-0.09C3 9.47 2.93 9.16 3.09 8.93c0.99-1.4 2.25-2.5 3.75-3.27C10 4.04 14 4.03 17.15 5.65c1.5 0.77 2.76 1.85 3.75 3.25 0.16 0.22 0.1 0.54-0.12 0.7-0.23 0.16-0.54 0.11-0.7-0.1-0.9-1.28-2.04-2.27-3.39-2.96-2.87-1.47-6.54-1.47-9.4 0.01-1.36 0.7-2.5 1.7-3.4 2.95C3.81 9.65 3.66 9.72 3.5 9.72m6.25 12.07c-0.13 0-0.25-0.05-0.35-0.15-0.87-0.87-1.34-1.43-2.01-2.64-0.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39a0.5 0.5 0 0 1-0.5 0.5 0.5 0.5 0 0 1-0.5-0.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44 0.32 2.77 0.93 3.84 0.64 1.16 1.08 1.65 1.85 2.43 0.19 0.2 0.19 0.51 0 0.71-0.12 0.1-0.24 0.15-0.37 0.15m7.17-1.85c-1.19 0-2.24-0.3-3.1-0.89-1.49-1.01-2.38-2.65-2.38-4.39a0.5 0.5 0 0 1 0.5-0.5 0.5 0.5 0 0 1 0.5 0.5c0 1.41 0.72 2.74 1.94 3.56 0.71 0.48 1.54 0.71 2.54 0.71 0.24 0 0.64-0.03 1.04-0.1 0.27-0.05 0.54 0.13 0.58 0.41 0.05 0.26-0.13 0.53-0.41 0.58-0.57 0.11-1.07 0.12-1.21 0.12M14.91 22h-0.13c-1.59-0.46-2.63-1.05-3.72-2.12-1.4-1.38-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07 0.95 1.94 2.08 1.94 1.15 0 2.08-0.87 2.08-1.94 0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.46 1.58-6.61 4.03-0.39 0.81-0.59 1.76-0.59 2.8 0 0.78 0.07 2.01 0.67 3.61 0.1 0.26-0.03 0.55-0.29 0.64C4.88 19 4.59 18.87 4.5 18.62 4 17.31 3.77 16 3.77 14.66c0-1.2 0.23-2.29 0.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.54 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94-1.7 0-3.08-1.32-3.08-2.94 0-1.07-0.93-1.94-2.08-1.94s-2.08 0.87-2.08 1.94c0 1.71 0.66 3.31 1.87 4.51 0.95 0.94 1.86 1.46 3.27 1.84 0.27 0.08 0.42 0.36 0.35 0.62-0.05 0.23-0.26 0.38-0.47 0.38z" tools:ignore="VectorPath"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/gender_female.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12 4a6 6 0 0 1 6 6c0 2.97-2.16 5.44-5 5.92V18h2v2h-2v2h-2v-2H9v-2h2v-2.08C8.16 15.44 6 12.97 6 10a6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/gender_female.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#FFF" android:pathData="M12 4a6 6 0 0 1 6 6c0 2.97-2.16 5.44-5 5.92V18h2v2h-2v2h-2v-2H9v-2h2v-2.08C8.16 15.44 6 12.97 6 10a6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/gender_male.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M9 9c1.29 0 2.5 0.41 3.47 1.11L17.58 5H13V3h8v8h-2V6.41l-5.11 5.09c0.7 1 1.11 2.2 1.11 3.5a6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/gender_male.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#FFF" android:pathData="M9 9c1.29 0 2.5 0.41 3.47 1.11L17.58 5H13V3h8v8h-2V6.41l-5.11 5.09c0.7 1 1.11 2.2 1.11 3.5a6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/history.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M13.5 8H12v5l4.28 2.54 0.72-1.21-3.5-2.08V8M13 3a9 9 0 0 0-9 9H1l3.96 4.03L9 12H6a7 7 0 0 1 7-7 7 7 0 0 1 7 7 7 7 0 0 1-7 7c-1.93 0-3.68-0.79-4.94-2.06l-1.42 1.42C8.27 20 10.5 21 13 21a9 9 0 0 0 9-9 9 9 0 0 0-9-9"/>
|
|
||||||
</vector>
|
|
||||||
|
Before Width: | Height: | Size: 3.7 KiB |
@@ -1,3 +0,0 @@
|
|||||||
<vector android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2019 tom5079 ~ ~ This program is free software: you can redistribute it and/or modify ~ it under the terms of the GNU General Public License as published by ~ the Free Software Foundation, either version 3 of the License, or ~ (at your option) any later version. ~ ~ This program is distributed in the hope that it will be useful, ~ but WITHOUT ANY WARRANTY; without even the implied warranty of ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ~ GNU General Public License for more details. ~ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see <http://www.gnu.org/licenses/>.-->
|
|
||||||
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:aapt="http://schemas.android.com/aapt" tools:ignore="NewApi">
|
|
||||||
<aapt:attr name="android:drawable">
|
|
||||||
<vector android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:name="path" android:pathData="M 19 9 L 15 9 L 15 3 L 9 3 L 9 9 L 5 9 L 12 16 L 19 9 Z" android:fillColor="#fff" android:strokeWidth="1"/>
|
|
||||||
<path android:name="path_2" android:pathData="M 5 19 L 19 19" android:fillColor="#fff" android:strokeColor="#fff" android:strokeWidth="2"/>
|
|
||||||
</vector>
|
|
||||||
</aapt:attr>
|
|
||||||
<target android:name="path_2">
|
|
||||||
<aapt:attr name="android:animation">
|
|
||||||
<set>
|
|
||||||
<objectAnimator android:propertyName="trimPathEnd" android:duration="500" android:valueFrom="0" android:valueTo="0.8" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
<objectAnimator android:propertyName="trimPathStart" android:startOffset="500" android:duration="500" android:valueFrom="0" android:valueTo="0.8" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
<objectAnimator android:propertyName="trimPathOffset" android:duration="1000" android:valueFrom="0" android:valueTo="0.2" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
|
|
||||||
</set>
|
|
||||||
</aapt:attr>
|
|
||||||
</target>
|
|
||||||
</animated-vector>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<vector android:height="24dp" android:width="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@color/material_orange_500" android:pathData="M12 15.39l-3.76 2.27 0.99-4.28-3.32-2.88 4.38-0.37L12 6.09l1.71 4.04 4.38 0.37-3.32 2.88 0.99 4.28M22 9.24l-7.19-0.61L12 2 9.19 8.63 2 9.24l5.45 4.73L5.82 21 12 17.27 18.18 21l-1.64-7.03L22 9.24z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<vector android:height="24dp" android:width="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@color/material_orange_500" android:pathData="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-0.62L12 2 9.19 8.62 2 9.24l5.45 4.73L5.82 21 12 17.27z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/image_broken_variant.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#000" android:pathData="M21 5v6.59l-3-3.01-4 4.01-4-4-4 4-3-3.01V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2m-3 6.42l3 3.01V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6.58l3 2.99 4-4 4 4"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/lastpass.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M14 12a2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2m-6 0a2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2m-6 0a2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2m20-7h-2v14h2V5z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/lock_pattern.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M7 3a4 4 0 0 1 4 4c0 1.86-1.27 3.43-3 3.87v2.26c0.37 0.09 0.72 0.24 1.04 0.43l4.52-4.52C13.2 8.44 13 7.75 13 7a4 4 0 0 1 4-4 4 4 0 0 1 4 4 4 4 0 0 1-4 4c-0.74 0-1.43-0.2-2-0.55L10.45 15C10.8 15.57 11 16.26 11 17a4 4 0 0 1-4 4 4 4 0 0 1-4-4c0-1.86 1.27-3.43 3-3.87v-2.26C4.27 10.43 3 8.86 3 7a4 4 0 0 1 4-4m10 10a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4m0 2a2 2 0 0 0-2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0-2-2z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
|
|
||||||
<group android:pivotX="12" android:scaleX="-1">
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
|
|
||||||
</group>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/numeric.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M4 17V9H2V7h4v10H4m18-2c0 1.11-0.9 2-2 2h-4v-2h4v-2h-2v-2h2V9h-4V7h4a2 2 0 0 1 2 2v1.5a1.5 1.5 0 0 1-1.5 1.5 1.5 1.5 0 0 1 1.5 1.5V15m-8 0v2H8v-4c0-1.11 0.9-2 2-2h2V9H8V7h4a2 2 0 0 1 2 2v2c0 1.11-0.9 2-2 2h-2v2h4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?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/>.-->
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
|
||||||
<solid android:color="@color/colorAccent"/>
|
|
||||||
</shape>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?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/>.-->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<stroke android:width="1dp" android:color="#555555"/>
|
|
||||||
<solid android:color="@android:color/transparent"/>
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</layer-list>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/refresh.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="#fff" android:pathData="M17.65 6.35C16.2 4.9 14.21 4 12 4a8 8 0 0 0-8 8 8 8 0 0 0 8 8c3.73 0 6.84-2.55 7.73-6h-2.08c-0.82 2.33-3.04 4-5.65 4a6 6 0 0 1-6-6 6 6 0 0 1 6-6c1.66 0 3.14 0.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/swap_horizontal.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M21 9l-4-4v3h-7v2h7v3M7 11l-4 4 4 4v-3h7v-2H7v-3z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/tag.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M5.5 7A1.5 1.5 0 0 1 4 5.5 1.5 1.5 0 0 1 5.5 4 1.5 1.5 0 0 1 7 5.5 1.5 1.5 0 0 1 5.5 7m15.91 4.58l-9-9C12.05 2.22 11.55 2 11 2H4C2.89 2 2 2.89 2 4v7c0 0.55 0.22 1.05 0.59 1.41l8.99 9C11.95 21.77 12.45 22 13 22c0.55 0 1.05-0.23 1.41-0.59l7-7C21.78 14.05 22 13.55 22 13c0-0.56-0.23-1.06-0.59-1.42z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<!--~ 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/>.-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="72dp" android:viewportWidth="64" android:viewportHeight="192">
|
|
||||||
<path android:pathData="M36 0h1l1 1h3l1 1h1l1 1h2l1 1h1l1 1h1l1 1 1 1 1 1 1 1h1v1l1 1 1 1 1 1v1l1 1 1 1v1l1 1v2l1 1v1l1 1v3l1 1v138l-1 1v3l-1 1v1l-1 1v2l-1 1v1l-1 1v1l-1 1-1 1-1 1-1 1v1h-1l-1 1-1 1-1 1-1 1h-1l-1 1h-1l-1 1h-2l-1 1h-1l-1 1h-3l-1 1H27l-1-1h-3l-1-1h-1l-1-1h-2l-1-1h-1l-1-1h-1l-1-1-1-1-1-1-1-1H9v-1l-1-1-1-1-1-1v-1l-1-1-1-1v-1l-1-1v-2l-1-1v-1l-1-1v-3l-1-1V27l1-1v-3l1-1v-1l1-1v-2l1-1v-1l1-1v-1l1-1 1-1 1-1 1-1V9h1l1-1 1-1 1-1 1-1h1l1-1h1l1-1h2l1-1h1l1-1h3l1-1h9z" android:fillColor="#4fc3f7"/>
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M42 80L32 70 22 80m20 32l-10 10-10-10" android:strokeWidth="2" android:fillAlpha="0" android:strokeColor="#000"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<!--~ 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/>.-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="72dp" android:height="24dp" android:viewportWidth="192" android:viewportHeight="64">
|
|
||||||
<path android:pathData="M29 64h-2l-1-1h-3l-1-1h-1l-1-1h-2l-1-1h-1l-1-1h-1l-1-1-1-1-1-1-1-1H9v-1l-1-1-1-1-1-1-1-1v-1l-1-1v-1l-1-1v-2l-1-1v-1l-1-1v-3l-1-1V27l1-1v-3l1-1v-1l1-1v-2l1-1v-1l1-1v-1l1-1 1-1 1-1 1-1V9h1l1-1 1-1 1-1 1-1h1l1-1h1l1-1h2l1-1h1l1-1h3l1-1h138l1 1h3l1 1h1l1 1h2l1 1h1l1 1h1l1 1 1 1 1 1 1 1h1v1l1 1 1 1 1 1 1 1v1l1 1v1l1 1v2l1 1v1l1 1v3l1 1v10l-1 1v3l-1 1v1l-1 1v2l-1 1v1l-1 1v1l-1 1-1 1-1 1-1 1v1h-1l-1 1-1 1-1 1-1 1h-1l-1 1h-1l-1 1h-2l-1 1h-1l-1 1h-3l-1 1H29z" android:fillColor="#4fc3f7"/>
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M80 42L70 32l10-10m32 20l10-10-10-10" android:strokeWidth="2" android:fillAlpha="0" android:strokeColor="#000"/>
|
|
||||||
</vector>
|
|
||||||
|
Before Width: | Height: | Size: 80 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<!--drawable/translate.xml-->
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
|
||||||
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12.87 15.07l-2.54-2.51 0.03-0.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c0.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11 0.76-2.04M18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12m-2.62 7l1.62-4.33L19.12 17h-3.24z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
|
||||||
~ Copyright (C) 2019 tom5079
|
|
||||||
~
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
tools:ignore="Autofill"
|
|
||||||
android:inputType="text"
|
|
||||||
android:hint="@string/settings_default_query"
|
|
||||||
android:id="@+id/edittext"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/language_layout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/edittext"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/default_query_dialog_language"/>
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/language_selector"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/BL_layout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingLeft="0dp"
|
|
||||||
android:paddingStart="0dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/language_layout"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/default_query_dialog_filter_BL"/>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/BL_checkbox"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/guro_layout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingLeft="0dp"
|
|
||||||
android:paddingStart="0dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/BL_layout"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/default_query_dialog_filter_guro"/>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/guro_checkbox"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingLeft="0dp"
|
|
||||||
android:paddingStart="0dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:paddingRight="8dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/guro_layout"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/default_query_dialog_filter_loli"/>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/loli_checkbox"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="?android:textAppearanceLarge"
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/settings_download_folder_name"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/message"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/title"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"/>
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/edittext"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/message"
|
|
||||||
android:layout_margin="8dp"/>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/ok_button"
|
|
||||||
style="?borderlessButtonStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@android:string/ok"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/edittext"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp"/>
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="horizontal" android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true">
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/button"
|
|
||||||
android:clickable="false"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_gravity="center_vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/location_type"
|
|
||||||
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/location_available"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
|
||||||
~ Copyright (C) 2019 tom5079
|
|
||||||
~
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".ui.LockActivity">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/lock_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/lock_fingerprint_layout"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/lock_fingerprint_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:gravity="center"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/lock_content"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/lock_button_layout">
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/fingerprint_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:srcCompat="@drawable/fingerprint"
|
|
||||||
app:backgroundTint="@color/lock_fab"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
app:tint="@null"
|
|
||||||
app:fabSize="mini"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/lock_button_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/lock_fingerprint_layout"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/pattern_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:tint="@null"
|
|
||||||
app:srcCompat="@drawable/lock_pattern"
|
|
||||||
app:backgroundTint="@color/lock_fab"
|
|
||||||
app:fabSize="mini"/>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/pin_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:tint="@null"
|
|
||||||
app:srcCompat="@drawable/numeric"
|
|
||||||
app:backgroundTint="@color/lock_fab"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
app:fabSize="mini"/>
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/password_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:tint="@null"
|
|
||||||
app:srcCompat="@drawable/lastpass"
|
|
||||||
app:backgroundTint="@color/lock_fab"
|
|
||||||
app:fabSize="mini"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
|
||||||
~ Copyright (C) 2019 tom5079
|
|
||||||
~
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="?android:textAppearanceLarge"
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/reader_go_to_page"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"/>
|
|
||||||
|
|
||||||
<NumberPicker
|
|
||||||
android:id="@+id/number_picker"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/title"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/ok_button"
|
|
||||||
style="?borderlessButtonStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@android:string/ok"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/number_picker"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
|
||||||
~ Copyright (C) 2019 tom5079
|
|
||||||
~
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:context=".ui.fragment.PatternLockFragment">
|
|
||||||
|
|
||||||
<com.andrognito.patternlockview.PatternLockView
|
|
||||||
android:id="@+id/pattern_lock_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
app:aspectRatioEnabled="true"
|
|
||||||
app:normalStateColor="@color/colorPrimary"
|
|
||||||
app:correctStateColor="@color/colorPrimaryDark"
|
|
||||||
app:wrongStateColor="@color/colorAccent"/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:paddingTop="100dp">
|
|
||||||
|
|
||||||
<com.andrognito.pinlockview.IndicatorDots
|
|
||||||
android:id="@+id/indicator_dots"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal|top"
|
|
||||||
app:dotFilledBackground="@drawable/pin_filled"/>
|
|
||||||
|
|
||||||
<com.andrognito.pinlockview.PinLockView
|
|
||||||
android:id="@+id/pin_lock_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:keypadTextColor="?android:attr/textColorPrimary"
|
|
||||||
android:layout_gravity="center_horizontal|bottom"
|
|
||||||
app:keypadDeleteButtonDrawable="@drawable/backspace_outline"
|
|
||||||
app:keypadShowDeleteButton="true"/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
<?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/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
style="@style/TextAppearance.AppCompat.Large"
|
|
||||||
android:text="@string/settings_proxy_title"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/type_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/title"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
android:text="@string/proxy_dialog_type"
|
|
||||||
android:textAppearance="?android:attr/listSeparatorTextViewStyle"/>
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/type_selector"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/type_text"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/server_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/type_selector"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
android:text="@string/proxy_dialog_server"
|
|
||||||
android:textAppearance="?android:attr/listSeparatorTextViewStyle"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/address_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/server_text">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatEditText
|
|
||||||
android:id="@+id/addr"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_weight="2"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/proxy_dialog_addr_hint"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatEditText
|
|
||||||
android:id="@+id/port"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/proxy_dialog_port_hint"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatEditText
|
|
||||||
android:id="@+id/username"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/address_layout"
|
|
||||||
android:hint="@string/proxy_dialog_username_hint"
|
|
||||||
android:enabled="false"/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatEditText
|
|
||||||
android:id="@+id/password"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/username"
|
|
||||||
android:hint="@string/proxy_dialog_password_hint"
|
|
||||||
android:enabled="false"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/cancel_button"
|
|
||||||
style="?borderlessButtonStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@android:string/cancel"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/password"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/ok_button"
|
|
||||||
app:layout_constraintRight_toLeftOf="@id/ok_button"/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/ok_button"
|
|
||||||
style="?borderlessButtonStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@android:string/ok"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/password"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
|
||||||
~ Copyright (C) 2019 tom5079
|
|
||||||
~
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/settings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"/>
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Pupil, Hitomi.la viewer for Android
|
|
||||||
~ Copyright (C) 2019 tom5079
|
|
||||||
~
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<item android:id="@+id/reader_menu_favorite"
|
|
||||||
android:title=""
|
|
||||||
android:icon="@drawable/avd_star"
|
|
||||||
app:showAsAction="always"/>
|
|
||||||
|
|
||||||
<item android:id="@+id/reader_type"
|
|
||||||
android:title=""
|
|
||||||
app:showAsAction="ifRoom"/>
|
|
||||||
|
|
||||||
<item android:id="@+id/reader_menu_page_indicator"
|
|
||||||
android:title="@string/page_indicator_placeholder"
|
|
||||||
app:showAsAction="always|withText"/>
|
|
||||||
|
|
||||||
</menu>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<string-array name="settings_galleries_per_page">
|
|
||||||
<item>5</item>
|
|
||||||
<item>10</item>
|
|
||||||
<item>25</item>
|
|
||||||
<item>50</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<string-array name="languages">
|
|
||||||
<item>indonesian|Bahasa Indonesia</item>
|
|
||||||
<item>catalan|català</item>
|
|
||||||
<item>cebuano|Cebuano</item>
|
|
||||||
<item>czech|Čeština</item>
|
|
||||||
<item>danish|Dansk</item>
|
|
||||||
<item>german|Deutsch</item>
|
|
||||||
<item>estonian|eesti</item>
|
|
||||||
<item>english|English</item>
|
|
||||||
<item>spanish|Español</item>
|
|
||||||
<item>esperanto|Esperanto</item>
|
|
||||||
<item>french|Français</item>
|
|
||||||
<item>italian|Italiano</item>
|
|
||||||
<item>latin|Latina</item>
|
|
||||||
<item>hungarian|magyar</item>
|
|
||||||
<item>dutch|Nederlands</item>
|
|
||||||
<item>norwegian|norsk</item>
|
|
||||||
<item>polish|polski</item>
|
|
||||||
<item>portuguese|Português</item>
|
|
||||||
<item>romanian|română</item>
|
|
||||||
<item>albanian|shqip</item>
|
|
||||||
<item>slovak|Slovenčina</item>
|
|
||||||
<item>finnish|Suomi</item>
|
|
||||||
<item>swedish|Svenska</item>
|
|
||||||
<item>tagalog|Tagalog</item>
|
|
||||||
<item>vietnamese|tiếng việt</item>
|
|
||||||
<item>turkish|Türkçe</item>
|
|
||||||
<item>greek|Ελληνικά</item>
|
|
||||||
<item>mongolian|Монгол</item>
|
|
||||||
<item>russian|Русский</item>
|
|
||||||
<item>ukrainian|Українська</item>
|
|
||||||
<item>hebrew|עברית</item>
|
|
||||||
<item>arabic|العربية</item>
|
|
||||||
<item>persian|فارسی</item>
|
|
||||||
<item>thai|ไทย</item>
|
|
||||||
<item>korean|한국어</item>
|
|
||||||
<item>chinese|中文</item>
|
|
||||||
<item>japanese|日本語</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<string-array name="proxy_type">
|
|
||||||
<item>Direct</item>
|
|
||||||
<item>HTTP</item>
|
|
||||||
<item>SOCKS</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<string-array name="cache_size">
|
|
||||||
<item>0</item>
|
|
||||||
<item>1</item>
|
|
||||||
<item>2</item>
|
|
||||||
<item>4</item>
|
|
||||||
<item>8</item>
|
|
||||||
<item>16</item>
|
|
||||||
<item>32</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<string-array name="cache_size_text">
|
|
||||||
<item>@string/unlimited</item>
|
|
||||||
<item>1G</item>
|
|
||||||
<item>2G</item>
|
|
||||||
<item>4G</item>
|
|
||||||
<item>8G</item>
|
|
||||||
<item>16G</item>
|
|
||||||
<item>32G</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<string-array name="concurrent_download">
|
|
||||||
<item>0</item>
|
|
||||||
<item>1</item>
|
|
||||||
<item>2</item>
|
|
||||||
<item>4</item>
|
|
||||||
<item>8</item>
|
|
||||||
<item>16</item>
|
|
||||||
<item>32</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<string-array name="concurrent_download_text">
|
|
||||||
<item>@string/unlimited</item>
|
|
||||||
<item>1</item>
|
|
||||||
<item>2</item>
|
|
||||||
<item>4</item>
|
|
||||||
<item>8</item>
|
|
||||||
<item>16</item>
|
|
||||||
<item>32</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
</resources>
|
|
||||||