Merge pull request #11 from tom5079/development

Pupil v2.10
This commit is contained in:
tom5079
2019-06-23 16:17:49 +09:00
committed by GitHub
31 changed files with 736 additions and 131 deletions

2
.idea/misc.xml generated
View File

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

View File

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

View File

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

View File

@@ -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":{}}]

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

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

View File

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

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

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

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

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

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

View File

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

View File

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