From cdb335111ed2a0a2f38a4b714455052dd1893f0d Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 13 Jun 2019 21:05:52 +0900 Subject: [PATCH 01/28] Added URL support Added Firebase Added progressbar to the full screen horizontal reader view --- app/build.gradle | 13 ++- app/src/main/AndroidManifest.xml | 97 ++++++++++++++++++- app/src/main/java/xyz/quaver/pupil/Pupil.kt | 8 +- .../java/xyz/quaver/pupil/ReaderActivity.kt | 61 ++++++++++-- app/src/main/res/layout/activity_reader.xml | 18 +++- build.gradle | 13 ++- 6 files changed, 176 insertions(+), 34 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index da8a39ab..702199c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,6 +2,9 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlinx-serialization' +apply plugin: 'com.google.gms.google-services' +apply plugin: 'io.fabric' +apply plugin: 'com.google.firebase.firebase-perf' android { compileSdkVersion 28 @@ -9,8 +12,8 @@ android { applicationId "xyz.quaver.pupil" minSdkVersion 16 targetSdkVersion 28 - versionCode 15 - versionName "2.8" + versionCode 16 + versionName "2.9" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -37,15 +40,15 @@ 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.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.deano2390:MaterialShowcaseView:1.3.4' 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('com.finotes:finotescore:2.5.7@aar') { - transitive = true - } testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e8cbecd4..294d57a0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ package="xyz.quaver.pupil"> - + - + android:configChanges="keyboardHidden|orientation|screenSize"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt index 368b183a..d40eef41 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt @@ -1,5 +1,6 @@ package xyz.quaver.pupil +import android.app.Application import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager @@ -7,12 +8,10 @@ import android.content.Context import android.os.Build import android.preference.PreferenceManager import androidx.core.content.ContextCompat -import com.finotes.android.finotescore.Fn -import com.finotes.android.finotescore.ObservableApplication import xyz.quaver.pupil.util.Histories import java.io.File -class Pupil : ObservableApplication() { +class Pupil : Application() { lateinit var histories: Histories lateinit var downloads: Histories @@ -26,9 +25,6 @@ class Pupil : ObservableApplication() { favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json")) super.onCreate() - Fn.init(this) - - Fn.enableFrameDetection() if (!preference.getBoolean("channel_created", false)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index d5d92b2f..bc379688 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -1,5 +1,6 @@ package xyz.quaver.pupil +import android.content.Intent import android.graphics.drawable.Animatable import android.graphics.drawable.Drawable import android.os.Bundle @@ -20,11 +21,13 @@ import kotlinx.android.synthetic.main.dialog_numberpicker.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.io.IOException import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import xyz.quaver.hitomi.GalleryBlock +import xyz.quaver.hitomi.getGalleryBlock import xyz.quaver.pupil.adapters.ReaderAdapter import xyz.quaver.pupil.util.GalleryDownloader import xyz.quaver.pupil.util.Histories @@ -39,6 +42,14 @@ class ReaderActivity : AppCompatActivity() { private var isScroll = true private var isFullscreen = false + set(value) { + field = value + + reader_progressbar.visibility = when { + value -> View.VISIBLE + else -> View.GONE + } + } private lateinit var downloader: GalleryDownloader @@ -59,10 +70,7 @@ class ReaderActivity : AppCompatActivity() { setContentView(R.layout.activity_reader) - galleryBlock = Json(JsonConfiguration.Stable).parse( - GalleryBlock.serializer(), - intent.getStringExtra("galleryblock") - ) + handleIntent(intent) supportActionBar?.title = galleryBlock.title supportActionBar?.setDisplayHomeAsUpEnabled(false) @@ -75,6 +83,40 @@ class ReaderActivity : AppCompatActivity() { downloader.start() } + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + handleIntent(intent) + } + + private fun handleIntent(intent: Intent) { + if (intent.action == Intent.ACTION_VIEW) { + val uri = intent.data + val lastPathSegment = uri?.lastPathSegment + if (uri != null && lastPathSegment != null) { + val nonNumber = Regex("[^-?0-9]+") + + val galleryID = when (uri.host) { + "hitomi.la" -> lastPathSegment.replace(nonNumber, "").toInt() + "히요비.asia" -> lastPathSegment.toInt() + "xn--9w3b15m8vo.asia" -> lastPathSegment.toInt() + "e-hentai.org" -> uri.pathSegments[1].toInt() + else -> return + } + + runBlocking { + CoroutineScope(Dispatchers.IO).launch { + galleryBlock = getGalleryBlock(galleryID) ?: return@launch + }.join() + } + } + } else { + galleryBlock = Json(JsonConfiguration.Stable).parse( + GalleryBlock.serializer(), + intent.getStringExtra("galleryblock") + ) + } + } + override fun onResume() { val preferences = PreferenceManager.getDefaultSharedPreferences(this) @@ -177,11 +219,13 @@ class ReaderActivity : AppCompatActivity() { downloader = d.apply { onReaderLoadedHandler = { CoroutineScope(Dispatchers.Main).launch { + with(reader_download_progressbar) { + max = it.size + progress = 0 + } with(reader_progressbar) { max = it.size progress = 0 - - visibility = View.VISIBLE } gallerySize = it.size @@ -190,7 +234,7 @@ class ReaderActivity : AppCompatActivity() { } onProgressHandler = { CoroutineScope(Dispatchers.Main).launch { - reader_progressbar.progress = it + reader_download_progressbar.progress = it menu?.findItem(R.id.reader_menu_use_hiyobi)?.isVisible = downloader.useHiyobi } } @@ -213,7 +257,7 @@ class ReaderActivity : AppCompatActivity() { } onCompleteHandler = { CoroutineScope(Dispatchers.Main).launch { - reader_progressbar.visibility = View.GONE + reader_download_progressbar.visibility = View.GONE } } onNotifyChangedHandler = { notify -> @@ -268,6 +312,7 @@ class ReaderActivity : AppCompatActivity() { return currentPage = layoutManager.findFirstVisibleItemPosition()+1 menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/$gallerySize" + this@ReaderActivity.reader_progressbar.progress = currentPage } }) diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index 9e49df68..ea646156 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -21,19 +21,27 @@ - + android:layout_height="wrap_content" + android:orientation="vertical"> + + + android:progressTint="@color/material_green_a700" + android:visibility="gone"/> - + Date: Thu, 13 Jun 2019 21:18:26 +0900 Subject: [PATCH 02/28] Bug fix --- app/build.gradle | 2 ++ app/release/output.json | 2 +- app/src/main/java/xyz/quaver/pupil/Pupil.kt | 3 ++- app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt | 5 +++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 702199c1..46f2ce9b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,6 +15,7 @@ android { versionCode 16 versionName "2.9" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + multiDexEnabled true } buildTypes { release { @@ -36,6 +37,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1' implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0" + implementation 'com.android.support:multidex:1.0.3' implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.preference:preference:1.1.0-beta01' diff --git a/app/release/output.json b/app/release/output.json index 456cc06e..0cc69cca 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":15,"versionName":"2.8","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file +[{"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":{}}] \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt index d40eef41..add8d659 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt @@ -8,10 +8,11 @@ import android.content.Context import android.os.Build import android.preference.PreferenceManager import androidx.core.content.ContextCompat +import androidx.multidex.MultiDexApplication import xyz.quaver.pupil.util.Histories import java.io.File -class Pupil : Application() { +class Pupil : MultiDexApplication() { lateinit var histories: Histories lateinit var downloads: Histories diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index bc379688..84e9378b 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -72,6 +72,11 @@ class ReaderActivity : AppCompatActivity() { handleIntent(intent) + if (!::galleryBlock.isInitialized) { + finish() + return + } + supportActionBar?.title = galleryBlock.title supportActionBar?.setDisplayHomeAsUpEnabled(false) From f48180665fbd4d63b01b8364587874a682a723a0 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 13 Jun 2019 21:29:57 +0900 Subject: [PATCH 03/28] Bug fix --- app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 84e9378b..a997f373 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -73,7 +73,7 @@ class ReaderActivity : AppCompatActivity() { handleIntent(intent) if (!::galleryBlock.isInitialized) { - finish() + onBackPressed() return } @@ -189,7 +189,7 @@ class ReaderActivity : AppCompatActivity() { override fun onDestroy() { super.onDestroy() - if (!downloader.download) + if (::downloader.isInitialized && !downloader.download) downloader.cancel() } From 45c6f95fcbe81c50e14269acb884ae8f1be0fe2c Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 13 Jun 2019 21:45:31 +0900 Subject: [PATCH 04/28] Changed to non-scrolling horizontal image view --- .../java/xyz/quaver/pupil/ReaderActivity.kt | 14 ++++++-------- .../xyz/quaver/pupil/adapters/ReaderAdapter.kt | 18 ++++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index a997f373..49fa1f33 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -9,9 +9,9 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.LinearSmoothScroller 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.google.android.material.snackbar.Snackbar @@ -330,12 +330,7 @@ class ReaderActivity : AppCompatActivity() { scrollMode(false) fullscreen(true) } else { - val smoothScroller = object : LinearSmoothScroller(context) { - override fun getVerticalSnapPreference() = SNAP_TO_START - }.apply { - targetPosition = currentPage - } - (reader_recyclerview.layoutManager as LinearLayoutManager?)?.startSmoothScroll(smoothScroller) + (reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPosition(currentPage) } } } @@ -377,7 +372,10 @@ class ReaderActivity : AppCompatActivity() { reader_recyclerview.layoutManager = LinearLayoutManager(this) } else { snapHelper.attachToRecyclerView(reader_recyclerview) - reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) + reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false).apply { + isItemPrefetchEnabled = true + initialPrefetchItemCount = 4 + } } (reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0) diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt index a579865e..f4f33a09 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -25,21 +25,19 @@ class ReaderAdapter(private val images: List) : RecyclerView.Adapter Date: Fri, 14 Jun 2019 20:01:32 +0900 Subject: [PATCH 05/28] Memory optimization --- app/build.gradle | 4 ++-- app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 46f2ce9b..b92db1e7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "xyz.quaver.pupil" minSdkVersion 16 targetSdkVersion 28 - versionCode 16 - versionName "2.9" + versionCode 17 + versionName "2.9.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true } diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 49fa1f33..fadaf454 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -11,7 +11,6 @@ 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.google.android.material.snackbar.Snackbar @@ -372,10 +371,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) From d705cd15b747f5aa64e1596f1f453ed272c85a4b Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sun, 23 Jun 2019 00:23:17 +0900 Subject: [PATCH 06/28] Migrate to Android 29 Re-Added Cache clear to prevent deleting downloading images --- .idea/misc.xml | 2 +- app/build.gradle | 16 +++-- app/proguard-rules.pro | 9 +-- app/release/output.json | 2 +- .../quaver/pupil/ExampleInstrumentedTest.kt | 13 ++-- app/src/main/AndroidManifest.xml | 49 ++++++++------- .../pupil/adapters/GalleryBlockAdapter.kt | 2 +- .../quaver/pupil/adapters/ReaderAdapter.kt | 3 - .../java/xyz/quaver/pupil/ui/LockActivity.kt | 29 +++++++++ .../xyz/quaver/pupil/{ => ui}/MainActivity.kt | 47 +++++++------- .../quaver/pupil/ui/PatternLockFragment.kt | 46 ++++++++++++++ .../java/xyz/quaver/pupil/{ => ui}/Pupil.kt | 6 +- .../quaver/pupil/{ => ui}/ReaderActivity.kt | 8 ++- .../quaver/pupil/{ => ui}/SettingsActivity.kt | 61 +++++++++++++++---- .../quaver/pupil/util/GalleryDownloader.kt | 32 +++++----- .../main/java/xyz/quaver/pupil/util/file.kt | 3 +- .../main/java/xyz/quaver/pupil/util/lock.kt | 39 ++++++++++++ .../main/java/xyz/quaver/pupil/util/update.kt | 19 +++++- app/src/main/res/drawable/fingerprint.xml | 8 +++ app/src/main/res/drawable/lastpass.xml | 8 +++ app/src/main/res/drawable/lock_pattern.xml | 8 +++ app/src/main/res/layout/activity_lock.xml | 54 ++++++++++++++++ .../main/res/layout/activity_main_content.xml | 2 +- app/src/main/res/layout/activity_reader.xml | 2 +- .../main/res/layout/fragment_pattern_lock.xml | 19 ++++++ app/src/main/res/values-ja/strings.xml | 6 +- app/src/main/res/values-ko/strings.xml | 6 +- app/src/main/res/values/strings.xml | 6 +- app/src/main/res/xml/root_preferences.xml | 17 ++++++ .../test/java/xyz/quaver/hitomi/UnitTest.kt | 17 ++++-- 30 files changed, 420 insertions(+), 119 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt rename app/src/main/java/xyz/quaver/pupil/{ => ui}/MainActivity.kt (97%) create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt rename app/src/main/java/xyz/quaver/pupil/{ => ui}/Pupil.kt (94%) rename app/src/main/java/xyz/quaver/pupil/{ => ui}/ReaderActivity.kt (98%) rename app/src/main/java/xyz/quaver/pupil/{ => ui}/SettingsActivity.kt (81%) create mode 100644 app/src/main/java/xyz/quaver/pupil/util/lock.kt create mode 100644 app/src/main/res/drawable/fingerprint.xml create mode 100644 app/src/main/res/drawable/lastpass.xml create mode 100644 app/src/main/res/drawable/lock_pattern.xml create mode 100644 app/src/main/res/layout/activity_lock.xml create mode 100644 app/src/main/res/layout/fragment_pattern_lock.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index fb8c126a..7631aec3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b92db1e7..97cb1eb5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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 + targetSdkVersion 29 versionCode 17 - versionName "2.9.1" + 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') diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index efb4fd24..481bb434 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -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 \ No newline at end of file +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/release/output.json b/app/release/output.json index 0cc69cca..f084575a 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -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":{}}] \ No newline at end of file +[{"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":{}}] \ No newline at end of file diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt index 79db10d2..facb9b50 100644 --- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt @@ -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::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 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 294d57a0..c3e6686f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,20 +6,19 @@ - + android:theme="@style/AppTheme"> + - + android:name=".ui.ReaderActivity" + android:configChanges="keyboardHidden|orientation|screenSize" + android:parentActivityName=".ui.MainActivity"> @@ -27,9 +26,9 @@ + android:pathPrefix="/galleries" + android:scheme="https" /> @@ -38,9 +37,9 @@ + android:pathPrefix="/reader" + android:scheme="https" /> @@ -49,9 +48,9 @@ + android:pathPrefix="/reader" + android:scheme="https" /> @@ -60,9 +59,9 @@ + android:pathPrefix="/g" + android:scheme="https" /> @@ -71,9 +70,9 @@ + android:pathPrefix="/galleries" + android:scheme="http" /> @@ -82,9 +81,9 @@ + android:pathPrefix="/reader" + android:scheme="http" /> @@ -93,9 +92,9 @@ + android:pathPrefix="/reader" + android:scheme="http" /> @@ -104,16 +103,16 @@ + android:pathPrefix="/g" + android:scheme="http" /> diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt index 12080356..9cf73343 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -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 diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt index f4f33a09..e75617c4 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -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) : RecyclerView.Adapter() { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt new file mode 100644 index 00000000..30f1ce1b --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt @@ -0,0 +1,29 @@ +package xyz.quaver.pupil.ui + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.widget.Toast +import kotlinx.android.synthetic.main.activity_lock.* +import xyz.quaver.pupil.R + +class LockActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_lock) + + supportFragmentManager.beginTransaction().add( + R.id.lock_content, + PatternLockFragment().apply { + onPatternDrawn = { + Toast.makeText(context, it, Toast.LENGTH_SHORT).show() + } + } + ).commit() + + lock_pattern.isEnabled = false + lock_fingerprint.isEnabled = false + lock_password.isEnabled = false + } + +} diff --git a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt similarity index 97% rename from app/src/main/java/xyz/quaver/pupil/MainActivity.kt rename to app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 1933c54e..87cd9027 100644 --- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -1,4 +1,4 @@ -package xyz.quaver.pupil +package xyz.quaver.pupil.ui import android.Manifest import android.content.Intent @@ -6,7 +6,6 @@ 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 +20,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 +40,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 +53,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,6 +68,12 @@ class MainActivity : AppCompatActivity() { private val galleries = ArrayList>>() private var query = "" + set(value) { + field = value + findViewById(R.id.search_bar_text) + .setText(query, TextView.BufferType.EDITABLE) + } + private var mode = Mode.SEARCH private val SETTINGS = 45162 @@ -110,19 +119,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(R.id.search_bar_text).setText(query, TextView.BufferType.EDITABLE) cancelFetch() clearGalleries() @@ -352,8 +353,7 @@ class MainActivity : AppCompatActivity() { onChipClickedHandler.add { runOnUiThread { query = it.toQuery() - this@MainActivity.findViewById(R.id.search_bar_text) - .setText(query, TextView.BufferType.EDITABLE) + currentPage = 0 cancelFetch() clearGalleries() @@ -726,7 +726,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 +805,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 +883,8 @@ class MainActivity : AppCompatActivity() { } private fun cancelFetch() { - runBlocking { - galleryIDs?.cancelAndJoin() - loadingJob?.cancelAndJoin() - } + galleryIDs?.cancel() + loadingJob?.cancel() } private fun clearGalleries() { @@ -992,7 +993,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 +1010,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 +1025,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)) } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt new file mode 100644 index 00000000..73a7197f --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt @@ -0,0 +1,46 @@ +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 + +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?) { + val password = PatternLockUtils.patternToMD5(lock_pattern_view, pattern) + onPatternDrawn?.invoke(hash(password)) + } + + override fun onProgress(progressPattern: MutableList?) { + + } + + override fun onStarted() { + + } + +} diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/ui/Pupil.kt similarity index 94% rename from app/src/main/java/xyz/quaver/pupil/Pupil.kt rename to app/src/main/java/xyz/quaver/pupil/ui/Pupil.kt index add8d659..c1859fe3 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/Pupil.kt @@ -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 diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt similarity index 98% rename from app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt rename to app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index fadaf454..990f7b50 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -1,4 +1,4 @@ -package xyz.quaver.pupil +package xyz.quaver.pupil.ui import android.content.Intent import android.graphics.drawable.Animatable @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.PagerSnapHelper import androidx.recyclerview.widget.RecyclerView 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.* @@ -27,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 @@ -71,6 +73,8 @@ class ReaderActivity : AppCompatActivity() { handleIntent(intent) + Crashlytics.setInt("GalleryID", galleryBlock.id) + if (!::galleryBlock.isInitialized) { onBackPressed() return @@ -116,7 +120,7 @@ class ReaderActivity : AppCompatActivity() { } else { galleryBlock = Json(JsonConfiguration.Stable).parse( GalleryBlock.serializer(), - intent.getStringExtra("galleryblock") + intent.getStringExtra("galleryblock")!! ) } } diff --git a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt similarity index 81% rename from app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt rename to app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt index e93b0166..850ed55d 100644 --- a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt @@ -1,8 +1,6 @@ -package xyz.quaver.pupil +package xyz.quaver.pupil.ui 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,7 +13,10 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import io.fabric.sdk.android.BuildConfig import kotlinx.android.synthetic.main.dialog_default_query.view.* +import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tags import java.io.File @@ -58,7 +59,7 @@ class SettingsActivity : AppCompatActivity() { "TB" //really? ) - private fun getCacheSize(dir: File) : String { + private fun getDirSize(dir: File) : String { var size = dir.walk().map { it.length() }.sum() var suffixIndex = 0 @@ -67,20 +68,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("app_version")) { + this ?: return@with + + val manager = context.packageManager + val info = manager.getPackageInfo(context.packageName, 0) + + summary = info.versionName + } + + with(findPreference("delete_cache")) { + this ?: return@with + + 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("delete_downloads")) { this ?: return@with - 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 +126,7 @@ class SettingsActivity : AppCompatActivity() { downloads.clear() - summary = getCacheSize(dir) + summary = getDirSize(dir) } setNegativeButton(android.R.string.no) { _, _ -> } }.show() @@ -107,7 +141,7 @@ class SettingsActivity : AppCompatActivity() { 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) @@ -139,7 +173,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 +188,7 @@ class SettingsActivity : AppCompatActivity() { with(dialogView.default_query_dialog_language_selector) { adapter = - ArrayAdapter( + ArrayAdapter( context, android.R.layout.simple_spinner_dropdown_item, arrayListOf( @@ -237,6 +271,9 @@ class SettingsActivity : AppCompatActivity() { true } } + with(findPreference("app_lock")) { + + } } } diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt index f4e08369..fda31db8 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -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 + private val reader: Deferred? 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) } } diff --git a/app/src/main/java/xyz/quaver/pupil/util/file.kt b/app/src/main/java/xyz/quaver/pupil/util/file.kt index 7e085650..f351dcd2 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/file.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/file.kt @@ -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") diff --git a/app/src/main/java/xyz/quaver/pupil/util/lock.kt b/app/src/main/java/xyz/quaver/pupil/util/lock.kt new file mode 100644 index 00000000..fefcc698 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/util/lock.kt @@ -0,0 +1,39 @@ +package xyz.quaver.pupil.util + +import android.content.Context +import android.content.ContextWrapper +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 { + val salt = (0 until 12).map { source.random() }.joinToString() + + return Pair(hash(password+salt), salt) +} + +val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +data class Lock(private val type: Type, private val hash: String, private val salt: String) { + + enum class Type { + PATTERN + } + + fun match(password: String): Boolean { + return hash(password+salt) == hash + } +} + +class LockManager(base: Context): ContextWrapper(base) { + + + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt index 2c951cbe..1461f75c 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/update.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt @@ -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 + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/fingerprint.xml b/app/src/main/res/drawable/fingerprint.xml new file mode 100644 index 00000000..ffa8a34b --- /dev/null +++ b/app/src/main/res/drawable/fingerprint.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/lastpass.xml b/app/src/main/res/drawable/lastpass.xml new file mode 100644 index 00000000..9dbf638b --- /dev/null +++ b/app/src/main/res/drawable/lastpass.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/lock_pattern.xml b/app/src/main/res/drawable/lock_pattern.xml new file mode 100644 index 00000000..f1be51c2 --- /dev/null +++ b/app/src/main/res/drawable/lock_pattern.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml new file mode 100644 index 00000000..6cb1e1c8 --- /dev/null +++ b/app/src/main/res/layout/activity_lock.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml index a5b3b266..bde8ddf4 100644 --- a/app/src/main/res/layout/activity_main_content.xml +++ b/app/src/main/res/layout/activity_main_content.xml @@ -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"> + tools:context=".ui.ReaderActivity"> + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 8e7dd7d0..683b49d7 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -8,7 +8,8 @@ ギャラリー検索 ギャラリー検索 イメージキャッシュクリア - サイズ: %1$d%2$s + キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか? + サイズ: %1$d%2$s デフォルトキーワード 一回にロードするギャラリー数 検索設定 @@ -66,4 +67,7 @@ エラーが発生しました ストレージ カカオトーク + アプリロック + アップロックの種類 + バージョン \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 0b99f608..2ffdb6ec 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -7,7 +7,8 @@ 갤러리 검색 기본 검색어 이미지 캐시 정리하기 - 사용량: %1$d%2$s + 캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까? + 사용량: %1$d%2$s 한 번에 로드할 갤러리 수 검색 설정 설정 @@ -66,4 +67,7 @@ 갤러리를 찾지 못했습니다 저장 공간 카카오톡 오픈채팅방 + 앱 잠금 + 앱 잠금 종류 + 앱 버전 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2cc8d514..b8fd3621 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,17 +80,21 @@ Download error Settings + App version Search Settings Galleries per page Default query Storage Clear image cache - Currently using %1$d%2$s + Deleting cache can affect image loading speed. Do you want to continue? + Currently using %1$d%2$s Clear downloads Delete all downloaded galleries.\nDo you want to continue? Clear history Do you want to clear histories? %1$d histories saved + App lock + App lock type Miscellaneous Use hiyobi.me Load images from hiyobi.me to improve loading speed (if available) diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 0ccfbbb2..5a802f27 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -2,6 +2,10 @@ + + @@ -24,6 +28,10 @@ + + @@ -34,6 +42,15 @@ + + + + + + diff --git a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt index bc7c0d78..1b864d91 100644 --- a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt +++ b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt @@ -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 From a990f5d663196333b1328fad00d989a244c32390 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sun, 23 Jun 2019 10:27:07 +0900 Subject: [PATCH 07/28] Added lock --- .../java/xyz/quaver/pupil/ui/LockActivity.kt | 66 +++++++++- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 15 ++- .../quaver/pupil/ui/PatternLockFragment.kt | 3 +- .../xyz/quaver/pupil/ui/SettingsActivity.kt | 123 +++++++++++++++++- .../main/java/xyz/quaver/pupil/util/lock.kt | 70 +++++++++- app/src/main/res/layout/activity_lock.xml | 10 ++ app/src/main/res/values-ja/strings.xml | 9 ++ app/src/main/res/values-ko/strings.xml | 9 ++ app/src/main/res/values/strings.xml | 11 ++ app/src/main/res/xml/lock_preferences.xml | 26 ++++ 10 files changed, 325 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/xml/lock_preferences.xml diff --git a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt index 30f1ce1b..d8eee511 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt @@ -1,10 +1,17 @@ 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() { @@ -12,18 +19,67 @@ class LockActivity : AppCompatActivity() { 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 = { - Toast.makeText(context, it, Toast.LENGTH_SHORT).show() + 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() - - lock_pattern.isEnabled = false - lock_fingerprint.isEnabled = false - lock_password.isEnabled = false } } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 87cd9027..8b59da1c 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -1,6 +1,7 @@ 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 @@ -76,7 +77,8 @@ class MainActivity : AppCompatActivity() { private var mode = Mode.SEARCH - private val SETTINGS = 45162 + private val REQUEST_SETTINGS = 45162 + private val REQUEST_LOCK = 561 private var galleryIDs: Deferred>? = null private var totalItems = 0 @@ -90,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) @@ -143,6 +147,7 @@ class MainActivity : AppCompatActivity() { WindowManager.LayoutParams.FLAG_SECURE) else window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + super.onResume() } @@ -187,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() @@ -195,6 +200,10 @@ class MainActivity : AppCompatActivity() { loadBlocks() } } + REQUEST_LOCK -> { + if (resultCode != Activity.RESULT_OK) + finish() + } } } @@ -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() diff --git a/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt index 73a7197f..11d8a53b 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/PatternLockFragment.kt @@ -12,6 +12,7 @@ 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 { @@ -32,7 +33,7 @@ class PatternLockFragment : Fragment(), PatternLockViewListener { override fun onComplete(pattern: MutableList?) { val password = PatternLockUtils.patternToMD5(lock_pattern_view, pattern) - onPatternDrawn?.invoke(hash(password)) + onPatternDrawn?.invoke(password) } override fun onProgress(progressPattern: MutableList?) { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt index 850ed55d..1f995222 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt @@ -1,5 +1,7 @@ package xyz.quaver.pupil.ui +import android.app.Activity +import android.content.Intent import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -14,14 +16,17 @@ import androidx.appcompat.app.AppCompatActivity import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceManager -import io.fabric.sdk.android.BuildConfig 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) @@ -59,6 +64,24 @@ class SettingsActivity : AppCompatActivity() { "TB" //really? ) + override fun onResume() { + super.onResume() + + val lockManager = LockManager(context!!) + + findPreference("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 @@ -75,7 +98,7 @@ class SettingsActivity : AppCompatActivity() { setPreferencesFromResource(R.xml.root_preferences, rootKey) with(findPreference("app_version")) { - this ?: return@with + this!! val manager = context.packageManager val info = manager.getPackageInfo(context.packageName, 0) @@ -84,7 +107,7 @@ class SettingsActivity : AppCompatActivity() { } with(findPreference("delete_cache")) { - this ?: return@with + this!! val dir = File(context.cacheDir, "imageCache") @@ -108,7 +131,7 @@ class SettingsActivity : AppCompatActivity() { } with(findPreference("delete_downloads")) { - this ?: return@with + this!! val dir = context.getExternalFilesDir("Pupil") ?: return@with @@ -135,7 +158,7 @@ class SettingsActivity : AppCompatActivity() { } } with(findPreference("clear_history")) { - this ?: return@with + this!! val histories = (activity!!.application as Pupil).histories @@ -157,7 +180,7 @@ class SettingsActivity : AppCompatActivity() { } with(findPreference("default_query")) { - this ?: return@with + this!! val preferences = PreferenceManager.getDefaultSharedPreferences(context) @@ -272,7 +295,80 @@ class SettingsActivity : AppCompatActivity() { } } with(findPreference("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("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("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 + } } } } @@ -284,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) + } + } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/lock.kt b/app/src/main/java/xyz/quaver/pupil/util/lock.kt index fefcc698..13f87003 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/lock.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/lock.kt @@ -2,6 +2,11 @@ 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 { @@ -21,10 +26,20 @@ fun hashWithSalt(password: String): Pair { val source = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" -data class Lock(private val type: Type, private val hash: String, private val salt: String) { +@Serializable +data class Lock(val type: Type, val hash: String, val salt: String) { enum class Type { - PATTERN + 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 { @@ -34,6 +49,57 @@ data class Lock(private val type: Type, private val hash: String, private val sa class LockManager(base: Context): ContextWrapper(base) { + var locks: ArrayList? = 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 + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml index 6cb1e1c8..e4632044 100644 --- a/app/src/main/res/layout/activity_lock.xml +++ b/app/src/main/res/layout/activity_lock.xml @@ -31,6 +31,16 @@ app:backgroundTint="@color/colorPrimary" app:fabSize="mini"/> + + アプリロック アップロックの種類 バージョン + 生体認識 + ロック確認のためもう一回入力してください。 + 有効 + 指紋 + パスワード + パターン + ロックが一致しません。やり直してください。 + なし + ロックを無効にしますか? \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2ffdb6ec..d7c7956d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -70,4 +70,13 @@ 앱 잠금 앱 잠금 종류 앱 버전 + 생체 인식 + 잠금 확인을 위해 한번 더 입력해주세요 + 사용 중 + 지문 + 비밀번호 + 패턴 + 잠금이 일치하지 않습니다. 다시 시도하세요. + 없음 + 잠금을 해제할까요? \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b8fd3621..c8ef231d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -101,6 +101,17 @@ Enable security mode Enable security mode to make the screen invisible on recent app window + None + Pattern + PIN + Password + Biomatrics + Fingerprint + Enabled + Input same lock once more to confirm Lock + Do you want to remove lock? + Lock is different from last one. Please try again. + Set default query Language: Filter BL diff --git a/app/src/main/res/xml/lock_preferences.xml b/app/src/main/res/xml/lock_preferences.xml new file mode 100644 index 00000000..9fed9fba --- /dev/null +++ b/app/src/main/res/xml/lock_preferences.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + \ No newline at end of file From ad6c061b6bb57e5afa2d97c45c45c1ecf2888ca6 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sun, 23 Jun 2019 23:09:01 +0900 Subject: [PATCH 08/28] Fixed bug for older devices Hoping that the viewer crashing bug is fixed Version 2.11 --- app/build.gradle | 20 ++++----- app/release/output.json | 2 +- .../quaver/pupil/ExampleInstrumentedTest.kt | 3 ++ app/src/main/AndroidManifest.xml | 2 +- .../java/xyz/quaver/pupil/{ui => }/Pupil.kt | 21 +++++++-- .../pupil/adapters/GalleryBlockAdapter.kt | 45 ++++++++++++------- .../quaver/pupil/adapters/ReaderAdapter.kt | 38 +++++++++++++++- .../java/xyz/quaver/pupil/ui/LockActivity.kt | 4 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 1 + .../xyz/quaver/pupil/ui/ReaderActivity.kt | 3 ++ .../xyz/quaver/pupil/ui/SettingsActivity.kt | 4 +- .../quaver/pupil/util/GalleryDownloader.kt | 9 ++-- .../main/java/xyz/quaver/pupil/util/file.kt | 11 ++++- .../ic_progressbar_complete.xml | 18 -------- app/src/main/res/drawable/ic_progressbar.xml | 37 +++++++-------- .../res/drawable/ic_progressbar_complete.xml | 38 ++++++++++++++++ app/src/main/res/drawable/numeric.xml | 8 ++++ app/src/main/res/layout/activity_lock.xml | 8 ++-- app/src/main/res/layout/activity_reader.xml | 4 +- app/src/main/res/layout/item_galleryblock.xml | 10 ++--- app/src/main/res/layout/item_next.xml | 3 +- app/src/main/res/layout/item_prev.xml | 3 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- app/src/main/res/xml/root_preferences.xml | 2 +- .../java/xyz/quaver/hitomi/galleryblock.kt | 1 + 27 files changed, 203 insertions(+), 98 deletions(-) rename app/src/main/java/xyz/quaver/pupil/{ui => }/Pupil.kt (72%) delete mode 100644 app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml create mode 100644 app/src/main/res/drawable/ic_progressbar_complete.xml create mode 100644 app/src/main/res/drawable/numeric.xml diff --git a/app/build.gradle b/app/build.gradle index 97cb1eb5..64be68c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,10 +12,11 @@ android { applicationId "xyz.quaver.pupil" minSdkVersion 16 targetSdkVersion 29 - versionCode 17 - versionName "2.10-alpha" + versionCode 19 + versionName "2.11" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true + vectorDrawables.useSupportLibrary = true } buildTypes { release { @@ -37,23 +38,22 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1' implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0" - implementation 'com.android.support:multidex:1.0.3' implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.preference:preference:1.1.0-beta01' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.0.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'com.android.support:multidex:1.0.3' implementation 'com.google.android.material:material:1.0.0' 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.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' + implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' + implementation "ru.noties.markwon:core:${markwonVersion}" testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test:rules:1.2.0' diff --git a/app/release/output.json b/app/release/output.json index f084575a..6020ec9f 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"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":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":18,"versionName":"2.10.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt index facb9b50..f7decdb4 100644 --- a/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/xyz/quaver/pupil/ExampleInstrumentedTest.kt @@ -9,6 +9,7 @@ import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import xyz.quaver.hitomi.fetchNozomi import xyz.quaver.hiyobi.cookie import xyz.quaver.hiyobi.getReader import xyz.quaver.hiyobi.user_agent @@ -29,6 +30,8 @@ class ExampleInstrumentedTest { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext assertEquals("xyz.quaver.pupil", appContext.packageName) + + Log.d("Pupil", fetchNozomi().first.size.toString()) } @Test diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3e6686f..89e70b1e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ = Build.VERSION_CODES.O) { @@ -41,6 +54,8 @@ class Pupil : MultiDexApplication() { preference.edit().putBoolean("channel_created", true).apply() } + + super.onCreate() } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt index 9cf73343..68b23485 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -2,7 +2,8 @@ package xyz.quaver.pupil.adapters import android.app.AlertDialog import android.graphics.BitmapFactory -import android.graphics.drawable.Animatable +import android.graphics.drawable.Drawable +import android.util.Log import android.util.SparseBooleanArray import android.view.LayoutInflater import android.view.View @@ -12,6 +13,7 @@ import android.widget.LinearLayout import androidx.cardview.widget.CardView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView +import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* @@ -24,7 +26,7 @@ import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.list import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.ReaderItem -import xyz.quaver.pupil.ui.Pupil +import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.util.Histories @@ -68,7 +70,7 @@ class GalleryBlockAdapter(private val galleries: List favorites.add(galleryBlock.id) - else -> favorites.remove(galleryBlock.id) - } - } - setOnCheckedChangeListener { _, isChecked -> - when { - isChecked -> (background as Animatable).start() - else -> background = AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star) + favorites.contains(galleryBlock.id) -> { + favorites.remove(galleryBlock.id) + + setImageResource(R.drawable.ic_star_empty) + } + else -> { + favorites.add(galleryBlock.id) + + setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star).apply { + this ?: return@apply + + registerAnimationCallback(object: Animatable2Compat.AnimationCallback() { + override fun onAnimationEnd(drawable: Drawable?) { + setImageResource(R.drawable.ic_star_filled) + } + }) + start() + }) + } } } } diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt index e75617c4..991828de 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -1,6 +1,8 @@ package xyz.quaver.pupil.adapters +import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -10,6 +12,8 @@ import xyz.quaver.pupil.R class ReaderAdapter(private val images: List) : RecyclerView.Adapter() { + var isFullScreen = false + class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -21,19 +25,49 @@ class ReaderAdapter(private val images: List) : RecyclerView.Adapter reqHeight || width > reqWidth) { + + val halfHeight: Int = height / 2 + val halfWidth: Int = width / 2 + + // Calculate the largest inSampleSize value that is a power of 2 and keeps both + // height and width larger than the requested height and width. + while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { + inSampleSize *= 2 + } + } + + return inSampleSize + } + with(holder.view as ImageView) { val options = BitmapFactory.Options() options.inJustDecodeBounds = true BitmapFactory.decodeFile(images[position], options) - options.inSampleSize = options.outWidth / - context.resources.displayMetrics.widthPixels + val (reqWidth, reqHeight) = context.resources.displayMetrics.let { + Pair(it.widthPixels, it.heightPixels) + } + + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) + + options.inPreferredConfig = Bitmap.Config.RGB_565 options.inJustDecodeBounds = false val image = BitmapFactory.decodeFile(images[position], options) + Log.d("Pupil", image.byteCount.toString()) + Log.d("Pupil", "${image.width}x${image.height}") + Log.d("Pupil", "deviceWidth ${context.resources.displayMetrics.widthPixels}") + setImageBitmap(image) } } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt index d8eee511..57f9ed9a 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt @@ -1,14 +1,12 @@ package xyz.quaver.pupil.ui import android.app.Activity -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle -import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity 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 diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 8b59da1c..a19254a9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -42,6 +42,7 @@ import kotlinx.serialization.stringify import ru.noties.markwon.Markwon import xyz.quaver.hitomi.* import xyz.quaver.pupil.BuildConfig +import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.adapters.GalleryBlockAdapter import xyz.quaver.pupil.types.Tag diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index 990f7b50..e29b3111 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -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.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.adapters.ReaderAdapter import xyz.quaver.pupil.util.GalleryDownloader @@ -46,6 +47,8 @@ class ReaderActivity : AppCompatActivity() { set(value) { field = value + (reader_recyclerview.adapter as ReaderAdapter).isFullScreen = value + reader_progressbar.visibility = when { value -> View.VISIBLE else -> View.GONE diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt index 1f995222..e4229365 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt @@ -17,10 +17,12 @@ 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.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tags import xyz.quaver.pupil.util.Lock import xyz.quaver.pupil.util.LockManager +import xyz.quaver.pupil.util.getDownloadDirectory import java.io.File class SettingsActivity : AppCompatActivity() { @@ -133,7 +135,7 @@ class SettingsActivity : AppCompatActivity() { with(findPreference("delete_downloads")) { this!! - val dir = context.getExternalFilesDir("Pupil") ?: return@with + val dir = getDownloadDirectory(context)!! summary = getDirSize(dir) diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt index fda31db8..9be00515 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -4,7 +4,6 @@ import android.app.PendingIntent import android.content.Context import android.content.ContextWrapper import android.content.Intent -import android.util.Log import android.util.SparseArray import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat @@ -18,8 +17,8 @@ import kotlinx.serialization.list import xyz.quaver.hitomi.* import xyz.quaver.hiyobi.cookie import xyz.quaver.hiyobi.user_agent -import xyz.quaver.pupil.ui.Pupil import xyz.quaver.pupil.R +import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.ui.ReaderActivity import java.io.File import java.io.FileOutputStream @@ -218,7 +217,7 @@ class GalleryDownloader( if (download) { File(cacheDir, "imageCache/${galleryBlock.id}").let { if (it.exists()) { - val target = File(getExternalFilesDir("Pupil"), galleryBlock.id.toString()) + val target = File(getDownloadDirectory(this@GalleryDownloader), galleryBlock.id.toString()) if (!target.exists()) target.mkdirs() @@ -230,10 +229,10 @@ class GalleryDownloader( notificationManager.notify(galleryBlock.id, notificationBuilder.build()) - onCompleteHandler?.invoke() - download = false } + + onCompleteHandler?.invoke() } remove(galleryBlock.id) diff --git a/app/src/main/java/xyz/quaver/pupil/util/file.kt b/app/src/main/java/xyz/quaver/pupil/util/file.kt index f351dcd2..a1823442 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/file.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/file.kt @@ -1,15 +1,24 @@ package xyz.quaver.pupil.util import android.content.Context +import android.os.Build import android.os.Environment import android.provider.MediaStore +import androidx.core.content.ContextCompat import java.io.File fun getCachedGallery(context: Context, galleryID: Int): File { - return File(context.getExternalFilesDir("Pupil"), galleryID.toString()).let { + return File(getDownloadDirectory(context), galleryID.toString()).let { when { it.exists() -> it else -> File(context.cacheDir, "imageCache/$galleryID") } } +} + +fun getDownloadDirectory(context: Context): File? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + context.getExternalFilesDir("Pupil") + else + File(Environment.getExternalStorageDirectory(), "Pupil") } \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml b/app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml deleted file mode 100644 index 85cae265..00000000 --- a/app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/ic_progressbar.xml b/app/src/main/res/drawable/ic_progressbar.xml index 3899286b..17f172e9 100644 --- a/app/src/main/res/drawable/ic_progressbar.xml +++ b/app/src/main/res/drawable/ic_progressbar.xml @@ -1,21 +1,22 @@ - - +xmlns:android="http://schemas.android.com/apk/res/android" +android:name="vector" +android:width="24dp" +android:height="24dp" +android:viewportWidth="24" +android:viewportHeight="24" +android:tintMode="multiply"> + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_progressbar_complete.xml b/app/src/main/res/drawable/ic_progressbar_complete.xml new file mode 100644 index 00000000..5fbef6ac --- /dev/null +++ b/app/src/main/res/drawable/ic_progressbar_complete.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/numeric.xml b/app/src/main/res/drawable/numeric.xml new file mode 100644 index 00000000..fc54c34a --- /dev/null +++ b/app/src/main/res/drawable/numeric.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml index e4632044..b570d386 100644 --- a/app/src/main/res/layout/activity_lock.xml +++ b/app/src/main/res/layout/activity_lock.xml @@ -27,7 +27,7 @@ android:id="@+id/lock_pattern" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/lock_pattern" + app:srcCompat="@drawable/lock_pattern" app:backgroundTint="@color/colorPrimary" app:fabSize="mini"/> @@ -35,7 +35,7 @@ android:id="@+id/lock_pin" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/numeric" + app:srcCompat="@drawable/numeric" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" app:backgroundTint="@color/dark_gray" @@ -45,7 +45,7 @@ android:id="@+id/lock_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/fingerprint" + app:srcCompat="@drawable/fingerprint" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" app:backgroundTint="@color/dark_gray" @@ -55,7 +55,7 @@ android:id="@+id/lock_password" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/lastpass" + app:srcCompat="@drawable/lastpass" app:backgroundTint="@color/dark_gray" app:fabSize="mini"/> diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index b1cd6c1d..789f04f3 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -55,7 +55,7 @@ android:id="@+id/reader_fab_download" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/ic_downloading" + app:srcCompat="@drawable/ic_downloading" app:fab_label="@string/reader_fab_download" app:fab_size="mini"/> @@ -63,7 +63,7 @@ android:id="@+id/reader_fab_fullscreen" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/ic_fullscreen" + app:srcCompat="@drawable/ic_fullscreen" app:fab_label="@string/reader_fab_fullscreen" app:fab_size="mini"/> diff --git a/app/src/main/res/layout/item_galleryblock.xml b/app/src/main/res/layout/item_galleryblock.xml index 7e05f4c2..7fbe3d7d 100644 --- a/app/src/main/res/layout/item_galleryblock.xml +++ b/app/src/main/res/layout/item_galleryblock.xml @@ -158,15 +158,13 @@ android:layout_height="1dp" android:layout_weight="1"/> - + app:srcCompat="@drawable/ic_star_empty" + app:backgroundTint="@color/material_orange_500"/> diff --git a/app/src/main/res/layout/item_next.xml b/app/src/main/res/layout/item_next.xml index ec429ad8..4b647b6c 100644 --- a/app/src/main/res/layout/item_next.xml +++ b/app/src/main/res/layout/item_next.xml @@ -1,5 +1,6 @@ diff --git a/app/src/main/res/layout/item_prev.xml b/app/src/main/res/layout/item_prev.xml index c01cc5da..60299e63 100644 --- a/app/src/main/res/layout/item_prev.xml +++ b/app/src/main/res/layout/item_prev.xml @@ -1,5 +1,6 @@ diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index bdab1daf..8ea7ac44 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -7,7 +7,7 @@ 検索 ギャラリー検索 ギャラリー検索 - イメージキャッシュクリア + キャッシュクリア キャッシュをクリアするとイメージのロード速度に影響を与えます。実行しますか? サイズ: %1$d%2$s デフォルトキーワード diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d7c7956d..a1fe7f96 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -6,7 +6,7 @@ 갤러리 검색 갤러리 검색 기본 검색어 - 이미지 캐시 정리하기 + 캐시 정리하기 캐시를 정리하면 이미지 로딩속도가 느려질 수 있습니다. 계속하시겠습니까? 사용량: %1$d%2$s 한 번에 로드할 갤러리 수 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8ef231d..3a2738d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -85,7 +85,7 @@ Galleries per page Default query Storage - Clear image cache + Clear cache Deleting cache can affect image loading speed. Do you want to continue? Currently using %1$d%2$s Clear downloads diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 5a802f27..e823e4fb 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -29,7 +29,7 @@ app:title="@string/settings_storage"> Date: Sun, 23 Jun 2019 23:32:38 +0900 Subject: [PATCH 09/28] Fixed app icon Version 2.11.1 --- .idea/misc.xml | 2 +- app/build.gradle | 4 +- app/release/output.json | 2 +- .../quaver/pupil/adapters/ReaderAdapter.kt | 4 - .../drawable-v24/ic_launcher_foreground.xml | 34 --------- .../res/drawable/ic_launcher_background.xml | 74 ------------------- .../res/drawable/ic_launcher_foreground.xml | 17 ----- .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 -- .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 -- 9 files changed, 4 insertions(+), 143 deletions(-) delete mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 7631aec3..84da703c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 64be68c8..989ed597 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "xyz.quaver.pupil" minSdkVersion 16 targetSdkVersion 29 - versionCode 19 - versionName "2.11" + versionCode 20 + versionName "2.11.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true diff --git a/app/release/output.json b/app/release/output.json index 6020ec9f..de58b687 100644 --- a/app/release/output.json +++ b/app/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":18,"versionName":"2.10.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":20,"versionName":"2.11.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt index 991828de..d52a1cab 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -64,10 +64,6 @@ class ReaderAdapter(private val images: List) : RecyclerView.Adapter - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index a0ad202f..00000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 5b8f761d..00000000 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 7353dbd1..00000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 7353dbd1..00000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file From bd4b61d7ac61f56be4da841c9ff2ba73e5430fef Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sun, 30 Jun 2019 22:04:35 +0900 Subject: [PATCH 10/28] Utilizing Glide Fixed Reader FAB icon Changed to use gallery id instead of galleryblock to open Reader --- .idea/misc.xml | 2 +- app/build.gradle | 6 ++ .../pupil/adapters/GalleryBlockAdapter.kt | 36 +++---- .../quaver/pupil/adapters/ReaderAdapter.kt | 56 +++------- .../quaver/pupil/types/SelectorSuggestion.kt | 13 +++ .../xyz/quaver/pupil/types/TagSuggestion.kt | 2 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 58 +++++----- .../xyz/quaver/pupil/ui/ReaderActivity.kt | 70 ++++++------ .../quaver/pupil/util/GalleryDownloader.kt | 102 ++++++++++-------- app/src/main/res/layout/item_reader.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../src/main/java/xyz/quaver/hitomi/reader.kt | 72 +++++++++++++ .../main/java/xyz/quaver/hitomi/readers.kt | 57 ---------- .../src/main/java/xyz/quaver/hiyobi/reader.kt | 51 ++++----- 16 files changed, 274 insertions(+), 255 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt create mode 100644 libpupil/src/main/java/xyz/quaver/hitomi/reader.kt delete mode 100644 libpupil/src/main/java/xyz/quaver/hitomi/readers.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 84da703c..7631aec3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 989ed597..9b1f14c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlinx-serialization' apply plugin: 'com.google.gms.google-services' @@ -52,8 +53,13 @@ dependencies { implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.clans:fab:1.6.4' + implementation 'com.github.bumptech.glide:glide:4.9.0' + implementation ("com.github.bumptech.glide:recyclerview-integration:4.9.0") { + transitive = false + } implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' implementation "ru.noties.markwon:core:${markwonVersion}" + kapt 'com.github.bumptech.glide:compiler:4.9.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test:rules:1.2.0' diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt index 68b23485..bf314f69 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -3,7 +3,6 @@ package xyz.quaver.pupil.adapters import android.app.AlertDialog import android.graphics.BitmapFactory import android.graphics.drawable.Drawable -import android.util.Log import android.util.SparseBooleanArray import android.view.LayoutInflater import android.view.View @@ -15,6 +14,8 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.coroutines.CoroutineScope @@ -23,9 +24,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.list import xyz.quaver.hitomi.GalleryBlock -import xyz.quaver.hitomi.ReaderItem +import xyz.quaver.hitomi.Reader import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tag @@ -47,8 +47,8 @@ class GalleryBlockAdapter(private val galleries: List>) { + inner class GalleryViewHolder(val view: CardView) : RecyclerView.ViewHolder(view) { + fun bind(holder: GalleryViewHolder, item: Pair>) { with(view) { val resources = context.resources val languages = resources.getStringArray(R.array.languages).map { @@ -62,17 +62,15 @@ class GalleryBlockAdapter(private val galleries: List) : RecyclerView.Adapter() { @@ -25,47 +25,19 @@ class ReaderAdapter(private val images: List) : RecyclerView.Adapter reqHeight || width > reqWidth) { - - val halfHeight: Int = height / 2 - val halfWidth: Int = width / 2 - - // Calculate the largest inSampleSize value that is a power of 2 and keeps both - // height and width larger than the requested height and width. - while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { - inSampleSize *= 2 - } - } - - return inSampleSize + val progressDrawable = CircularProgressDrawable(holder.view.context).apply { + strokeWidth = 10f + centerRadius = 100f + start() } - with(holder.view as ImageView) { - val options = BitmapFactory.Options() - - options.inJustDecodeBounds = true - BitmapFactory.decodeFile(images[position], options) - - val (reqWidth, reqHeight) = context.resources.displayMetrics.let { - Pair(it.widthPixels, it.heightPixels) - } - - options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) - - options.inPreferredConfig = Bitmap.Config.RGB_565 - - options.inJustDecodeBounds = false - - val image = BitmapFactory.decodeFile(images[position], options) - - setImageBitmap(image) - } + Glide.with(holder.view) + .load(images[position]) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .placeholder(progressDrawable) + .error(R.drawable.image_broken_variant) + .into(holder.view as ImageView) } override fun getItemCount() = images.size diff --git a/app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt b/app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt new file mode 100644 index 00000000..a350228c --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/types/SelectorSuggestion.kt @@ -0,0 +1,13 @@ +package xyz.quaver.pupil.types + +import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion +import kotlinx.android.parcel.Parcelize +import xyz.quaver.hitomi.Suggestion + +@Parcelize +class SelectorSuggestion : SearchSuggestion { + + override fun getBody(): String { + return "" + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt b/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt index 2b39db9f..f46a83a0 100644 --- a/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt +++ b/app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt @@ -5,7 +5,7 @@ import kotlinx.android.parcel.Parcelize import xyz.quaver.hitomi.Suggestion @Parcelize -data class TagSuggestion constructor(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion { +data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion { constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n) override fun getBody(): String { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index a19254a9..084020b1 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -55,6 +55,8 @@ import java.net.URL import java.util.* import javax.net.ssl.HttpsURLConnection import kotlin.collections.ArrayList +import kotlin.math.abs +import kotlin.math.ceil import kotlin.math.min import kotlin.math.roundToInt @@ -72,8 +74,10 @@ class MainActivity : AppCompatActivity() { private var query = "" set(value) { field = value - findViewById(R.id.search_bar_text) - .setText(query, TextView.BufferType.EDITABLE) + with(findViewById(R.id.search_bar_text)) { + if (text.toString() != value) + setText(query, TextView.BufferType.EDITABLE) + } } private var mode = Mode.SEARCH @@ -155,7 +159,7 @@ class MainActivity : AppCompatActivity() { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { val preference = PreferenceManager.getDefaultSharedPreferences(this) val perPage = preference.getString("per_page", "25")!!.toInt() - val maxPage = Math.ceil(totalItems / perPage.toDouble()).roundToInt() + val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt() return when(keyCode) { KeyEvent.KEYCODE_VOLUME_DOWN -> { @@ -380,9 +384,9 @@ class MainActivity : AppCompatActivity() { val intent = Intent(this@MainActivity, ReaderActivity::class.java) val gallery = galleries[position].first - intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)) + intent.putExtra("galleryID", gallery.id) - //TODO: Maybe sprinke some transitions will be nice :D + //TODO: Maybe sprinkling some transitions will be nice :D startActivity(intent) histories.add(gallery.id) @@ -391,7 +395,7 @@ class MainActivity : AppCompatActivity() { if (v !is CardView) return@setOnItemLongClickListener true - val galleryBlock = galleries[position].first + val gallery = galleries[position].first val view = LayoutInflater.from(this@MainActivity) .inflate(R.layout.dialog_galleryblock, recyclerView, false) @@ -400,15 +404,15 @@ class MainActivity : AppCompatActivity() { }.create() with(view.main_dialog_download) { - text = when(GalleryDownloader.get(galleryBlock.id)) { + text = when(GalleryDownloader.get(gallery.id)) { null -> getString(R.string.reader_fab_download) else -> getString(R.string.reader_fab_download_cancel) } - isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(galleryBlock.id, false) + isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(gallery.id, false) setOnClickListener { - val downloader = GalleryDownloader.get(galleryBlock.id) + val downloader = GalleryDownloader.get(gallery.id) if (downloader == null) - GalleryDownloader(context, galleryBlock, true).start() + GalleryDownloader(context, gallery.id, true).start() else { downloader.cancel() downloader.clearNotification() @@ -420,16 +424,16 @@ class MainActivity : AppCompatActivity() { view.main_dialog_delete.setOnClickListener { CoroutineScope(Dispatchers.Default).launch { - with(GalleryDownloader[galleryBlock.id]) { + with(GalleryDownloader[gallery.id]) { this?.cancelAndJoin() this?.clearNotification() } - val cache = File(cacheDir, "imageCache/${galleryBlock.id}") - val data = getCachedGallery(context, galleryBlock.id) + val cache = File(cacheDir, "imageCache/${gallery.id}") + val data = getCachedGallery(context, gallery.id) cache.deleteRecursively() data.deleteRecursively() - downloads.remove(galleryBlock.id) + downloads.remove(gallery.id) if (mode == Mode.DOWNLOAD) { runOnUiThread { @@ -440,7 +444,7 @@ class MainActivity : AppCompatActivity() { } } - (adapter as GalleryBlockAdapter).completeFlag.put(galleryBlock.id, false) + (adapter as GalleryBlockAdapter).completeFlag.put(gallery.id, false) } dialog.dismiss() } @@ -583,7 +587,7 @@ class MainActivity : AppCompatActivity() { //BOTTOM //Scrolling DOWN - if (dist < 0 && currentPage != Math.ceil(totalItems.toDouble()/perPage).roundToInt()-1) { + if (dist < 0 && currentPage != ceil(totalItems.toDouble()/perPage).roundToInt()-1) { with(main_recyclerview.adapter as GalleryBlockAdapter) { if(!showNext) { showNext = true @@ -595,7 +599,7 @@ class MainActivity : AppCompatActivity() { getChildAt(childCount-1) } - val absDist = Math.abs(dist) + val absDist = abs(dist) if (next is LinearLayout) { val icon = next.findViewById(R.id.icon_next) @@ -701,7 +705,7 @@ class MainActivity : AppCompatActivity() { setMessage(getString( R.string.main_jump_message, currentPage+1, - Math.ceil(totalItems / perPage.toDouble()).roundToInt() + ceil(totalItems / perPage.toDouble()).roundToInt() )) setPositiveButton(android.R.string.ok) { _, _ -> @@ -729,10 +733,7 @@ class MainActivity : AppCompatActivity() { val intent = Intent(this@MainActivity, ReaderActivity::class.java) val gallery = getGalleryBlock(editText.text.toString().toInt()) ?: throw Exception() - intent.putExtra( - "galleryblock", - Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery) - ) + intent.putExtra("galleryID", gallery.id) startActivity(intent) } catch (e: Exception) { @@ -747,10 +748,17 @@ class MainActivity : AppCompatActivity() { } setOnQueryChangeListener { _, query -> + this@MainActivity.query = query + clearSuggestions() - if (query.isEmpty() or query.endsWith(' ')) + if (query.isEmpty() or query.endsWith(' ')) { + swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { + TagSuggestion(it.tag, -1, "", it.area ?: "tag") + }) + return@setOnQueryChangeListener + } val currentQuery = query.split(" ").last().replace('_', ' ') @@ -852,8 +860,6 @@ class MainActivity : AppCompatActivity() { delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length) append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ") } - - clearSuggestions() } override fun onSearchAction(currentQuery: String?) { @@ -863,7 +869,7 @@ class MainActivity : AppCompatActivity() { setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener { override fun onFocus() { - if (searchInputView.text.isEmpty()) + if (query.isEmpty() or query.endsWith(' ')) swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { TagSuggestion(it.tag, -1, "", it.area ?: "tag") }) diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index e29b3111..6bd102b9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.view.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.PagerSnapHelper @@ -21,13 +22,8 @@ import kotlinx.android.synthetic.main.dialog_numberpicker.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.io.IOException import kotlinx.serialization.ImplicitReflectionSerializer -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonConfiguration -import xyz.quaver.hitomi.GalleryBlock -import xyz.quaver.hitomi.getGalleryBlock import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.adapters.ReaderAdapter @@ -37,8 +33,8 @@ import xyz.quaver.pupil.util.ItemClickSupport class ReaderActivity : AppCompatActivity() { + private var galleryID = 0 private val images = ArrayList() - private lateinit var galleryBlock: GalleryBlock private var gallerySize = 0 private var currentPage = 0 @@ -66,6 +62,9 @@ class ReaderActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + title = getString(R.string.reader_loading) + supportActionBar?.setDisplayHomeAsUpEnabled(false) + favorites = (application as Pupil).favorites window.setFlags( @@ -76,16 +75,13 @@ class ReaderActivity : AppCompatActivity() { handleIntent(intent) - Crashlytics.setInt("GalleryID", galleryBlock.id) + Crashlytics.setInt("GalleryID", galleryID) - if (!::galleryBlock.isInitialized) { + if (galleryID == 0) { onBackPressed() return } - supportActionBar?.title = galleryBlock.title - supportActionBar?.setDisplayHomeAsUpEnabled(false) - initDownloader() initView() @@ -106,25 +102,16 @@ class ReaderActivity : AppCompatActivity() { if (uri != null && lastPathSegment != null) { val nonNumber = Regex("[^-?0-9]+") - val galleryID = when (uri.host) { + galleryID = when (uri.host) { "hitomi.la" -> lastPathSegment.replace(nonNumber, "").toInt() "히요비.asia" -> lastPathSegment.toInt() "xn--9w3b15m8vo.asia" -> lastPathSegment.toInt() "e-hentai.org" -> uri.pathSegments[1].toInt() else -> return } - - runBlocking { - CoroutineScope(Dispatchers.IO).launch { - galleryBlock = getGalleryBlock(galleryID) ?: return@launch - }.join() - } } } else { - galleryBlock = Json(JsonConfiguration.Stable).parse( - GalleryBlock.serializer(), - intent.getStringExtra("galleryblock")!! - ) + galleryID = intent.getIntExtra("galleryID", 0) } } @@ -148,7 +135,7 @@ class ReaderActivity : AppCompatActivity() { with(menu?.findItem(R.id.reader_menu_favorite)) { this ?: return@with - if (favorites.contains(galleryBlock.id)) + if (favorites.contains(galleryID)) (icon as Animatable).start() } @@ -176,7 +163,7 @@ class ReaderActivity : AppCompatActivity() { dialog.show() } R.id.reader_menu_favorite -> { - val id = galleryBlock.id + val id = galleryID val favorite = menu?.findItem(R.id.reader_menu_favorite) ?: return true if (favorites.contains(id)) { @@ -215,11 +202,11 @@ class ReaderActivity : AppCompatActivity() { } private fun initDownloader() { - var d: GalleryDownloader? = GalleryDownloader.get(galleryBlock.id) + var d: GalleryDownloader? = GalleryDownloader.get(galleryID) if (d == null) { try { - d = GalleryDownloader(this, galleryBlock) + d = GalleryDownloader(this, galleryID) } catch (e: IOException) { Snackbar.make(reader_layout, R.string.unable_to_connect, Snackbar.LENGTH_LONG).show() finish() @@ -230,17 +217,18 @@ class ReaderActivity : AppCompatActivity() { downloader = d.apply { onReaderLoadedHandler = { CoroutineScope(Dispatchers.Main).launch { + title = it.title with(reader_download_progressbar) { - max = it.size + max = it.readerItems.size progress = 0 } with(reader_progressbar) { - max = it.size + max = it.readerItems.size progress = 0 } - gallerySize = it.size - menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.size}" + gallerySize = it.readerItems.size + menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.readerItems.size}" } } onProgressHandler = { @@ -341,18 +329,24 @@ class ReaderActivity : AppCompatActivity() { } } - reader_fab_fullscreen.setOnClickListener { - isFullscreen = true - fullscreen(isFullscreen) + with(reader_fab_download) { + setImageResource(R.drawable.ic_download) + setOnClickListener { + downloader.download = !downloader.download - reader_fab.close(true) + if (!downloader.download) + downloader.clearNotification() + } } - reader_fab_download.setOnClickListener { - downloader.download = !downloader.download + with(reader_fab_fullscreen) { + setImageResource(R.drawable.ic_fullscreen) + setOnClickListener { + isFullscreen = true + fullscreen(isFullscreen) - if (!downloader.download) - downloader.clearNotification() + this@ReaderActivity.reader_fab.close(true) + } } } diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt index 9be00515..53a46770 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -13,12 +13,13 @@ import kotlinx.coroutines.* import kotlinx.io.IOException import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.list -import xyz.quaver.hitomi.* +import xyz.quaver.hitomi.Reader +import xyz.quaver.hitomi.getReader +import xyz.quaver.hitomi.getReferer import xyz.quaver.hiyobi.cookie import xyz.quaver.hiyobi.user_agent -import xyz.quaver.pupil.R import xyz.quaver.pupil.Pupil +import xyz.quaver.pupil.R import xyz.quaver.pupil.ui.ReaderActivity import java.io.File import java.io.FileOutputStream @@ -30,7 +31,7 @@ import kotlin.concurrent.schedule class GalleryDownloader( base: Context, - private val galleryBlock: GalleryBlock, + private val galleryID: Int, _notify: Boolean = false ) : ContextWrapper(base) { @@ -41,10 +42,10 @@ class GalleryDownloader( set(value) { if (value) { field = true - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + notificationManager.notify(galleryID, notificationBuilder.build()) - val data = getCachedGallery(this, galleryBlock.id) - val cache = File(cacheDir, "imageCache/${galleryBlock.id}") + val data = getCachedGallery(this, galleryID) + val cache = File(cacheDir, "imageCache/$galleryID") if (File(cache, "images").exists() && !data.exists()) { cache.copyRecursively(data, true) @@ -54,7 +55,7 @@ class GalleryDownloader( if (reader?.isActive == false && downloadJob?.isActive != true) field = false - downloads.add(galleryBlock.id) + downloads.add(galleryID) } else { field = false } @@ -78,24 +79,24 @@ class GalleryDownloader( companion object : SparseArray() init { - put(galleryBlock.id, this) + put(galleryID, this) initNotification() reader = CoroutineScope(Dispatchers.IO).async { download = _notify val json = Json(JsonConfiguration.Stable) - val serializer = ReaderItem.serializer().list + val serializer = Reader.serializer() //Check cache - val cache = File(getCachedGallery(this@GalleryDownloader, galleryBlock.id), "reader.json") + val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "reader.json") if (cache.exists()) { val cached = json.parse(serializer, cache.readText()) - if (cached.isNotEmpty()) { + if (cached.readerItems.isNotEmpty()) { useHiyobi = when { - cached.first().url.contains("hitomi.la") -> false + cached.readerItems[0].url.contains("hitomi.la") -> false else -> true } @@ -108,22 +109,22 @@ class GalleryDownloader( //Cache doesn't exist. Load from internet val reader = when { useHiyobi -> { - xyz.quaver.hiyobi.getReader(galleryBlock.id).let { + xyz.quaver.hiyobi.getReader(galleryID).let { when { - it.isEmpty() -> { + it.readerItems.isEmpty() -> { useHiyobi = false - getReader(galleryBlock.id) + getReader(galleryID) } else -> it } } } else -> { - getReader(galleryBlock.id) + getReader(galleryID) } } - if (reader.isNotEmpty()) { + if (reader.readerItems.isNotEmpty()) { //Save cache if (cache.parentFile?.exists() == false) cache.parentFile!!.mkdirs() @@ -141,7 +142,7 @@ class GalleryDownloader( downloadJob = CoroutineScope(Dispatchers.Default).launch { val reader = reader!!.await() - if (reader.isEmpty()) + if (reader.readerItems.isEmpty()) onErrorHandler?.invoke(IOException("Couldn't retrieve Reader")) val list = ArrayList() @@ -149,29 +150,20 @@ class GalleryDownloader( onReaderLoadedHandler?.invoke(reader) notificationBuilder - .setProgress(reader.size, 0, false) - .setContentText("0/${reader.size}") + .setProgress(reader.readerItems.size, 0, false) + .setContentText("0/${reader.readerItems.size}") - reader.chunked(4).forEachIndexed { chunkIndex, chunked -> + reader.readerItems.chunked(4).forEachIndexed { chunkIndex, chunked -> chunked.mapIndexed { i, it -> val index = chunkIndex*4+i - onProgressHandler?.invoke(index) - - notificationBuilder - .setProgress(reader.size, index, false) - .setContentText("$index/${reader.size}") - - if (download) - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) - async(Dispatchers.IO) { val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url val name = "$index".padStart(4, '0') val ext = url.split('.').last() - val cache = File(getCachedGallery(this@GalleryDownloader, galleryBlock.id), "images/$name.$ext") + val cache = File(getCachedGallery(this@GalleryDownloader, galleryID), "images/$name.$ext") if (!cache.exists()) try { @@ -180,7 +172,7 @@ class GalleryDownloader( setRequestProperty("User-Agent", user_agent) setRequestProperty("Cookie", cookie) } else - setRequestProperty("Referer", getReferer(galleryBlock.id)) + setRequestProperty("Referer", getReferer(galleryID)) if (cache.parentFile?.exists() == false) cache.parentFile!!.mkdirs() @@ -193,31 +185,43 @@ class GalleryDownloader( onErrorHandler?.invoke(e) notificationBuilder - .setContentTitle(galleryBlock.title) + .setContentTitle(reader.title) .setContentText(getString(R.string.reader_notification_error)) .setProgress(0, 0, false) - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + notificationManager.notify(galleryID, notificationBuilder.build()) } cache.absolutePath } }.forEach { list.add(it.await()) + + val index = list.size + + onProgressHandler?.invoke(index) + + notificationBuilder + .setProgress(reader.readerItems.size, index, false) + .setContentText("$index/${reader.readerItems.size}") + + if (download) + notificationManager.notify(galleryID, notificationBuilder.build()) + onDownloadedHandler?.invoke(list) } } Timer(false).schedule(1000) { notificationBuilder - .setContentTitle(galleryBlock.title) + .setContentTitle(reader.title) .setContentText(getString(R.string.reader_notification_complete)) .setProgress(0, 0, false) if (download) { - File(cacheDir, "imageCache/${galleryBlock.id}").let { + File(cacheDir, "imageCache/${galleryID}").let { if (it.exists()) { - val target = File(getDownloadDirectory(this@GalleryDownloader), galleryBlock.id.toString()) + val target = File(getDownloadDirectory(this@GalleryDownloader), galleryID.toString()) if (!target.exists()) target.mkdirs() @@ -227,7 +231,7 @@ class GalleryDownloader( } } - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + notificationManager.notify(galleryID, notificationBuilder.build()) download = false } @@ -235,20 +239,20 @@ class GalleryDownloader( onCompleteHandler?.invoke() } - remove(galleryBlock.id) + remove(galleryID) } } fun cancel() { downloadJob?.cancel() - remove(galleryBlock.id) + remove(galleryID) } suspend fun cancelAndJoin() { downloadJob?.cancelAndJoin() - remove(galleryBlock.id) + remove(galleryID) } fun invokeOnReaderLoaded() { @@ -258,7 +262,7 @@ class GalleryDownloader( } fun clearNotification() { - notificationManager.cancel(galleryBlock.id) + notificationManager.cancel(galleryID) } fun invokeOnNotifyChanged() { @@ -267,22 +271,28 @@ class GalleryDownloader( private fun initNotification() { val intent = Intent(this, ReaderActivity::class.java).apply { - putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), galleryBlock)) + putExtra("galleryID", galleryID) } val pendingIntent = TaskStackBuilder.create(this).run { addNextIntentWithParentStack(intent) getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) } + notificationManager = NotificationManagerCompat.from(this) + notificationBuilder = NotificationCompat.Builder(this, "download").apply { - setContentTitle(galleryBlock.title) + setContentTitle(getString(R.string.reader_loading)) setContentText(getString(R.string.reader_notification_text)) setSmallIcon(R.drawable.ic_download) setContentIntent(pendingIntent) setProgress(0, 0, true) priority = NotificationCompat.PRIORITY_LOW } - notificationManager = NotificationManagerCompat.from(this) + + CoroutineScope(Dispatchers.Default).launch { + while (reader == null) ; + notificationBuilder.setContentTitle(reader.await().title) + } } } \ No newline at end of file diff --git a/app/src/main/res/layout/item_reader.xml b/app/src/main/res/layout/item_reader.xml index 9f5abe22..eb9909bd 100644 --- a/app/src/main/res/layout/item_reader.xml +++ b/app/src/main/res/layout/item_reader.xml @@ -3,6 +3,7 @@ android:contentDescription="@string/reader_imageview_description" android:layout_width="match_parent" android:layout_height="wrap_content" + android:minHeight="100dp" android:paddingBottom="8dp" android:scaleType="fitCenter" android:adjustViewBounds="true"/> \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 8ea7ac44..8e0c1660 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -79,4 +79,5 @@ ロックが一致しません。やり直してください。 なし ロックを無効にしますか? + ロード中 \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index a1fe7f96..8fe85791 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -79,4 +79,5 @@ 잠금이 일치하지 않습니다. 다시 시도하세요. 없음 잠금을 해제할까요? + 로딩중 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3a2738d3..3bfb2b16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -71,6 +71,7 @@ Type: %1$s Language: %1$s + Loading Go to page Fullscreen Background download diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt b/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt new file mode 100644 index 00000000..ef73d8da --- /dev/null +++ b/libpupil/src/main/java/xyz/quaver/hitomi/reader.kt @@ -0,0 +1,72 @@ +package xyz.quaver.hitomi + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import kotlinx.serialization.list +import org.jsoup.Jsoup +import xyz.quaver.hiyobi.HiyobiReader +import java.net.URL + +fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html" +fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp" + +fun webpReaderFromReader(reader: Reader) : Reader { + if (reader is HiyobiReader) + return reader + + return Reader(reader.title, reader.readerItems.map { + ReaderItem( + if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url, + it.galleryInfo + ) + }) +} + +@Serializable +data class GalleryInfo( + val width: Int, + val haswebp: Int, + val name: String, + val height: Int +) +@Serializable +data class ReaderItem( + val url: String, + val galleryInfo: GalleryInfo? +) + +@Serializable +open class Reader(val title: String, val readerItems: List) + +//Set header `Referer` to reader url to avoid 403 error +fun getReader(galleryID: Int) : Reader { + val readerUrl = "https://hitomi.la/reader/$galleryID.html" + val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js" + + val doc = Jsoup.connect(readerUrl).get() + + val title = doc.title() + + val images = doc.select(".img-url").map { + protocol + urlFromURL(it.text()) + } + + val galleryInfo = ArrayList() + + galleryInfo.addAll( + Json(JsonConfiguration.Stable).parse( + GalleryInfo.serializer().list, + Regex("""\[.+]""").find( + URL(galleryInfoUrl).readText() + )?.value ?: "[]" + ) + ) + + if (images.size > galleryInfo.size) + galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) + + return Reader(title, (images zip galleryInfo).map { + ReaderItem(it.first, it.second) + }) +} \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt deleted file mode 100644 index 0be173e9..00000000 --- a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt +++ /dev/null @@ -1,57 +0,0 @@ -package xyz.quaver.hitomi - -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonConfiguration -import kotlinx.serialization.list -import org.jsoup.Jsoup -import java.net.URL - -fun getReferer(galleryID: Int) = "https://hitomi.la/reader/$galleryID.html" - -@Serializable -data class GalleryInfo( - val width: Int, - val haswebp: Int, - val name: String, - val height: Int -) -@Serializable -data class ReaderItem( - val url: String, - val galleryInfo: GalleryInfo? -) -typealias Reader = List -//Set header `Referer` to reader url to avoid 403 error -fun getReader(galleryID: Int) : Reader { - val readerUrl = "https://hitomi.la/reader/$galleryID.html" - val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js" - - try { - val doc = Jsoup.connect(readerUrl).get() - - val images = doc.select(".img-url").map { - protocol + urlFromURL(it.text()) - } - - val galleryInfo = ArrayList() - - galleryInfo.addAll( - Json(JsonConfiguration.Stable).parse( - GalleryInfo.serializer().list, - Regex("""\[.+]""").find( - URL(galleryInfoUrl).readText() - )?.value ?: "[]" - ) - ) - - if (images.size > galleryInfo.size) - galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) - - return (images zip galleryInfo).map { - ReaderItem(it.first, it.second) - } - } catch (e: Exception) { - return emptyList() - } -} \ No newline at end of file diff --git a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt index a20de850..e02983fe 100644 --- a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt @@ -1,9 +1,9 @@ package xyz.quaver.hiyobi -import kotlinx.io.IOException import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.content +import org.jsoup.Jsoup import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.ReaderItem import java.net.URL @@ -12,13 +12,15 @@ import javax.net.ssl.HttpsURLConnection const val hiyobi = "xn--9w3b15m8vo.asia" const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" -var cookie: String = "" -get() { - if (field.isEmpty()) - field = renewCookie() +class HiyobiReader(title: String, readerItems: List) : Reader(title, readerItems) - return field -} +var cookie: String = "" + get() { + if (field.isEmpty()) + field = renewCookie() + + return field + } fun renewCookie() : String { val url = "https://$hiyobi/" @@ -35,26 +37,25 @@ fun renewCookie() : String { } } -fun getReader(galleryId: Int) : Reader { - val url = "https://$hiyobi/data/json/${galleryId}_list.json" +fun getReader(galleryID: Int) : Reader { + val reader = "https://$hiyobi/reader/$galleryID" + val url = "https://$hiyobi/data/json/${galleryID}_list.json" - try { - val json = Json(JsonConfiguration.Stable).parseJson( - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("User-Agent", user_agent) - setRequestProperty("Cookie", cookie) - connectTimeout = 2000 - connect() + val title = Jsoup.connect(reader).get().title() - inputStream.bufferedReader().use { it.readText() } - } - ) + val json = Json(JsonConfiguration.Stable).parseJson( + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("User-Agent", user_agent) + setRequestProperty("Cookie", cookie) + connectTimeout = 2000 + connect() - return json.jsonArray.map { - val name = it.jsonObject["name"]!!.content - ReaderItem("https://$hiyobi/data/$galleryId/$name", null) + inputStream.bufferedReader().use { it.readText() } } - } catch (e: Exception) { - return emptyList() - } + ) + + return Reader(title, json.jsonArray.map { + val name = it.jsonObject["name"]!!.content + ReaderItem("https://$hiyobi/data/$galleryID/$name", null) + }) } \ No newline at end of file From 7e87bb6838ff8a7412fdff3ebf66a62520bea018 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Wed, 3 Jul 2019 19:40:19 +0900 Subject: [PATCH 11/28] Search algorithm improved Language settings in default tag fixed --- app/build.gradle | 2 +- .../main/java/xyz/quaver/pupil/types/Tags.kt | 4 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 162 +++++++++++------- .../xyz/quaver/pupil/ui/SettingsActivity.kt | 4 +- .../res/layout/item_selector_suggestion.xml | 34 ++++ .../main/java/xyz/quaver/hitomi/results.kt | 31 ++-- 6 files changed, 152 insertions(+), 85 deletions(-) create mode 100644 app/src/main/res/layout/item_selector_suggestion.xml diff --git a/app/build.gradle b/app/build.gradle index 9b1f14c2..db1cf65d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,7 +14,7 @@ android { minSdkVersion 16 targetSdkVersion 29 versionCode 20 - versionName "2.11.1" + versionName "2.12" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt index 70d0a119..4d784f1e 100644 --- a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt +++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt @@ -90,8 +90,8 @@ class Tags(tag: List?) : ArrayList() { } } - fun removeByArea(area: String) { - filter { it.area == area }.forEach { + fun removeByArea(area: String, isNegative: Boolean? = null) { + filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach { remove(it) } } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 084020b1..cfdf076d 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -17,6 +17,7 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.cardview.widget.CardView +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat @@ -45,6 +46,7 @@ import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.adapters.GalleryBlockAdapter +import xyz.quaver.pupil.types.SelectorSuggestion import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.types.TagSuggestion import xyz.quaver.pupil.types.Tags @@ -755,7 +757,7 @@ class MainActivity : AppCompatActivity() { if (query.isEmpty() or query.endsWith(' ')) { swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { TagSuggestion(it.tag, -1, "", it.area ?: "tag") - }) + } + SelectorSuggestion()) return@setOnQueryChangeListener } @@ -782,83 +784,115 @@ class MainActivity : AppCompatActivity() { } setOnBindSuggestionCallback { suggestionView, leftIcon, textView, item, _ -> - val suggestion = item as TagSuggestion - val tag = "${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")}" + if (item is SelectorSuggestion) { + var hasSelector = false - leftIcon.setImageDrawable( - ResourcesCompat.getDrawable( - resources, - when(suggestion.n) { - "female" -> R.drawable.ic_gender_female - "male" -> R.drawable.ic_gender_male - "language" -> R.drawable.ic_translate - "group" -> R.drawable.ic_account_group - "character" -> R.drawable.ic_account_star - "series" -> R.drawable.ic_book_open - "artist" -> R.drawable.ic_brush - else -> R.drawable.ic_tag - }, - null) - ) - - with(suggestionView.findViewById(R.id.right_icon)) { - - if (Tags(json.parse(serializer, favoritesFile.readText())).contains(tag)) - setImageResource(R.drawable.ic_star_filled) - else - setImageResource(R.drawable.ic_star_empty) - - visibility = View.VISIBLE - rotation = 0f - isEnabled = true - - setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500)) - - isClickable = true - setOnClickListener { - val favorites = Tags(json.parse(serializer, favoritesFile.readText())) - - if (favorites.contains(tag)) { - setImageResource(R.drawable.ic_star_empty) - favorites.remove(tag) + with(suggestionView as LinearLayout) { + for (i in 0 until childCount) { + val child = getChildAt(i) + if (child is ConstraintLayout) { + child.visibility = View.VISIBLE + hasSelector = true + } + else + child.visibility = View.GONE } - else { - setImageDrawable(AnimatedVectorDrawableCompat.create(context, - R.drawable.avd_star - )) - (drawable as Animatable).start() - - favorites.add(tag) - } - - favoritesFile.writeText(json.stringify(favorites)) } - } - if (suggestion.t == -1) { - textView.text = suggestion.s - } else { - val text = "${suggestion.s}\n ${suggestion.t}" + if (!hasSelector) { + val view = LayoutInflater.from(context) + .inflate(R.layout.item_selector_suggestion, suggestionView, false) - val len = text.length - val left = suggestion.s.length + suggestionView.addView(view) + } + } else if(item is TagSuggestion) { + with(suggestionView as LinearLayout) { + for (i in 0 until childCount) { + val child = getChildAt(i) + if (child is ConstraintLayout) { + child.visibility = View.GONE + } + else + child.visibility = View.VISIBLE + } + } + val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}" - textView.text = SpannableString(text).apply { - val s = AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE) - setSpan(s, left, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(SetLineOverlap(true), 1, len-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(SetLineOverlap(false), len-1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + leftIcon.setImageDrawable( + ResourcesCompat.getDrawable( + resources, + when(item.n) { + "female" -> R.drawable.ic_gender_female + "male" -> R.drawable.ic_gender_male + "language" -> R.drawable.ic_translate + "group" -> R.drawable.ic_account_group + "character" -> R.drawable.ic_account_star + "series" -> R.drawable.ic_book_open + "artist" -> R.drawable.ic_brush + else -> R.drawable.ic_tag + }, + null) + ) + + with(suggestionView.findViewById(R.id.right_icon)) { + + if (Tags(json.parse(serializer, favoritesFile.readText())).contains(tag)) + setImageResource(R.drawable.ic_star_filled) + else + setImageResource(R.drawable.ic_star_empty) + + rotation = 0f + isEnabled = true + + setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500)) + + isClickable = true + setOnClickListener { + val favorites = Tags(json.parse(serializer, favoritesFile.readText())) + + if (favorites.contains(tag)) { + setImageResource(R.drawable.ic_star_empty) + favorites.remove(tag) + } + else { + setImageDrawable(AnimatedVectorDrawableCompat.create(context, + R.drawable.avd_star + )) + (drawable as Animatable).start() + + favorites.add(tag) + } + + favoritesFile.writeText(json.stringify(favorites)) + } + } + + if (item.t == -1) { + textView.text = item.s + } else { + val text = "${item.s}\n ${item.t}" + + val len = text.length + val left = item.s.length + + textView.text = SpannableString(text).apply { + val s = AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE) + setSpan(s, left, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + setSpan(SetLineOverlap(true), 1, len-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + setSpan(SetLineOverlap(false), len-1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } } } } setOnSearchListener(object : FloatingSearchView.OnSearchListener { override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) { - val suggestion = searchSuggestion as TagSuggestion + if (searchSuggestion !is TagSuggestion) + return with(searchInputView.text) { delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length) - append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ") + append("${searchSuggestion.n}:${searchSuggestion.s.replace(Regex("\\s"), "_")} ") } } @@ -872,7 +906,7 @@ class MainActivity : AppCompatActivity() { if (query.isEmpty() or query.endsWith(' ')) swapSuggestions(json.parse(serializer, favoritesFile.readText()).map { TagSuggestion(it.tag, -1, "", it.area ?: "tag") - }) + } + SelectorSuggestion()) } override fun onFocusCleared() { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt index e4229365..ea0d9f8c 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt @@ -222,14 +222,14 @@ class SettingsActivity : AppCompatActivity() { addAll(languages.values) } ) - if (tags.any { it.area == "language" }) { + if (tags.any { it.area == "language" && !it.isNegative }) { val tag = languages[tags.first { it.area == "language" }.tag] if (tag != null) { setSelection( @Suppress("UNCHECKED_CAST") (adapter as ArrayAdapter).getPosition(tag) ) - tags.removeByArea("language") + tags.removeByArea("language", false) } } } diff --git a/app/src/main/res/layout/item_selector_suggestion.xml b/app/src/main/res/layout/item_selector_suggestion.xml new file mode 100644 index 00000000..144792cb --- /dev/null +++ b/app/src/main/res/layout/item_selector_suggestion.xml @@ -0,0 +1,34 @@ + + + + + + + +