2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8 (2)" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/classes" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -7,13 +7,13 @@ apply plugin: 'io.fabric'
|
||||
apply plugin: 'com.google.firebase.firebase-perf'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
applicationId "xyz.quaver.pupil"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
versionCode 16
|
||||
versionName "2.9"
|
||||
targetSdkVersion 29
|
||||
versionCode 17
|
||||
versionName "2.10-alpha"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
}
|
||||
@@ -42,17 +42,21 @@ dependencies {
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.preference:preference:1.1.0-beta01'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'com.google.firebase:firebase-core:16.0.9'
|
||||
implementation 'com.google.firebase:firebase-perf:17.0.2'
|
||||
implementation 'com.google.firebase:firebase-core:17.0.0'
|
||||
implementation 'com.google.firebase:firebase-perf:18.0.1'
|
||||
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
|
||||
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
||||
implementation 'com.github.deano2390:MaterialShowcaseView:1.3.4'
|
||||
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation "ru.noties.markwon:core:${markwonVersion}"
|
||||
implementation 'com.github.clans:fab:1.6.4'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test:rules:1.2.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation project(path: ':libpupil')
|
||||
|
||||
9
app/proguard-rules.pro
vendored
9
app/proguard-rules.pro
vendored
@@ -18,11 +18,4 @@
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
-keep class com.finotes.android.finotescore.* { *; }
|
||||
|
||||
-keepclassmembers class * {
|
||||
@com.finotes.android.finotescore.annotation.Observe *;
|
||||
}
|
||||
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1 +1 @@
|
||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":16,"versionName":"2.9","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":17,"versionName":"2.9.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
||||
@@ -1,16 +1,18 @@
|
||||
package xyz.quaver.pupil
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.getReader
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import java.io.File
|
||||
import xyz.quaver.pupil.ui.LockActivity
|
||||
import java.net.URL
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
||||
@@ -21,6 +23,7 @@ import javax.net.ssl.HttpsURLConnection
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
@@ -30,12 +33,12 @@ class ExampleInstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun checkCacheDir() {
|
||||
val activityTestRule = ActivityTestRule<LockActivity>(LockActivity::class.java)
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val file = File(appContext.cacheDir, "imageCache/1412251/01.jpg.webp")
|
||||
|
||||
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
|
||||
activityTestRule.launchActivity(Intent())
|
||||
|
||||
Log.d("Pupil", bitmap.byteCount.toString())
|
||||
while(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -6,20 +6,19 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".ui.Pupil"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:name=".Pupil">
|
||||
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".ui.LockActivity"/>
|
||||
<activity
|
||||
android:name=".ReaderActivity"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize">
|
||||
|
||||
android:name=".ui.ReaderActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:parentActivityName=".ui.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
@@ -27,9 +26,9 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="hitomi.la"
|
||||
android:pathPrefix="/galleries" />
|
||||
android:pathPrefix="/galleries"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -38,9 +37,9 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="히요비.asia"
|
||||
android:pathPrefix="/reader" />
|
||||
android:pathPrefix="/reader"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -49,9 +48,9 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="xn--9w3b15m8vo.asia"
|
||||
android:pathPrefix="/reader" />
|
||||
android:pathPrefix="/reader"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -60,9 +59,9 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="e-hentai.org"
|
||||
android:pathPrefix="/g" />
|
||||
android:pathPrefix="/g"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -71,9 +70,9 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="http"
|
||||
android:host="hitomi.la"
|
||||
android:pathPrefix="/galleries" />
|
||||
android:pathPrefix="/galleries"
|
||||
android:scheme="http" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -82,9 +81,9 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="http"
|
||||
android:host="히요비.asia"
|
||||
android:pathPrefix="/reader" />
|
||||
android:pathPrefix="/reader"
|
||||
android:scheme="http" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -93,9 +92,9 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="http"
|
||||
android:host="xn--9w3b15m8vo.asia"
|
||||
android:pathPrefix="/reader" />
|
||||
android:pathPrefix="/reader"
|
||||
android:scheme="http" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -104,16 +103,16 @@
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="http"
|
||||
android:host="e-hentai.org"
|
||||
android:pathPrefix="/g" />
|
||||
android:pathPrefix="/g"
|
||||
android:scheme="http" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:name=".ui.SettingsActivity"
|
||||
android:label="@string/settings_title" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".ui.MainActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:theme="@style/NoActionBarAppTheme">
|
||||
<intent-filter>
|
||||
|
||||
@@ -24,7 +24,7 @@ import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.list
|
||||
import xyz.quaver.hitomi.GalleryBlock
|
||||
import xyz.quaver.hitomi.ReaderItem
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.ui.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.types.Tag
|
||||
import xyz.quaver.pupil.util.Histories
|
||||
|
||||
@@ -6,9 +6,6 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.quaver.pupil.R
|
||||
|
||||
class ReaderAdapter(private val images: List<String>) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
|
||||
|
||||
85
app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt
Normal file
85
app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt
Normal file
@@ -0,0 +1,85 @@
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.andrognito.patternlockview.PatternLockView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.activity_lock.*
|
||||
import kotlinx.android.synthetic.main.fragment_pattern_lock.*
|
||||
import kotlinx.android.synthetic.main.settings_activity.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.util.Lock
|
||||
import xyz.quaver.pupil.util.LockManager
|
||||
|
||||
class LockActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_lock)
|
||||
|
||||
val lockManager = LockManager(this)
|
||||
|
||||
val mode = intent.getStringExtra("mode")
|
||||
|
||||
lock_pattern.isEnabled = false
|
||||
lock_pin.isEnabled = false
|
||||
lock_fingerprint.isEnabled = false
|
||||
lock_password.isEnabled = false
|
||||
|
||||
when(mode) {
|
||||
null -> {
|
||||
if (lockManager.empty()) {
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
"add_lock" -> {
|
||||
when(intent.getStringExtra("type")!!) {
|
||||
"pattern" -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
supportFragmentManager.beginTransaction().add(
|
||||
R.id.lock_content,
|
||||
PatternLockFragment().apply {
|
||||
var lastPass = ""
|
||||
onPatternDrawn = {
|
||||
when(mode) {
|
||||
null -> {
|
||||
val result = lockManager.check(it)
|
||||
|
||||
if (result == true) {
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
} else
|
||||
lock_pattern_view.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 {
|
||||
lock_pattern_view.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
||||
lastPass = ""
|
||||
|
||||
Snackbar.make(view!!, R.string.settings_lock_wrong_confirm, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
).commit()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package xyz.quaver.pupil
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.text.*
|
||||
import android.text.style.AlignmentSpan
|
||||
import android.view.*
|
||||
@@ -21,6 +21,7 @@ import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.arlib.floatingsearchview.FloatingSearchView
|
||||
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
@@ -40,6 +41,8 @@ import kotlinx.serialization.list
|
||||
import kotlinx.serialization.stringify
|
||||
import ru.noties.markwon.Markwon
|
||||
import xyz.quaver.hitomi.*
|
||||
import xyz.quaver.pupil.BuildConfig
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||
import xyz.quaver.pupil.types.Tag
|
||||
import xyz.quaver.pupil.types.TagSuggestion
|
||||
@@ -51,6 +54,7 @@ import java.net.URL
|
||||
import java.util.*
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
@@ -65,9 +69,16 @@ class MainActivity : AppCompatActivity() {
|
||||
private val galleries = ArrayList<Pair<GalleryBlock, Deferred<String>>>()
|
||||
|
||||
private var query = ""
|
||||
set(value) {
|
||||
field = value
|
||||
findViewById<SearchInputView>(R.id.search_bar_text)
|
||||
.setText(query, TextView.BufferType.EDITABLE)
|
||||
}
|
||||
|
||||
private var mode = Mode.SEARCH
|
||||
|
||||
private val SETTINGS = 45162
|
||||
private val REQUEST_SETTINGS = 45162
|
||||
private val REQUEST_LOCK = 561
|
||||
|
||||
private var galleryIDs: Deferred<List<Int>>? = null
|
||||
private var totalItems = 0
|
||||
@@ -81,6 +92,8 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
startActivityForResult(Intent(this, LockActivity::class.java), REQUEST_LOCK)
|
||||
|
||||
checkPermissions()
|
||||
|
||||
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
@@ -110,19 +123,11 @@ class MainActivity : AppCompatActivity() {
|
||||
initView()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
if (cacheDir.exists())
|
||||
cacheDir.deleteRecursively()
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
when {
|
||||
main_drawer_layout.isDrawerOpen(GravityCompat.START) -> main_drawer_layout.closeDrawer(GravityCompat.START)
|
||||
query.isNotEmpty() -> runOnUiThread {
|
||||
query = ""
|
||||
findViewById<SearchInputView>(R.id.search_bar_text).setText(query, TextView.BufferType.EDITABLE)
|
||||
|
||||
cancelFetch()
|
||||
clearGalleries()
|
||||
@@ -142,6 +147,7 @@ class MainActivity : AppCompatActivity() {
|
||||
WindowManager.LayoutParams.FLAG_SECURE)
|
||||
else
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
@@ -186,7 +192,7 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when(requestCode) {
|
||||
SETTINGS -> {
|
||||
REQUEST_SETTINGS -> {
|
||||
runOnUiThread {
|
||||
cancelFetch()
|
||||
clearGalleries()
|
||||
@@ -194,6 +200,10 @@ class MainActivity : AppCompatActivity() {
|
||||
loadBlocks()
|
||||
}
|
||||
}
|
||||
REQUEST_LOCK -> {
|
||||
if (resultCode != Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,8 +362,7 @@ class MainActivity : AppCompatActivity() {
|
||||
onChipClickedHandler.add {
|
||||
runOnUiThread {
|
||||
query = it.toQuery()
|
||||
this@MainActivity.findViewById<SearchInputView>(R.id.search_bar_text)
|
||||
.setText(query, TextView.BufferType.EDITABLE)
|
||||
currentPage = 0
|
||||
|
||||
cancelFetch()
|
||||
clearGalleries()
|
||||
@@ -679,7 +688,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
setOnMenuItemClickListener {
|
||||
when(it.itemId) {
|
||||
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), SETTINGS)
|
||||
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), REQUEST_SETTINGS)
|
||||
R.id.main_menu_jump -> {
|
||||
val preference = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val perPage = preference.getString("per_page", "25")!!.toInt()
|
||||
@@ -726,7 +735,8 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Snackbar.make(main_layout, R.string.main_open_gallery_by_id_error, Snackbar.LENGTH_LONG).show()
|
||||
Snackbar.make(main_layout,
|
||||
R.string.main_open_gallery_by_id_error, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -804,7 +814,9 @@ class MainActivity : AppCompatActivity() {
|
||||
favorites.remove(tag)
|
||||
}
|
||||
else {
|
||||
setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star))
|
||||
setImageDrawable(AnimatedVectorDrawableCompat.create(context,
|
||||
R.drawable.avd_star
|
||||
))
|
||||
(drawable as Animatable).start()
|
||||
|
||||
favorites.add(tag)
|
||||
@@ -880,10 +892,8 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun cancelFetch() {
|
||||
runBlocking {
|
||||
galleryIDs?.cancelAndJoin()
|
||||
loadingJob?.cancelAndJoin()
|
||||
}
|
||||
galleryIDs?.cancel()
|
||||
loadingJob?.cancel()
|
||||
}
|
||||
|
||||
private fun clearGalleries() {
|
||||
@@ -992,7 +1002,7 @@ class MainActivity : AppCompatActivity() {
|
||||
query.isEmpty() and defaultQuery.isEmpty() and (mode == Mode.SEARCH) ->
|
||||
galleryIDs
|
||||
else ->
|
||||
galleryIDs.slice(currentPage*perPage until Math.min(currentPage*perPage+perPage, galleryIDs.size))
|
||||
galleryIDs.slice(currentPage*perPage until min(currentPage*perPage+perPage, galleryIDs.size))
|
||||
}.chunked(5).let { chunks ->
|
||||
for (chunk in chunks)
|
||||
chunk.map { galleryID ->
|
||||
@@ -1009,8 +1019,8 @@ class MainActivity : AppCompatActivity() {
|
||||
getGalleryBlock(galleryID).apply {
|
||||
this ?: return@apply
|
||||
|
||||
if (!cache.parentFile.exists())
|
||||
cache.parentFile.mkdirs()
|
||||
if (cache.parentFile?.exists() == false)
|
||||
cache.parentFile!!.mkdirs()
|
||||
|
||||
cache.writeText(json.stringify(serializer, this))
|
||||
}
|
||||
@@ -1024,8 +1034,8 @@ class MainActivity : AppCompatActivity() {
|
||||
if (!exists())
|
||||
try {
|
||||
with(URL(galleryBlock.thumbnails[0]).openConnection() as HttpsURLConnection) {
|
||||
if (!this@apply.parentFile.exists())
|
||||
this@apply.parentFile.mkdirs()
|
||||
if (this@apply.parentFile?.exists() == false)
|
||||
this@apply.parentFile!!.mkdirs()
|
||||
|
||||
inputStream.copyTo(FileOutputStream(this@apply))
|
||||
}
|
||||
47
app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt
Normal file
47
app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
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 kotlinx.android.synthetic.main.fragment_pattern_lock.*
|
||||
import kotlinx.android.synthetic.main.fragment_pattern_lock.view.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.util.hash
|
||||
import xyz.quaver.pupil.util.hashWithSalt
|
||||
|
||||
class PatternLockFragment : Fragment(), PatternLockViewListener {
|
||||
|
||||
var onPatternDrawn: ((String) -> Unit)? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_pattern_lock, container, false).apply {
|
||||
lock_pattern_view.addPatternLockListener(this@PatternLockFragment)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
|
||||
}
|
||||
|
||||
override fun onComplete(pattern: MutableList<PatternLockView.Dot>?) {
|
||||
val password = PatternLockUtils.patternToMD5(lock_pattern_view, pattern)
|
||||
onPatternDrawn?.invoke(password)
|
||||
}
|
||||
|
||||
override fun onProgress(progressPattern: MutableList<PatternLockView.Dot>?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onStarted() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package xyz.quaver.pupil
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
import android.app.Application
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.preference.PreferenceManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import androidx.preference.PreferenceManager
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.util.Histories
|
||||
import java.io.File
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package xyz.quaver.pupil
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Animatable
|
||||
@@ -11,9 +11,9 @@ import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.activity_reader.*
|
||||
import kotlinx.android.synthetic.main.activity_reader.view.*
|
||||
@@ -28,6 +28,7 @@ import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import xyz.quaver.hitomi.GalleryBlock
|
||||
import xyz.quaver.hitomi.getGalleryBlock
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.adapters.ReaderAdapter
|
||||
import xyz.quaver.pupil.util.GalleryDownloader
|
||||
import xyz.quaver.pupil.util.Histories
|
||||
@@ -72,6 +73,8 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
Crashlytics.setInt("GalleryID", galleryBlock.id)
|
||||
|
||||
if (!::galleryBlock.isInitialized) {
|
||||
onBackPressed()
|
||||
return
|
||||
@@ -117,7 +120,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
} else {
|
||||
galleryBlock = Json(JsonConfiguration.Stable).parse(
|
||||
GalleryBlock.serializer(),
|
||||
intent.getStringExtra("galleryblock")
|
||||
intent.getStringExtra("galleryblock")!!
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -372,10 +375,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
reader_recyclerview.layoutManager = LinearLayoutManager(this)
|
||||
} else {
|
||||
snapHelper.attachToRecyclerView(reader_recyclerview)
|
||||
reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false).apply {
|
||||
isItemPrefetchEnabled = true
|
||||
initialPrefetchItemCount = 4
|
||||
}
|
||||
reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0)
|
||||
@@ -1,8 +1,8 @@
|
||||
package xyz.quaver.pupil
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.preference.PreferenceManager
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
@@ -15,12 +15,18 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlinx.android.synthetic.main.dialog_default_query.view.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.types.Tags
|
||||
import xyz.quaver.pupil.util.Lock
|
||||
import xyz.quaver.pupil.util.LockManager
|
||||
import java.io.File
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
val REQUEST_LOCK = 38238
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -58,7 +64,25 @@ class SettingsActivity : AppCompatActivity() {
|
||||
"TB" //really?
|
||||
)
|
||||
|
||||
private fun getCacheSize(dir: File) : String {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
val lockManager = LockManager(context!!)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDirSize(dir: File) : String {
|
||||
var size = dir.walk().map { it.length() }.sum()
|
||||
var suffixIndex = 0
|
||||
|
||||
@@ -67,20 +91,53 @@ class SettingsActivity : AppCompatActivity() {
|
||||
suffixIndex++
|
||||
}
|
||||
|
||||
return getString(R.string.settings_clear_downloads_summary, size, suffix[suffixIndex])
|
||||
return getString(R.string.settings_clear_summary, size, suffix[suffixIndex])
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||
|
||||
with(findPreference<Preference>("app_version")) {
|
||||
this!!
|
||||
|
||||
val manager = context.packageManager
|
||||
val info = manager.getPackageInfo(context.packageName, 0)
|
||||
|
||||
summary = info.versionName
|
||||
}
|
||||
|
||||
with(findPreference<Preference>("delete_cache")) {
|
||||
this!!
|
||||
|
||||
val dir = File(context.cacheDir, "imageCache")
|
||||
|
||||
summary = getDirSize(dir)
|
||||
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.settings_clear_cache_alert_message)
|
||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
if (dir.exists())
|
||||
dir.deleteRecursively()
|
||||
|
||||
summary = getDirSize(dir)
|
||||
}
|
||||
setNegativeButton(android.R.string.no) { _, _ -> }
|
||||
}.show()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
with(findPreference<Preference>("delete_downloads")) {
|
||||
this ?: return@with
|
||||
this!!
|
||||
|
||||
val dir = File(Environment.getExternalStorageDirectory(), "Pupil")
|
||||
val dir = context.getExternalFilesDir("Pupil") ?: return@with
|
||||
|
||||
summary = getCacheSize(dir)
|
||||
summary = getDirSize(dir)
|
||||
|
||||
setOnPreferenceClickListener {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.settings_clear_downloads_alert_message)
|
||||
@@ -92,7 +149,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
downloads.clear()
|
||||
|
||||
summary = getCacheSize(dir)
|
||||
summary = getDirSize(dir)
|
||||
}
|
||||
setNegativeButton(android.R.string.no) { _, _ -> }
|
||||
}.show()
|
||||
@@ -101,13 +158,13 @@ class SettingsActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
with(findPreference<Preference>("clear_history")) {
|
||||
this ?: return@with
|
||||
this!!
|
||||
|
||||
val histories = (activity!!.application as Pupil).histories
|
||||
|
||||
summary = getString(R.string.settings_clear_history_summary, histories.size)
|
||||
|
||||
setOnPreferenceClickListener {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.settings_clear_history_alert_message)
|
||||
@@ -123,7 +180,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
with(findPreference<Preference>("default_query")) {
|
||||
this ?: return@with
|
||||
this!!
|
||||
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@@ -139,7 +196,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
val excludeBL = "-male:yaoi"
|
||||
val excludeGuro = listOf("-female:guro", "-male:guro")
|
||||
|
||||
setOnPreferenceClickListener {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
val dialogView = LayoutInflater.from(context).inflate(
|
||||
R.layout.dialog_default_query,
|
||||
LinearLayout(context),
|
||||
@@ -154,7 +211,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
with(dialogView.default_query_dialog_language_selector) {
|
||||
adapter =
|
||||
ArrayAdapter<String>(
|
||||
ArrayAdapter(
|
||||
context,
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
arrayListOf(
|
||||
@@ -234,6 +291,82 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
dialog.show()
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
with(findPreference<Preference>("app_lock")) {
|
||||
this!!
|
||||
|
||||
val lockManager = LockManager(context)
|
||||
|
||||
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 = Preference.OnPreferenceClickListener {
|
||||
val intent = Intent(context, LockActivity::class.java)
|
||||
activity?.startActivityForResult(intent, (activity as SettingsActivity).REQUEST_LOCK)
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LockFragment : PreferenceFragmentCompat() {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
val lockManager = LockManager(context!!)
|
||||
|
||||
findPreference<Preference>("lock_pattern")?.summary =
|
||||
if (lockManager.contains(Lock.Type.PATTERN))
|
||||
getString(R.string.settings_lock_enabled)
|
||||
else
|
||||
""
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.lock_preferences, rootKey)
|
||||
|
||||
with(findPreference<Preference>("lock_pattern")) {
|
||||
this!!
|
||||
|
||||
if (LockManager(context!!).contains(Lock.Type.PATTERN))
|
||||
summary = getString(R.string.settings_lock_enabled)
|
||||
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
val lockManager = LockManager(context!!)
|
||||
|
||||
if (lockManager.contains(Lock.Type.PATTERN)) {
|
||||
AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.warning)
|
||||
setMessage(R.string.settings_lock_remove_message)
|
||||
|
||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
lockManager.remove(Lock.Type.PATTERN)
|
||||
onResume()
|
||||
}
|
||||
setNegativeButton(android.R.string.no) { _, _ -> }
|
||||
}.show()
|
||||
} else {
|
||||
val intent = Intent(context, LockActivity::class.java).apply {
|
||||
putExtra("mode", "add_lock")
|
||||
putExtra("type", "pattern")
|
||||
}
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -247,4 +380,19 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when(requestCode) {
|
||||
REQUEST_LOCK -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, LockFragment())
|
||||
.addToBackStack("Lock")
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
else -> super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.util.SparseArray
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
@@ -18,9 +18,9 @@ import kotlinx.serialization.list
|
||||
import xyz.quaver.hitomi.*
|
||||
import xyz.quaver.hiyobi.cookie
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.ui.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.ReaderActivity
|
||||
import xyz.quaver.pupil.ui.ReaderActivity
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
@@ -52,7 +52,7 @@ class GalleryDownloader(
|
||||
cache.deleteRecursively()
|
||||
}
|
||||
|
||||
if (!reader.isActive && downloadJob?.isActive != true)
|
||||
if (reader?.isActive == false && downloadJob?.isActive != true)
|
||||
field = false
|
||||
|
||||
downloads.add(galleryBlock.id)
|
||||
@@ -63,7 +63,7 @@ class GalleryDownloader(
|
||||
onNotifyChangedHandler?.invoke(value)
|
||||
}
|
||||
|
||||
private val reader: Deferred<Reader>
|
||||
private val reader: Deferred<Reader>?
|
||||
private var downloadJob: Job? = null
|
||||
|
||||
private lateinit var notificationBuilder: NotificationCompat.Builder
|
||||
@@ -126,8 +126,8 @@ class GalleryDownloader(
|
||||
|
||||
if (reader.isNotEmpty()) {
|
||||
//Save cache
|
||||
if (!cache.parentFile.exists())
|
||||
cache.parentFile.mkdirs()
|
||||
if (cache.parentFile?.exists() == false)
|
||||
cache.parentFile!!.mkdirs()
|
||||
|
||||
cache.writeText(json.stringify(serializer, reader))
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class GalleryDownloader(
|
||||
|
||||
fun start() {
|
||||
downloadJob = CoroutineScope(Dispatchers.Default).launch {
|
||||
val reader = reader.await()
|
||||
val reader = reader!!.await()
|
||||
|
||||
if (reader.isEmpty())
|
||||
onErrorHandler?.invoke(IOException("Couldn't retrieve Reader"))
|
||||
@@ -183,8 +183,8 @@ class GalleryDownloader(
|
||||
} else
|
||||
setRequestProperty("Referer", getReferer(galleryBlock.id))
|
||||
|
||||
if (!cache.parentFile.exists())
|
||||
cache.parentFile.mkdirs()
|
||||
if (cache.parentFile?.exists() == false)
|
||||
cache.parentFile!!.mkdirs()
|
||||
|
||||
inputStream.copyTo(FileOutputStream(cache))
|
||||
}
|
||||
@@ -209,8 +209,6 @@ class GalleryDownloader(
|
||||
}
|
||||
}
|
||||
|
||||
onCompleteHandler?.invoke()
|
||||
|
||||
Timer(false).schedule(1000) {
|
||||
notificationBuilder
|
||||
.setContentTitle(galleryBlock.title)
|
||||
@@ -220,7 +218,7 @@ class GalleryDownloader(
|
||||
if (download) {
|
||||
File(cacheDir, "imageCache/${galleryBlock.id}").let {
|
||||
if (it.exists()) {
|
||||
val target = File(Environment.getExternalStorageDirectory(), "Pupil/${galleryBlock.id}")
|
||||
val target = File(getExternalFilesDir("Pupil"), galleryBlock.id.toString())
|
||||
|
||||
if (!target.exists())
|
||||
target.mkdirs()
|
||||
@@ -231,9 +229,11 @@ class GalleryDownloader(
|
||||
}
|
||||
|
||||
notificationManager.notify(galleryBlock.id, notificationBuilder.build())
|
||||
}
|
||||
|
||||
download = false
|
||||
onCompleteHandler?.invoke()
|
||||
|
||||
download = false
|
||||
}
|
||||
}
|
||||
|
||||
remove(galleryBlock.id)
|
||||
@@ -254,7 +254,7 @@ class GalleryDownloader(
|
||||
|
||||
fun invokeOnReaderLoaded() {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
onReaderLoadedHandler?.invoke(reader.await())
|
||||
onReaderLoadedHandler?.invoke(reader?.await() ?: return@launch)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ package xyz.quaver.pupil.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import java.io.File
|
||||
|
||||
fun getCachedGallery(context: Context, galleryID: Int): File {
|
||||
return File(Environment.getExternalStorageDirectory(), "Pupil/$galleryID").let {
|
||||
return File(context.getExternalFilesDir("Pupil"), galleryID.toString()).let {
|
||||
when {
|
||||
it.exists() -> it
|
||||
else -> File(context.cacheDir, "imageCache/$galleryID")
|
||||
|
||||
105
app/src/main/java/xyz/quaver/pupil/util/lock.kt
Normal file
105
app/src/main/java/xyz/quaver/pupil/util/lock.kt
Normal file
@@ -0,0 +1,105 @@
|
||||
package xyz.quaver.pupil.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||
private fun load() {
|
||||
val lock = File(ContextCompat.getDataDir(this), "lock.json")
|
||||
|
||||
if (!lock.exists()) {
|
||||
lock.createNewFile()
|
||||
lock.writeText("[]")
|
||||
}
|
||||
|
||||
locks = ArrayList(Json(JsonConfiguration.Stable).parseList(lock.readText()))
|
||||
}
|
||||
|
||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||
private fun save() {
|
||||
val lock = File(ContextCompat.getDataDir(this), "lock.json")
|
||||
|
||||
if (!lock.exists())
|
||||
lock.createNewFile()
|
||||
|
||||
lock.writeText(Json(JsonConfiguration.Stable).stringify(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 empty(): Boolean {
|
||||
return locks.isNullOrEmpty()
|
||||
}
|
||||
|
||||
fun contains(type: Lock.Type): Boolean {
|
||||
return locks?.any { it.type == type } ?: false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package xyz.quaver.pupil.util
|
||||
|
||||
import android.util.Log
|
||||
import kotlinx.serialization.json.*
|
||||
import java.net.URL
|
||||
|
||||
@@ -19,8 +20,20 @@ fun checkUpdate(url: String, currentVersion: String) : JsonObject? {
|
||||
if (releases.isEmpty())
|
||||
return null
|
||||
|
||||
if (currentVersion != releases[0].jsonObject["tag_name"]?.content)
|
||||
return releases[0].jsonObject
|
||||
val latestVersion = releases[0].jsonObject["tag_name"]?.content
|
||||
|
||||
return null
|
||||
return when {
|
||||
currentVersion.split('-').size == 1 -> {
|
||||
when {
|
||||
currentVersion != latestVersion -> releases[0].jsonObject
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
when {
|
||||
(currentVersion.split('-')[0] == latestVersion) -> releases[0].jsonObject
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
app/src/main/res/drawable/fingerprint.xml
Normal file
8
app/src/main/res/drawable/fingerprint.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- drawable/fingerprint.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="M17.81,4.47C17.73,4.47 17.65,4.45 17.58,4.41C15.66,3.42 14,3 12,3C10.03,3 8.15,3.47 6.44,4.41C6.2,4.54 5.9,4.45 5.76,4.21C5.63,3.97 5.72,3.66 5.96,3.53C7.82,2.5 9.86,2 12,2C14.14,2 16,2.47 18.04,3.5C18.29,3.65 18.38,3.95 18.25,4.19C18.16,4.37 18,4.47 17.81,4.47M3.5,9.72C3.4,9.72 3.3,9.69 3.21,9.63C3,9.47 2.93,9.16 3.09,8.93C4.08,7.53 5.34,6.43 6.84,5.66C10,4.04 14,4.03 17.15,5.65C18.65,6.42 19.91,7.5 20.9,8.9C21.06,9.12 21,9.44 20.78,9.6C20.55,9.76 20.24,9.71 20.08,9.5C19.18,8.22 18.04,7.23 16.69,6.54C13.82,5.07 10.15,5.07 7.29,6.55C5.93,7.25 4.79,8.25 3.89,9.5C3.81,9.65 3.66,9.72 3.5,9.72M9.75,21.79C9.62,21.79 9.5,21.74 9.4,21.64C8.53,20.77 8.06,20.21 7.39,19C6.7,17.77 6.34,16.27 6.34,14.66C6.34,11.69 8.88,9.27 12,9.27C15.12,9.27 17.66,11.69 17.66,14.66A0.5,0.5 0 0,1 17.16,15.16A0.5,0.5 0 0,1 16.66,14.66C16.66,12.24 14.57,10.27 12,10.27C9.43,10.27 7.34,12.24 7.34,14.66C7.34,16.1 7.66,17.43 8.27,18.5C8.91,19.66 9.35,20.15 10.12,20.93C10.31,21.13 10.31,21.44 10.12,21.64C10,21.74 9.88,21.79 9.75,21.79M16.92,19.94C15.73,19.94 14.68,19.64 13.82,19.05C12.33,18.04 11.44,16.4 11.44,14.66A0.5,0.5 0 0,1 11.94,14.16A0.5,0.5 0 0,1 12.44,14.66C12.44,16.07 13.16,17.4 14.38,18.22C15.09,18.7 15.92,18.93 16.92,18.93C17.16,18.93 17.56,18.9 17.96,18.83C18.23,18.78 18.5,18.96 18.54,19.24C18.59,19.5 18.41,19.77 18.13,19.82C17.56,19.93 17.06,19.94 16.92,19.94M14.91,22C14.87,22 14.82,22 14.78,22C13.19,21.54 12.15,20.95 11.06,19.88C9.66,18.5 8.89,16.64 8.89,14.66C8.89,13.04 10.27,11.72 11.97,11.72C13.67,11.72 15.05,13.04 15.05,14.66C15.05,15.73 16,16.6 17.13,16.6C18.28,16.6 19.21,15.73 19.21,14.66C19.21,10.89 15.96,7.83 11.96,7.83C9.12,7.83 6.5,9.41 5.35,11.86C4.96,12.67 4.76,13.62 4.76,14.66C4.76,15.44 4.83,16.67 5.43,18.27C5.53,18.53 5.4,18.82 5.14,18.91C4.88,19 4.59,18.87 4.5,18.62C4,17.31 3.77,16 3.77,14.66C3.77,13.46 4,12.37 4.45,11.42C5.78,8.63 8.73,6.82 11.96,6.82C16.5,6.82 20.21,10.33 20.21,14.65C20.21,16.27 18.83,17.59 17.13,17.59C15.43,17.59 14.05,16.27 14.05,14.65C14.05,13.58 13.12,12.71 11.97,12.71C10.82,12.71 9.89,13.58 9.89,14.65C9.89,16.36 10.55,17.96 11.76,19.16C12.71,20.1 13.62,20.62 15.03,21C15.3,21.08 15.45,21.36 15.38,21.62C15.33,21.85 15.12,22 14.91,22Z" />
|
||||
</vector>
|
||||
8
app/src/main/res/drawable/lastpass.xml
Normal file
8
app/src/main/res/drawable/lastpass.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- 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="#000" android:pathData="M14,12A2,2 0 0,1 16,10A2,2 0 0,1 18,12A2,2 0 0,1 16,14A2,2 0 0,1 14,12M8,12A2,2 0 0,1 10,10A2,2 0 0,1 12,12A2,2 0 0,1 10,14A2,2 0 0,1 8,12M2,12A2,2 0 0,1 4,10A2,2 0 0,1 6,12A2,2 0 0,1 4,14A2,2 0 0,1 2,12M22,5H20V19H22V5Z" />
|
||||
</vector>
|
||||
8
app/src/main/res/drawable/lock_pattern.xml
Normal file
8
app/src/main/res/drawable/lock_pattern.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<!-- 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="#000" android:pathData="M7,3A4,4 0 0,1 11,7C11,8.86 9.73,10.43 8,10.87V13.13C8.37,13.22 8.72,13.37 9.04,13.56L13.56,9.04C13.2,8.44 13,7.75 13,7A4,4 0 0,1 17,3A4,4 0 0,1 21,7A4,4 0 0,1 17,11C16.26,11 15.57,10.8 15,10.45L10.45,15C10.8,15.57 11,16.26 11,17A4,4 0 0,1 7,21A4,4 0 0,1 3,17C3,15.14 4.27,13.57 6,13.13V10.87C4.27,10.43 3,8.86 3,7A4,4 0 0,1 7,3M17,13A4,4 0 0,1 21,17A4,4 0 0,1 17,21A4,4 0 0,1 13,17A4,4 0 0,1 17,13M17,15A2,2 0 0,0 15,17A2,2 0 0,0 17,19A2,2 0 0,0 19,17A2,2 0 0,0 17,15Z" />
|
||||
</vector>
|
||||
64
app/src/main/res/layout/activity_lock.xml
Normal file
64
app/src/main/res/layout/activity_lock.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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_button_layout"/>
|
||||
|
||||
<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_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:gravity="center">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/lock_pattern"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/lock_pattern"
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
app:fabSize="mini"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/lock_pin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/numeric"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
app:backgroundTint="@color/dark_gray"
|
||||
app:fabSize="mini"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/lock_fingerprint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/fingerprint"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
app:backgroundTint="@color/dark_gray"
|
||||
app:fabSize="mini"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/lock_password"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/lastpass"
|
||||
app:backgroundTint="@color/dark_gray"
|
||||
app:fabSize="mini"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -6,7 +6,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
tools:context=".ui.MainActivity">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/dark_gray"
|
||||
tools:context=".ReaderActivity">
|
||||
tools:context=".ui.ReaderActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
19
app/src/main/res/layout/fragment_pattern_lock.xml
Normal file
19
app/src/main/res/layout/fragment_pattern_lock.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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.PatternLockFragment">
|
||||
|
||||
<com.andrognito.patternlockview.PatternLockView
|
||||
android:id="@+id/lock_pattern_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>
|
||||
@@ -8,7 +8,8 @@
|
||||
<string name="search_hint">ギャラリー検索</string>
|
||||
<string name="search_hint_with_page">ギャラリー検索</string>
|
||||
<string name="settings_clear_image_cache">イメージキャッシュクリア</string>
|
||||
<string name="settings_clear_downloads_summary">サイズ: %1$d%2$s</string>
|
||||
<string name="settings_clear_cache_alert_message">キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか?</string>
|
||||
<string name="settings_clear_summary">サイズ: %1$d%2$s</string>
|
||||
<string name="settings_default_query">デフォルトキーワード</string>
|
||||
<string name="settings_galleries_per_page">一回にロードするギャラリー数</string>
|
||||
<string name="settings_search_title">検索設定</string>
|
||||
@@ -66,4 +67,16 @@
|
||||
<string name="main_open_gallery_by_id_error">エラーが発生しました</string>
|
||||
<string name="settings_storage">ストレージ</string>
|
||||
<string name="main_drawer_grouop_contact_kakaotalk">カカオトーク</string>
|
||||
<string name="settings_app_lock">アプリロック</string>
|
||||
<string name="settings_app_lock_type">アップロックの種類</string>
|
||||
<string name="settings_app_version_title">バージョン</string>
|
||||
<string name="settings_lock_biomatrics">生体認識</string>
|
||||
<string name="settings_lock_confirm">ロック確認のためもう一回入力してください。</string>
|
||||
<string name="settings_lock_enabled">有効</string>
|
||||
<string name="settings_lock_fingerprint">指紋</string>
|
||||
<string name="settings_lock_password">パスワード</string>
|
||||
<string name="settings_lock_pattern">パターン</string>
|
||||
<string name="settings_lock_wrong_confirm">ロックが一致しません。やり直してください。</string>
|
||||
<string name="settings_lock_none">なし</string>
|
||||
<string name="settings_lock_remove_message">ロックを無効にしますか?</string>
|
||||
</resources>
|
||||
@@ -7,7 +7,8 @@
|
||||
<string name="search_hint_with_page">갤러리 검색</string>
|
||||
<string name="settings_default_query">기본 검색어</string>
|
||||
<string name="settings_clear_image_cache">이미지 캐시 정리하기</string>
|
||||
<string name="settings_clear_downloads_summary">사용량: %1$d%2$s</string>
|
||||
<string name="settings_clear_cache_alert_message">캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까?</string>
|
||||
<string name="settings_clear_summary">사용량: %1$d%2$s</string>
|
||||
<string name="settings_galleries_per_page">한 번에 로드할 갤러리 수</string>
|
||||
<string name="settings_search_title">검색 설정</string>
|
||||
<string name="settings_title">설정</string>
|
||||
@@ -66,4 +67,16 @@
|
||||
<string name="main_open_gallery_by_id_error">갤러리를 찾지 못했습니다</string>
|
||||
<string name="settings_storage">저장 공간</string>
|
||||
<string name="main_drawer_grouop_contact_kakaotalk">카카오톡 오픈채팅방</string>
|
||||
<string name="settings_app_lock">앱 잠금</string>
|
||||
<string name="settings_app_lock_type">앱 잠금 종류</string>
|
||||
<string name="settings_app_version_title">앱 버전</string>
|
||||
<string name="settings_lock_biomatrics">생체 인식</string>
|
||||
<string name="settings_lock_confirm">잠금 확인을 위해 한번 더 입력해주세요</string>
|
||||
<string name="settings_lock_enabled">사용 중</string>
|
||||
<string name="settings_lock_fingerprint">지문</string>
|
||||
<string name="settings_lock_password">비밀번호</string>
|
||||
<string name="settings_lock_pattern">패턴</string>
|
||||
<string name="settings_lock_wrong_confirm">잠금이 일치하지 않습니다. 다시 시도하세요.</string>
|
||||
<string name="settings_lock_none">없음</string>
|
||||
<string name="settings_lock_remove_message">잠금을 해제할까요?</string>
|
||||
</resources>
|
||||
@@ -80,23 +80,38 @@
|
||||
<string name="reader_notification_error">Download error</string>
|
||||
|
||||
<string name="settings_title">Settings</string>
|
||||
<string name="settings_app_version_title">App version</string>
|
||||
<string name="settings_search_title">Search Settings</string>
|
||||
<string name="settings_galleries_per_page">Galleries per page</string>
|
||||
<string name="settings_default_query">Default query</string>
|
||||
<string name="settings_storage">Storage</string>
|
||||
<string name="settings_clear_image_cache">Clear image cache</string>
|
||||
<string name="settings_clear_downloads_summary">Currently using %1$d%2$s</string>
|
||||
<string name="settings_clear_cache_alert_message">Deleting cache can affect image loading speed. Do you want to continue?</string>
|
||||
<string name="settings_clear_summary">Currently using %1$d%2$s</string>
|
||||
<string name="settings_clear_downloads">Clear downloads</string>
|
||||
<string name="settings_clear_downloads_alert_message">Delete all downloaded galleries.\nDo you want to continue?</string>
|
||||
<string name="settings_clear_history">Clear history</string>
|
||||
<string name="settings_clear_history_alert_message">Do you want to clear histories?</string>
|
||||
<string name="settings_clear_history_summary">%1$d histories saved</string>
|
||||
<string name="settings_app_lock">App lock</string>
|
||||
<string name="settings_app_lock_type">App lock type</string>
|
||||
<string name="settings_miscellaneous_title">Miscellaneous</string>
|
||||
<string name="settings_use_hiyobi_title">Use hiyobi.me</string>
|
||||
<string name="settings_use_hiyobi_summary">Load images from hiyobi.me to improve loading speed (if available)</string>
|
||||
<string name="settings_security_mode_title">Enable security mode</string>
|
||||
<string name="settings_security_mode_summary">Enable security mode to make the screen invisible on recent app window</string>
|
||||
|
||||
<string name="settings_lock_none">None</string>
|
||||
<string name="settings_lock_pattern">Pattern</string>
|
||||
<string name="settings_lock_pin" translatable="false">PIN</string>
|
||||
<string name="settings_lock_password">Password</string>
|
||||
<string name="settings_lock_biomatrics">Biomatrics</string>
|
||||
<string name="settings_lock_fingerprint">Fingerprint</string>
|
||||
<string name="settings_lock_enabled">Enabled</string>
|
||||
<string name="settings_lock_confirm">Input same lock once more to confirm Lock</string>
|
||||
<string name="settings_lock_remove_message">Do you want to remove lock?</string>
|
||||
<string name="settings_lock_wrong_confirm">Lock is different from last one. Please try again.</string>
|
||||
|
||||
<string name="default_query_dialog_title">Set default query</string>
|
||||
<string name="default_query_dialog_language">Language: </string>
|
||||
<string name="default_query_dialog_filter_BL">Filter BL</string>
|
||||
|
||||
26
app/src/main/res/xml/lock_preferences.xml
Normal file
26
app/src/main/res/xml/lock_preferences.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_lock_pattern"
|
||||
app:key="lock_pattern"/>
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_lock_pin"
|
||||
app:key="lock_pin"/>
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_lock_password"
|
||||
app:key="lock_password"/>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings_lock_biomatrics">
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_lock_fingerprint"
|
||||
app:key="lock_fingerprint"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -2,6 +2,10 @@
|
||||
<androidx.preference.PreferenceScreen
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_app_version_title"
|
||||
app:key="app_version"/>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings_search_title">
|
||||
|
||||
@@ -24,6 +28,10 @@
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings_storage">
|
||||
|
||||
<Preference
|
||||
app:title="Clear cache"
|
||||
app:key="delete_cache"/>
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_clear_downloads"
|
||||
app:key="delete_downloads"/>
|
||||
@@ -34,6 +42,15 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings_app_lock">
|
||||
|
||||
<Preference
|
||||
app:title="@string/settings_app_lock_type"
|
||||
app:key="app_lock"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/settings_miscellaneous_title">
|
||||
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
package xyz.quaver.hitomi
|
||||
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
||||
|
||||
class UnitTest {
|
||||
@Test
|
||||
fun test() {
|
||||
val galleries = getGalleryIDsForQuery("series:touhou_project")
|
||||
|
||||
println(galleries.size)
|
||||
}
|
||||
|
||||
private fun getByIp(host: String): InetAddress {
|
||||
try {
|
||||
return InetAddress.getByName(host)
|
||||
} catch (e: UnknownHostException) {
|
||||
// unlikely
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user