From 23bf93e960d9dfbbff1ab012fa98a4db48beb34f Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 16 May 2019 16:25:28 +0900 Subject: [PATCH 1/8] Improved loading speed of the cached gallery --- .idea/misc.xml | 2 +- app/build.gradle | 1 + .../java/xyz/quaver/pupil/ReaderActivity.kt | 165 +++++++++++------- .../res/drawable-anydpi/ic_fullscreen.xml | 10 ++ .../main/res/drawable-hdpi/ic_fullscreen.png | Bin 0 -> 145 bytes .../main/res/drawable-mdpi/ic_fullscreen.png | Bin 0 -> 99 bytes .../main/res/drawable-xhdpi/ic_fullscreen.png | Bin 0 -> 125 bytes .../res/drawable-xxhdpi/ic_fullscreen.png | Bin 0 -> 149 bytes app/src/main/res/drawable/github_circle.xml | 8 + app/src/main/res/layout/activity_reader.xml | 33 +++- 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 + .../main/java/xyz/quaver/hitomi/readers.kt | 13 +- .../src/main/java/xyz/quaver/hiyobi/reader.kt | 3 +- 15 files changed, 162 insertions(+), 76 deletions(-) create mode 100644 app/src/main/res/drawable-anydpi/ic_fullscreen.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_fullscreen.png create mode 100644 app/src/main/res/drawable-mdpi/ic_fullscreen.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_fullscreen.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_fullscreen.png create mode 100644 app/src/main/res/drawable/github_circle.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 ea00135a..c7cfaa59 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,6 +41,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation "ru.noties.markwon:core:${markwonVersion}" implementation 'com.shawnlin:number-picker:2.4.8' + implementation 'com.github.clans:fab:1.6.4' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test:runner:1.1.1' diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 80471c11..f6e56537 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -1,19 +1,23 @@ package xyz.quaver.pupil import android.os.Bundle -import android.util.Log import android.view.* 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 kotlinx.android.synthetic.main.activity_reader.* import kotlinx.android.synthetic.main.activity_reader.view.* import kotlinx.android.synthetic.main.dialog_numberpicker.view.* import kotlinx.coroutines.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration +import kotlinx.serialization.list import xyz.quaver.hitomi.Reader +import xyz.quaver.hitomi.ReaderItem import xyz.quaver.hitomi.getReader import xyz.quaver.hitomi.getReferer import xyz.quaver.pupil.adapters.ReaderAdapter @@ -32,7 +36,10 @@ class ReaderActivity : AppCompatActivity() { private lateinit var reader: Deferred private var loadJob: Job? = null - private lateinit var snapHelper: PagerSnapHelper + private var isScroll = true + private var isFullscreen = false + + private val snapHelper = PagerSnapHelper() private var menu: Menu? = null @@ -48,44 +55,39 @@ class ReaderActivity : AppCompatActivity() { supportActionBar?.title = intent.getStringExtra("GALLERY_TITLE") galleryID = intent.getIntExtra("GALLERY_ID", 0) - CoroutineScope(Dispatchers.Unconfined).launch { - reader = async(Dispatchers.IO) { - val preference = PreferenceManager.getDefaultSharedPreferences(this@ReaderActivity) - if (preference.getBoolean("use_hiyobi", false)) { - try { - xyz.quaver.hiyobi.getReader(galleryID) - } catch (e: Exception) { + reader = CoroutineScope(Dispatchers.IO).async { + val json = Json(JsonConfiguration.Stable) + val serializer = ReaderItem.serializer().list + val preference = PreferenceManager.getDefaultSharedPreferences(this@ReaderActivity) + val isHiyobi = preference.getBoolean("use_hiyobi", false) + + val cache = when { + isHiyobi -> File(cacheDir, "imageCache/$galleryID/reader-hiyobi.json") + else -> File(cacheDir, "imageCache/$galleryID/reader.json") + } + + if (cache.exists()) + json.parse(serializer, cache.readText()) + else { + val reader = when { + isHiyobi -> { + try { + xyz.quaver.hiyobi.getReader(galleryID) + } catch (e: Exception) { + getReader(galleryID) + } + } + else -> { getReader(galleryID) } } - getReader(galleryID) + + cache.writeText(json.stringify(serializer, reader)) + + reader } } - snapHelper = PagerSnapHelper() - - val preferences = PreferenceManager.getDefaultSharedPreferences(this) - - val attrs = window.attributes - - if (preferences.getBoolean("reader_fullscreen", false)) { - attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN - supportActionBar?.hide() - } else { - attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv() - supportActionBar?.show() - } - - window.attributes = attrs - - if (preferences.getBoolean("reader_one_by_one", false)) { - snapHelper.attachToRecyclerView(reader_recyclerview) - reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) - } else { - snapHelper.attachToRecyclerView(null) - reader_recyclerview.layoutManager = LinearLayoutManager(this) - } - initView() loadImages() } @@ -138,6 +140,21 @@ class ReaderActivity : AppCompatActivity() { loadJob?.cancel() } + override fun onBackPressed() { + if (isScroll and !isFullscreen) + super.onBackPressed() + + if (isFullscreen) { + isFullscreen = false + fullscreen(false) + } + + if (!isScroll) { + isScroll = true + scrollMode(true) + } + } + private fun initView() { with(reader_recyclerview) { adapter = ReaderAdapter(images) @@ -155,45 +172,61 @@ class ReaderActivity : AppCompatActivity() { } }) - val preferences = PreferenceManager.getDefaultSharedPreferences(context) ItemClickSupport.addTo(this) .setOnItemClickListener { _, _, _ -> - val attrs = window.attributes - val fullscreen = preferences.getBoolean("reader_fullscreen", false) + if (isScroll) { + isScroll = false + isFullscreen = true - if (fullscreen) { - attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv() - supportActionBar?.show() + scrollMode(false) + fullscreen(true) } else { - attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN - supportActionBar?.hide() + val smoothScroller = object : LinearSmoothScroller(context) { + override fun getVerticalSnapPreference() = SNAP_TO_START + }.apply { + targetPosition = currentPage + } + (reader_recyclerview.layoutManager as LinearLayoutManager?)?.startSmoothScroll(smoothScroller) } - - window.attributes = attrs - - preferences.edit().putBoolean("reader_fullscreen", !fullscreen).apply() - }.setOnItemLongClickListener { _, _, _ -> - val oneByOne = preferences.getBoolean("reader_one_by_one", false) - if (oneByOne) { - snapHelper.attachToRecyclerView(null) - reader_recyclerview.layoutManager = LinearLayoutManager(context) - } - else { - snapHelper.attachToRecyclerView(reader_recyclerview) - reader_recyclerview.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - } - - (reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0) - - preferences.edit().putBoolean("reader_one_by_one", !oneByOne).apply() - - true } } + + reader_fab_fullscreen.setOnClickListener { + isFullscreen = true + fullscreen(isFullscreen) + } + } + + private fun fullscreen(isFullscreen: Boolean) { + with(window.attributes) { + if (isFullscreen) { + flags = flags or WindowManager.LayoutParams.FLAG_FULLSCREEN + supportActionBar?.hide() + this@ReaderActivity.reader_fab.visibility = View.INVISIBLE + } else { + flags = flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv() + supportActionBar?.show() + this@ReaderActivity.reader_fab.visibility = View.VISIBLE + } + + window.attributes = this + } + } + + private fun scrollMode(isScroll: Boolean) { + if (isScroll) { + snapHelper.attachToRecyclerView(null) + reader_recyclerview.layoutManager = LinearLayoutManager(this) + } else { + snapHelper.attachToRecyclerView(reader_recyclerview) + reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) + } + + (reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0) } private fun loadImages() { - fun webpUrlFromUrl(url: URL) = URL(url.toString().replace("/galleries/", "/webp/") + ".webp") + fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp" loadJob = CoroutineScope(Dispatchers.Default).launch { val reader = reader.await() @@ -213,18 +246,18 @@ class ReaderActivity : AppCompatActivity() { reader.chunked(8).forEach { chunked -> chunked.map { async(Dispatchers.IO) { - val url = if (it.second?.haswebp == 1) webpUrlFromUrl(it.first) else it.first + val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url val fileName: String - with(url.path) { + with(url) { fileName = substring(lastIndexOf('/')+1) } val cache = File(cacheDir, "/imageCache/$galleryID/$fileName") if (!cache.exists()) - with(url.openConnection() as HttpsURLConnection) { + with(URL(url).openConnection() as HttpsURLConnection) { setRequestProperty("Referer", getReferer(galleryID)) if (!cache.parentFile.exists()) diff --git a/app/src/main/res/drawable-anydpi/ic_fullscreen.xml b/app/src/main/res/drawable-anydpi/ic_fullscreen.xml new file mode 100644 index 00000000..f9831623 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_fullscreen.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_fullscreen.png b/app/src/main/res/drawable-hdpi/ic_fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..c72a9b9eb59f52f566d83fa5c178bc9b92094282 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBB0OCjLn>~)z2?oupupjJaoV+W zF;U)|rA#k%ewkc-VBseP4{yc>*U;A*8&ACyUY@0LK_;Q-m(3)z0B)Pe+kzf>=M^v1 tH!{fF`1Z${#jCciz;Ivv6Xp{un60OsRSur8gB558gQu&X%Q~loCIBDAGwc8W literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_fullscreen.png b/app/src/main/res/drawable-mdpi/ic_fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..f19ccbfa392ba7a7975a9a5ed7d334464283c107 GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjTAnVBAr-fh6C_xbh5sC9IuU66 wLF|IpUxN(^B0TeY%x3wt7e<_Y_>z%f$BCRIMkba$!Vk0+A8cn}NI1as X_vjxzO$PaRpm_|Qu6{1-oD!M<3#}yT literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_fullscreen.png b/app/src/main/res/drawable-xxhdpi/ic_fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..4712c6c1418e20cca01bb0f0f9a842c695d12b84 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!F`h1tAr-gY-rC5^z`(-b_*>sM zm8rL2JHt;uB_^PJ!!+jE>6|JP9GE!2SWc*A5%N%I1QYYvfzk&GU(6DRiLiY7_sAZ~ c0qHE}dfxlJQsScWS|E?X)78&qol`;+03Hb{3jhEB literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/github_circle.xml b/app/src/main/res/drawable/github_circle.xml new file mode 100644 index 00000000..7df41959 --- /dev/null +++ b/app/src/main/res/drawable/github_circle.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index a77f48e5..f77b8d6b 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -6,12 +6,18 @@ android:layout_height="match_parent" tools:context=".ReaderActivity"> - + android:layout_height="wrap_content" + android:layout_gravity="center_vertical"> + + + + + + + + + + \ 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 d9519336..a0018929 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -43,4 +43,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 1ecefb30..a4de07d9 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -43,4 +43,5 @@ 문의 준비 중 준비중입니다.\n만화 화면에서 사진을 길게 누르면 스크롤 방식이 바뀝니다. 알고 계셨나요? :) + 전체 화면 \ 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 551b9f54..97f25378 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,6 +48,7 @@ Language: %1$s Go to page + Fullscreen Settings Search Settings diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt index 5fee483d..f2a06ec1 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt @@ -16,7 +16,12 @@ data class GalleryInfo( val name: String, val height: Int ) -typealias Reader = List> +@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" @@ -25,7 +30,7 @@ fun getReader(galleryID: Int) : Reader { val doc = Jsoup.connect(readerUrl).get() val images = doc.select(".img-url").map { - URL(protocol + urlFromURL(it.text())) + protocol + urlFromURL(it.text()) } val galleryInfo = ArrayList() @@ -42,5 +47,7 @@ fun getReader(galleryID: Int) : Reader { if (images.size > galleryInfo.size) galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) - return images zip galleryInfo + return (images zip galleryInfo).map { + ReaderItem(it.first, it.second) + } } \ 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 dfd289bb..47f766b4 100644 --- a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.content import xyz.quaver.hitomi.Reader +import xyz.quaver.hitomi.ReaderItem import java.net.URL import javax.net.ssl.HttpsURLConnection @@ -42,6 +43,6 @@ fun getReader(galleryId: Int) : Reader { return json.jsonArray.map { val name = it.jsonObject["name"]!!.content - Pair(URL("https://$hiyobi/data/$galleryId/$name"), null) + ReaderItem("https://$hiyobi/data/$galleryId/$name", null) } } \ No newline at end of file From d090a59800ed0e8c4b5d9758355c30faf579dd17 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 16 May 2019 18:15:53 +0900 Subject: [PATCH 2/8] Added Offline mode --- .../java/xyz/quaver/pupil/MainActivity.kt | 60 +++++++++++++----- .../java/xyz/quaver/pupil/ReaderActivity.kt | 60 +++++++++++------- .../pupil/adapters/GalleryBlockAdapter.kt | 6 +- .../main/java/xyz/quaver/pupil/util/update.kt | 8 ++- .../res/drawable-anydpi/ic_placeholder.xml | 10 +++ .../main/res/drawable-hdpi/ic_placeholder.png | Bin 0 -> 361 bytes .../main/res/drawable-mdpi/ic_placeholder.png | Bin 0 -> 224 bytes .../res/drawable-xhdpi/ic_placeholder.png | Bin 0 -> 370 bytes .../res/drawable-xxhdpi/ic_placeholder.png | Bin 0 -> 602 bytes .../java/xyz/quaver/hitomi/galleryblock.kt | 42 ++++++------ .../main/java/xyz/quaver/hitomi/readers.kt | 37 ++++++----- .../src/main/java/xyz/quaver/hitomi/search.kt | 37 +++++++---- .../src/main/java/xyz/quaver/hiyobi/reader.kt | 42 +++++++----- .../test/java/xyz/quaver/hitomi/UnitTest.kt | 7 +- 14 files changed, 201 insertions(+), 108 deletions(-) create mode 100644 app/src/main/res/drawable-anydpi/ic_placeholder.xml create mode 100644 app/src/main/res/drawable-hdpi/ic_placeholder.png create mode 100644 app/src/main/res/drawable-mdpi/ic_placeholder.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_placeholder.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_placeholder.png diff --git a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt index 0d97193c..6e9f18f7 100644 --- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt @@ -6,7 +6,6 @@ import android.os.Bundle import android.preference.PreferenceManager import android.text.* import android.text.style.AlignmentSpan -import android.util.Log import android.view.View import android.view.WindowManager import androidx.appcompat.app.AlertDialog @@ -22,6 +21,8 @@ import com.google.android.material.appbar.AppBarLayout import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main_content.* import kotlinx.coroutines.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.content import ru.noties.markwon.Markwon @@ -34,6 +35,7 @@ import xyz.quaver.pupil.util.SetLineOverlap import xyz.quaver.pupil.util.checkUpdate import java.io.File import java.io.FileOutputStream +import java.net.URL import java.util.* import javax.net.ssl.HttpsURLConnection import kotlin.collections.ArrayList @@ -436,25 +438,48 @@ class MainActivity : AppCompatActivity() { galleryIDs else -> galleryIDs.slice(galleries.size until Math.min(galleries.size+perPage, galleryIDs.size)) - }.chunked(4).let { chunks -> + }.chunked(5).let { chunks -> for (chunk in chunks) chunk.map { async { try { - val galleryBlock = getGalleryBlock(it) + val json = Json(JsonConfiguration.Stable) + val serializer = GalleryBlock.serializer() + + val galleryBlock = + File(cacheDir, "imageCache/$it/galleryBlock.json").let { cache -> + when { + cache.exists() -> json.parse(serializer, cache.readText()) + else -> { + getGalleryBlock(it).apply { + this ?: return@apply + + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() + + cache.writeText(json.stringify(serializer, this)) + } + } + } + } ?: return@async null val thumbnail = async { - val cache = File(cacheDir, "imageCache/$it/thumbnail.${galleryBlock.thumbnails[0].path.split('.').last()}") + val ext = galleryBlock.thumbnails[0].split('.').last() + File(cacheDir, "imageCache/$it/thumbnail.$ext").apply { + val cache = this - if (!cache.exists()) - with(galleryBlock.thumbnails[0].openConnection() as HttpsURLConnection) { - if (!cache.parentFile.exists()) - cache.parentFile.mkdirs() + if (!cache.exists()) + try { + with(URL(galleryBlock.thumbnails[0]).openConnection() as HttpsURLConnection) { + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() - inputStream.copyTo(FileOutputStream(cache)) - } - - cache.absolutePath + inputStream.copyTo(FileOutputStream(cache)) + } + } catch (e: Exception) { + cache.delete() + } + }.absolutePath } Pair(galleryBlock, thumbnail) @@ -463,16 +488,19 @@ class MainActivity : AppCompatActivity() { } } }.forEach { - val galleryBlock = it.await() ?: return@forEach + val galleryBlock = it.await() withContext(Dispatchers.Main) { main_progressbar.hide() - galleries.add(galleryBlock) - main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1) + if (galleryBlock != null) { + galleries.add(galleryBlock) + + main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1) + } } } - } + } } } } diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index f6e56537..94f5fa8d 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -66,26 +66,36 @@ class ReaderActivity : AppCompatActivity() { else -> File(cacheDir, "imageCache/$galleryID/reader.json") } - if (cache.exists()) - json.parse(serializer, cache.readText()) - else { - val reader = when { - isHiyobi -> { - try { - xyz.quaver.hiyobi.getReader(galleryID) - } catch (e: Exception) { - getReader(galleryID) - } - } - else -> { - getReader(galleryID) - } - } + if (cache.exists()) { + val cached = json.parse(serializer, cache.readText()) - cache.writeText(json.stringify(serializer, reader)) - - reader + if (cached.isNotEmpty()) + return@async cached } + + val reader = when { + isHiyobi -> { + xyz.quaver.hiyobi.getReader(galleryID).let { + when { + it.isEmpty() -> getReader(galleryID) + else -> it + } + } + } + else -> { + getReader(galleryID) + } + } + + if (reader.isEmpty()) + finish() + + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() + + cache.writeText(json.stringify(serializer, reader)) + + reader } initView() @@ -257,13 +267,17 @@ class ReaderActivity : AppCompatActivity() { val cache = File(cacheDir, "/imageCache/$galleryID/$fileName") if (!cache.exists()) - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("Referer", getReferer(galleryID)) + try { + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("Referer", getReferer(galleryID)) - if (!cache.parentFile.exists()) - cache.parentFile.mkdirs() + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() - inputStream.copyTo(FileOutputStream(cache)) + inputStream.copyTo(FileOutputStream(cache)) + } + } catch (e: Exception) { + cache.delete() } cache.absolutePath 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 e83e4f86..c32ffb78 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -9,11 +9,13 @@ import androidx.cardview.widget.CardView import androidx.recyclerview.widget.RecyclerView import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.toTag import xyz.quaver.pupil.R -import java.io.File class GalleryBlockAdapter(private val galleries: List>>) : RecyclerView.Adapter() { 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 b25e03e0..2c951cbe 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/update.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt @@ -4,8 +4,12 @@ import kotlinx.serialization.json.* import java.net.URL fun getReleases(url: String) : JsonArray { - return URL(url).readText().let { - Json(JsonConfiguration.Stable).parse(JsonArray.serializer(), it) + return try { + URL(url).readText().let { + Json(JsonConfiguration.Stable).parse(JsonArray.serializer(), it) + } + } catch (e: Exception) { + JsonArray(emptyList()) } } diff --git a/app/src/main/res/drawable-anydpi/ic_placeholder.xml b/app/src/main/res/drawable-anydpi/ic_placeholder.xml new file mode 100644 index 00000000..7b74d4b6 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_placeholder.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_placeholder.png b/app/src/main/res/drawable-hdpi/ic_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..1604365ee4cfd9686abc9af4c6b8b3889469cb9e GIT binary patch literal 361 zcmV-v0ha!WP)!clwb=yPI z0oeAC#D40e{`!dUr@aUIDbVJA@;7%4+T2BPlUHdkUKH{I7AE#Niuw;m00000NkvXX Hu0mjfnWma` literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_placeholder.png b/app/src/main/res/drawable-mdpi/ic_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..cc9eb1eb4c55dfe2c692efd200d7a0f7e8659df5 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj%ROBjLn>~)o$ATgpupq0x9@P< zOTK&mIW{#0Ye-~E1#?-fS#q+!yZ^tT$lG0Erm9QlObs&2*sCdRJgLDTp;CY^%HqJU zwtt)-+PiA_99k>ZeifPesn;-BI&6AzSe%zQ#c{Xr@h1B+=m|`PCO4b zKa5debrCLNNIcJ=Kl7H5jdPNB?2ePQ>$*3!&-xhaIXi4?>h6>4nfCpSssDF4)AcHw Y@5IBCK0e!d1n6W2Pgg&ebxsLQ0RH1!<^TWy literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_placeholder.png b/app/src/main/res/drawable-xhdpi/ic_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..2a7d65cbccfd703475685da900a5b5acfcad4e2a GIT binary patch literal 370 zcmV-&0ge8NP)Wpo<8gLjZ=Df&c^{00I63KuiH2SgHdw z1c0cj>LLS#XczhI5de*=`8gMWXxI69oA#2(}D*#}@#-hfJD+~m3&ZWGB>%)jQ4|$KADQ)&`{JLRKd_g89(ZGvRD?wMzrK;27glBXtVKXw$suzp(?g)Hn=CodU7+vPO|8fwqZA zRvbbkD~^G*vmueTU0usH7KZ>y;p{xF{|^#*<30k(3nu}|3uj1Kmha~HEO$4?mTM|b z1X8Ca96G*y&wd!6?f#etQWQ=AQXhkJi^O^p@pid};0))WNF;@`cWo;2HLtGm+3&QP z%QX!|o=!PFG87-li5<2TLOgQ=wJxO8 zFWr>y1droK!;0i{S|!-FqmTlQ9gTeL6DLsc!7Sq77lfpD(vX8|BkPe9$Qvo)r-K(# z1P&ik=qZ-q$LA!H-Z{I3@f$hD19v~Dvu(SC?F%cCReKmY;|fB*y_00HQ~f$%7T0|*bZIQqVL9BL|fpw1(B oB-3*x;h|mxK@bE%5CoAq1CeP~KZTO{AOHXW07*qoM6N<$f-O7$MgRZ+ literal 0 HcmV?d00001 diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt b/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt index 39dfb263..cd4859b0 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/galleryblock.kt @@ -1,5 +1,6 @@ package xyz.quaver.hitomi +import kotlinx.serialization.Serializable import org.jsoup.Jsoup import java.net.URL import java.net.URLDecoder @@ -39,13 +40,14 @@ fun fetchNozomi(area: String? = null, tag: String = "index", language: String = return nozomi } } catch (e: Exception) { - return listOf() + return emptyList() } } +@Serializable data class GalleryBlock( val id: Int, - val thumbnails: List, + val thumbnails: List, val title: String, val artists: List, val series: List, @@ -53,27 +55,31 @@ data class GalleryBlock( val language: String, val relatedTags: List ) -fun getGalleryBlock(galleryID: Int) : GalleryBlock { +fun getGalleryBlock(galleryID: Int) : GalleryBlock? { val url = "$protocol//$domain/$galleryblockdir/$galleryID$extension" - val doc = Jsoup.connect(url).get() + try { + val doc = Jsoup.connect(url).get() - val thumbnails = doc.select("img").map { URL(protocol + it.attr("data-src")) } + val thumbnails = doc.select("img").map { protocol + it.attr("data-src") } - val title = doc.selectFirst("h1.lillie > a").text() - val artists = doc.select("div.artist-list a").map{ it.text() } - val series = doc.select("a[href~=^/series/]").map { it.text() } - val type = doc.selectFirst("a[href~=^/type/]").text() + val title = doc.selectFirst("h1.lillie > a").text() + val artists = doc.select("div.artist-list a").map{ it.text() } + val series = doc.select("a[href~=^/series/]").map { it.text() } + val type = doc.selectFirst("a[href~=^/type/]").text() - val language = { - val href = doc.select("a[href~=^/index-.+-1.html]").attr("href") - href.slice(7 until href.indexOf("-1")) - }.invoke() + val language = { + val href = doc.select("a[href~=^/index-.+-1.html]").attr("href") + href.slice(7 until href.indexOf("-1")) + }.invoke() - val relatedTags = doc.select(".relatedtags a").map { - val href = URLDecoder.decode(it.attr("href"), "UTF-8") - href.slice(5 until href.indexOf('-')) + val relatedTags = doc.select(".relatedtags a").map { + val href = URLDecoder.decode(it.attr("href"), "UTF-8") + href.slice(5 until href.indexOf('-')) + } + + return GalleryBlock(galleryID, thumbnails, title, artists, series, type, language, relatedTags) + } catch (e: Exception) { + return null } - - return GalleryBlock(galleryID, thumbnails, title, artists, series, type, language, relatedTags) } \ 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 index f2a06ec1..36b9811d 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt @@ -27,27 +27,32 @@ 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() + try { - val images = doc.select(".img-url").map { - protocol + urlFromURL(it.text()) - } + val doc = Jsoup.connect(readerUrl).get() - val galleryInfo = ArrayList() + val images = doc.select(".img-url").map { + protocol + urlFromURL(it.text()) + } - galleryInfo.addAll( - Json(JsonConfiguration.Stable).parse( - GalleryInfo.serializer().list, - Regex("""\[.+]""").find( - URL(galleryInfoUrl).readText() - )?.value ?: "[]" + 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)) + if (images.size > galleryInfo.size) + galleryInfo.addAll(arrayOfNulls(images.size - galleryInfo.size)) - return (images zip galleryInfo).map { - ReaderItem(it.first, it.second) + 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/hitomi/search.kt b/libpupil/src/main/java/xyz/quaver/hitomi/search.kt index e771ec3d..833c826c 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/search.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/search.kt @@ -15,8 +15,8 @@ const val max_node_size = 464 const val B = 16 const val compressed_nozomi_prefix = "n" -val tag_index_version = getIndexVersion("tagindex") -val galleries_index_version = getIndexVersion("galleriesindex") +var tag_index_version = getIndexVersion("tagindex") +var galleries_index_version = getIndexVersion("galleriesindex") fun sha256(data: ByteArray) : ByteArray { return MessageDigest.getInstance("SHA-256").digest(data) @@ -32,8 +32,12 @@ fun sanitize(input: String) : String { } fun getIndexVersion(name: String) : String { - return URL("$protocol//$domain/$name/version?_=${System.currentTimeMillis()}") - .readText() + return try { + URL("$protocol//$domain/$name/version?_=${System.currentTimeMillis()}") + .readText() + } catch (e: Exception) { + "" + } } //search.js @@ -64,14 +68,14 @@ fun getGalleryIDsForQuery(query: String) : List { val key = hashTerm(it) val field = "galleries" - val node = getNodeAtAddress(field, 0) ?: return listOf() + val node = getNodeAtAddress(field, 0) ?: return emptyList() val data = bSearch(field, key, node) if (data != null) return getGalleryIDsFromData(data) - return arrayListOf() + return emptyList() } } @@ -87,24 +91,27 @@ fun getSuggestionsForQuery(query: String) : List { } val key = hashTerm(term) - val node = getNodeAtAddress(field, 0) ?: return listOf() + val node = getNodeAtAddress(field, 0) ?: return emptyList() val data = bSearch(field, key, node) if (data != null) return getSuggestionsFromData(field, data) - return listOf() + return emptyList() } } data class Suggestion(val s: String, val t: Int, val u: String, val n: String) fun getSuggestionsFromData(field: String, data: Pair) : List { + if (tag_index_version.isEmpty()) + tag_index_version = getIndexVersion("tagindex") + val url = "$protocol//$domain/$index_dir/$field.$tag_index_version.data" val (offset, length) = data if (length > 10000 || length <= 0) throw Exception("length $length is too long") - val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return listOf() + val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return emptyList() val suggestions = ArrayList() @@ -166,17 +173,20 @@ fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : List return nozomi } } catch (e: Exception) { - return listOf() + return emptyList() } } fun getGalleryIDsFromData(data: Pair) : List { + if (galleries_index_version.isEmpty()) + galleries_index_version = getIndexVersion("galleriesindex") + val url = "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.data" val (offset, length) = data if (length > 100000000 || length <= 0) throw Exception("length $length is too long") - val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return listOf() + val inbuf = getURLAtRange(url, offset.until(offset+length)) ?: return emptyList() val galleryIDs = ArrayList() @@ -200,6 +210,11 @@ fun getGalleryIDsFromData(data: Pair) : List { } fun getNodeAtAddress(field: String, address: Long) : Node? { + if (tag_index_version.isEmpty()) + tag_index_version = getIndexVersion("tagindex") + if (galleries_index_version.isEmpty()) + galleries_index_version = getIndexVersion("galleriesindex") + val url = when(field) { "galleries" -> "$protocol//$domain/$galleries_index_dir/galleries.$galleries_index_version.index" diff --git a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt index 47f766b4..2f201385 100644 --- a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt @@ -16,11 +16,15 @@ var cookie: String = "" fun renewCookie() : String { val url = "https://$hiyobi/" - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("User-Agent", user_agent) - connectTimeout = 2000 - connect() - return headerFields["Set-Cookie"]!![0] + try { + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("User-Agent", user_agent) + connectTimeout = 2000 + connect() + return headerFields["Set-Cookie"]!![0] + } + } catch (e: Exception) { + return "" } } @@ -30,19 +34,23 @@ fun getReader(galleryId: Int) : Reader { if (cookie.isEmpty()) cookie = renewCookie() - val json = Json(JsonConfiguration.Stable).parseJson( - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("User-Agent", user_agent) - setRequestProperty("Cookie", cookie) - connectTimeout = 2000 - connect() + try { + val json = Json(JsonConfiguration.Stable).parseJson( + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("User-Agent", user_agent) + setRequestProperty("Cookie", cookie) + connectTimeout = 2000 + connect() - inputStream.bufferedReader().use { it.readText() } + inputStream.bufferedReader().use { it.readText() } + } + ) + + return json.jsonArray.map { + val name = it.jsonObject["name"]!!.content + ReaderItem("https://$hiyobi/data/$galleryId/$name", null) } - ) - - return json.jsonArray.map { - val name = it.jsonObject["name"]!!.content - ReaderItem("https://$hiyobi/data/$galleryId/$name", null) + } catch (e: Exception) { + return emptyList() } } \ No newline at end of file diff --git a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt index e32567f5..b573e193 100644 --- a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt +++ b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt @@ -1,14 +1,15 @@ package xyz.quaver.hitomi import org.junit.Test +import java.io.File import java.net.URL class UnitTest { @Test fun test() { - val url = URL("https://ltn.hitomi.la/galleries/1411672.js") + val f = File("C:/Users/tom50/Workspace/Pupil/nodir/nodir/asdf.txt") - print(url.path.substring(url.path.lastIndexOf('/')+1)) + f.delete() } @Test @@ -63,6 +64,6 @@ class UnitTest { @Test fun test_hiyobi() { - xyz.quaver.hiyobi.getReader(1414061) + print(xyz.quaver.hiyobi.getReader(1415416).size) } } \ No newline at end of file From 234f9b069a848c2de676bd625fcf2e9e8e82a0f5 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Thu, 16 May 2019 19:40:44 +0900 Subject: [PATCH 3/8] Small fixes --- app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt | 2 ++ app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt | 8 ++++---- .../main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt | 1 - app/src/main/java/xyz/quaver/pupil/util/history.kt | 4 ---- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 94f5fa8d..3bec8707 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -204,6 +204,8 @@ class ReaderActivity : AppCompatActivity() { reader_fab_fullscreen.setOnClickListener { isFullscreen = true fullscreen(isFullscreen) + + reader_fab.close(true) } } diff --git a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt index 1640e5d5..9a3c92b0 100644 --- a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt @@ -6,16 +6,14 @@ import android.text.Editable import android.text.TextWatcher import android.view.LayoutInflater import android.view.MenuItem -import android.view.View import android.view.WindowManager -import android.widget.AdapterView import android.widget.ArrayAdapter +import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import kotlinx.android.synthetic.main.dialog_default_query.* import kotlinx.android.synthetic.main.dialog_default_query.view.* import xyz.quaver.pupil.types.Tags import xyz.quaver.pupil.util.Histories @@ -140,7 +138,8 @@ class SettingsActivity : AppCompatActivity() { setOnPreferenceClickListener { val dialogView = LayoutInflater.from(context).inflate( R.layout.dialog_default_query, - null + LinearLayout(context), + false ) val tags = Tags.parse( @@ -164,6 +163,7 @@ class SettingsActivity : AppCompatActivity() { val tag = languages[tags.first { it.area == "language" }.tag] if (tag != null) { setSelection( + @Suppress("UNCHECKED_CAST") (adapter as ArrayAdapter).getPosition(tag) ) tags.removeByArea("language") 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 8e84e6a6..ea653cf4 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -6,7 +6,6 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.item_reader.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/app/src/main/java/xyz/quaver/pupil/util/history.kt b/app/src/main/java/xyz/quaver/pupil/util/history.kt index 8ebfaad1..7ddf2551 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/history.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/history.kt @@ -23,10 +23,6 @@ class Histories(private val file: File) : ArrayList() { companion object { lateinit var default: Histories - - fun load(file: File) : Histories { - return Histories(file).load() - } } @UseExperimental(ImplicitReflectionSerializer::class) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index a4de07d9..ed39440d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -42,6 +42,6 @@ 홈페이지 문의 준비 중 - 준비중입니다.\n만화 화면에서 사진을 길게 누르면 스크롤 방식이 바뀝니다. 알고 계셨나요? :) + 준비중입니다. 전체 화면 \ 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 97f25378..e004b06b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,7 +34,7 @@ Email me! WIP - While in progress!\nOne thing might you don\'t know:\nLong tap an image in reader will change scrolling mode! Go try it :) + While in progress! Update available Download started From 369fa679a6c9b663158137ed85c8f9efb80c261c Mon Sep 17 00:00:00 2001 From: tom5079 Date: Fri, 17 May 2019 10:18:11 +0900 Subject: [PATCH 4/8] Several design changes --- .idea/misc.xml | 2 +- app/build.gradle | 1 - .../java/xyz/quaver/pupil/ReaderActivity.kt | 3 +- .../pupil/adapters/GalleryBlockAdapter.kt | 29 +++++++++++++++---- .../main/java/xyz/quaver/pupil/types/Tags.kt | 6 ++-- .../res/drawable/ic_gender_female_white.xml | 8 +++++ .../res/drawable/ic_gender_male_white.xml | 8 +++++ app/src/main/res/layout/activity_reader.xml | 1 + app/src/main/res/layout/tag_chip.xml | 10 +++++++ app/src/main/res/values/colors.xml | 3 ++ .../src/main/java/xyz/quaver/hitomi/common.kt | 16 ---------- 11 files changed, 60 insertions(+), 27 deletions(-) create mode 100644 app/src/main/res/drawable/ic_gender_female_white.xml create mode 100644 app/src/main/res/drawable/ic_gender_male_white.xml create mode 100644 app/src/main/res/layout/tag_chip.xml 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 c7cfaa59..22b0cb32 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,7 +40,6 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation "ru.noties.markwon:core:${markwonVersion}" - implementation 'com.shawnlin:number-picker:2.4.8' implementation 'com.github.clans:fab:1.6.4' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.0' diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 3bec8707..7ba74574 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -1,6 +1,7 @@ package xyz.quaver.pupil import android.os.Bundle +import android.util.Log import android.view.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity @@ -255,7 +256,7 @@ class ReaderActivity : AppCompatActivity() { menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/$gallerySize" } - reader.chunked(8).forEach { chunked -> + reader.chunked(4).forEach { chunked -> chunked.map { async(Dispatchers.IO) { val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url 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 c32ffb78..c7c4f67e 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import androidx.cardview.widget.CardView +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* @@ -14,8 +15,8 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import xyz.quaver.hitomi.GalleryBlock -import xyz.quaver.hitomi.toTag import xyz.quaver.pupil.R +import xyz.quaver.pupil.types.Tag class GalleryBlockAdapter(private val galleries: List>>) : RecyclerView.Adapter() { @@ -110,11 +111,29 @@ class GalleryBlockAdapter(private val galleries: List { + chip.setChipBackgroundColorResource(R.color.material_blue_100) + chip.setTextColor(ContextCompat.getColor(context, android.R.color.white)) + ContextCompat.getDrawable(context, R.drawable.ic_gender_male_white) } - ) + "female" -> { + chip.setChipBackgroundColorResource(R.color.material_pink_100) + chip.setTextColor(ContextCompat.getColor(context, android.R.color.white)) + ContextCompat.getDrawable(context, R.drawable.ic_gender_female_white) + } + else -> null + } + + chip.chipIcon = icon + chip.text = Tag.parse(it).tag.wordCapitalize() + + galleryblock_tag_group.addView(chip) } } } 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 7abc6c97..083674bd 100644 --- a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt +++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt @@ -2,7 +2,7 @@ package xyz.quaver.pupil.types data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) { companion object { - fun parseTag(tag: String) : Tag { + fun parse(tag: String) : Tag { if (tag.first() == '-') { tag.substring(1).split(Regex(":"), 2).let { return when(it.size) { @@ -49,7 +49,7 @@ class Tags(tag: List?) : ArrayList() { return Tags( tags.split(' ').map { if (it.isNotEmpty()) - Tag.parseTag(it) + Tag.parse(it) else null } @@ -74,7 +74,7 @@ class Tags(tag: List?) : ArrayList() { } fun add(element: String): Boolean { - return super.add(Tag.parseTag(element)) + return super.add(Tag.parse(element)) } fun remove(element: String) { diff --git a/app/src/main/res/drawable/ic_gender_female_white.xml b/app/src/main/res/drawable/ic_gender_female_white.xml new file mode 100644 index 00000000..6620153b --- /dev/null +++ b/app/src/main/res/drawable/ic_gender_female_white.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_gender_male_white.xml b/app/src/main/res/drawable/ic_gender_male_white.xml new file mode 100644 index 00000000..a6e1a428 --- /dev/null +++ b/app/src/main/res/drawable/ic_gender_male_white.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index f77b8d6b..edc95288 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/dark_gray" tools:context=".ReaderActivity"> + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index fcd0a08d..2f09f814 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -4,4 +4,7 @@ #0093c4 #D81B60 #FFFFFF + + #d81b60 + #1976d2 diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/common.kt b/libpupil/src/main/java/xyz/quaver/hitomi/common.kt index 23b705da..bc3c181b 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/common.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/common.kt @@ -2,22 +2,6 @@ package xyz.quaver.hitomi const val protocol = "https:" -fun String.toTag() : String { - if (this.indexOf(':') > -1) { - val split = this.split(':') - - val field = split[0] - val term = split[1] - - when(field) { - "male" -> return "$term ♂" - "female" -> return "$term ♀" - } - } - - return this -} - //common.js var adapose = false const val numberOfFrontends = 2 From ba37740c6eb00604511bcdd33c35679b56210cfc Mon Sep 17 00:00:00 2001 From: tom5079 Date: Fri, 17 May 2019 11:07:07 +0900 Subject: [PATCH 5/8] Added Bug Tracking System --- app/build.gradle | 3 +++ app/proguard-rules.pro | 9 +++++++- app/src/main/AndroidManifest.xml | 3 ++- app/src/main/java/xyz/quaver/pupil/Pupil.kt | 22 +++++++++++++++++++ .../java/xyz/quaver/pupil/ReaderActivity.kt | 2 +- .../java/xyz/quaver/pupil/util/history.kt | 1 + .../java/xyz/quaver/pupil/util/progress.kt | 2 ++ .../main/java/xyz/quaver/pupil/util/update.kt | 1 + app/src/main/res/layout/activity_reader.xml | 5 +++++ build.gradle | 7 ++++++ .../main/java/xyz/quaver/hitomi/readers.kt | 1 - .../src/main/java/xyz/quaver/hiyobi/reader.kt | 1 + 12 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/Pupil.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/util/progress.kt diff --git a/app/build.gradle b/app/build.gradle index 22b0cb32..3e89689a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,6 +41,9 @@ dependencies { 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.0' androidTestImplementation 'androidx.test:runner:1.1.1' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb434..efb4fd24 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,11 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-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 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0e380367..8a646c94 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,8 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme" + android:name=".Pupil"> + Fn.reportException(t, Exception(e), Severity.FATAL) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 7ba74574..91e54659 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -14,6 +14,7 @@ import kotlinx.android.synthetic.main.activity_reader.* import kotlinx.android.synthetic.main.activity_reader.view.* import kotlinx.android.synthetic.main.dialog_numberpicker.view.* import kotlinx.coroutines.* +import kotlinx.io.IOException import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.list @@ -148,7 +149,6 @@ class ReaderActivity : AppCompatActivity() { override fun onDestroy() { super.onDestroy() - loadJob?.cancel() } override fun onBackPressed() { diff --git a/app/src/main/java/xyz/quaver/pupil/util/history.kt b/app/src/main/java/xyz/quaver/pupil/util/history.kt index 7ddf2551..b0ad4395 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/history.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/history.kt @@ -1,5 +1,6 @@ package xyz.quaver.pupil.util +import kotlinx.io.IOException import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration diff --git a/app/src/main/java/xyz/quaver/pupil/util/progress.kt b/app/src/main/java/xyz/quaver/pupil/util/progress.kt new file mode 100644 index 00000000..554cfe05 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/util/progress.kt @@ -0,0 +1,2 @@ +package xyz.quaver.pupil.util + 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..198c82a2 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 kotlinx.io.IOException import kotlinx.serialization.json.* import java.net.URL diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index edc95288..0e0c6bd7 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -49,6 +49,11 @@ app:fab_label="@string/reader_fab_fullscreen" app:fab_size="mini"/> + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9b93e54a..2853a5c4 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,13 @@ allprojects { repositories { google() jcenter() + maven { + url "s3://finotescore-android/release" + credentials(AwsCredentials) { + accessKey = "AKIAJ7TPIN63PV5SWK3A" + secretKey = "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" + } + } } } diff --git a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt index 36b9811d..0be173e9 100644 --- a/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt +++ b/libpupil/src/main/java/xyz/quaver/hitomi/readers.kt @@ -28,7 +28,6 @@ fun getReader(galleryID: Int) : Reader { val galleryInfoUrl = "https://ltn.hitomi.la/galleries/$galleryID.js" try { - val doc = Jsoup.connect(readerUrl).get() val images = doc.select(".img-url").map { diff --git a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt index 2f201385..003b0c55 100644 --- a/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt +++ b/libpupil/src/main/java/xyz/quaver/hiyobi/reader.kt @@ -1,5 +1,6 @@ package xyz.quaver.hiyobi +import kotlinx.io.IOException import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.json.content From e787428e6fd9ba4893c8bbd702f43f033f32f964 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sat, 18 May 2019 10:37:58 +0900 Subject: [PATCH 6/8] Download added --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 3 +- .../java/xyz/quaver/pupil/MainActivity.kt | 7 +- app/src/main/java/xyz/quaver/pupil/Pupil.kt | 27 +- .../java/xyz/quaver/pupil/ReaderActivity.kt | 237 +++++++++--------- .../pupil/adapters/GalleryBlockAdapter.kt | 92 ++++++- .../quaver/pupil/util/GalleryDownloader.kt | 232 +++++++++++++++++ .../java/xyz/quaver/pupil/util/history.kt | 2 - .../java/xyz/quaver/pupil/util/progress.kt | 2 - .../res/drawable-anydpi/ic_downloading.xml | 55 ++++ app/src/main/res/drawable/ic_download.xml | 5 + app/src/main/res/layout/activity_reader.xml | 14 +- app/src/main/res/layout/item_galleryblock.xml | 10 +- app/src/main/res/layout/tag_chip.xml | 7 +- app/src/main/res/values-ja/strings.xml | 7 + app/src/main/res/values-ko/strings.xml | 7 + app/src/main/res/values/strings.xml | 12 +- .../java/xyz/quaver/pupil/ExampleUnitTest.kt | 1 - .../test/java/xyz/quaver/hitomi/UnitTest.kt | 4 +- 19 files changed, 566 insertions(+), 159 deletions(-) create mode 100644 app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt delete mode 100644 app/src/main/java/xyz/quaver/pupil/util/progress.kt create mode 100644 app/src/main/res/drawable-anydpi/ic_downloading.xml create mode 100644 app/src/main/res/drawable/ic_download.xml diff --git a/app/build.gradle b/app/build.gradle index 3e89689a..3fef32d8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 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" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8a646c94..766a3ca7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,7 +25,8 @@ + android:parentActivityName=".MainActivity" + android:configChanges="keyboardHidden|orientation|screenSize"/> diff --git a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt index 6e9f18f7..951ff28a 100644 --- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt @@ -218,7 +218,9 @@ class MainActivity : AppCompatActivity() { private fun setupRecyclerView() { with(main_recyclerview) { - adapter = GalleryBlockAdapter(galleries) + adapter = GalleryBlockAdapter(galleries).apply { + + } addOnScrollListener( object: RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { @@ -235,8 +237,7 @@ class MainActivity : AppCompatActivity() { ItemClickSupport.addTo(this).setOnItemClickListener { _, position, _ -> val intent = Intent(this@MainActivity, ReaderActivity::class.java) val gallery = galleries[position].first - intent.putExtra("GALLERY_ID", gallery.id) - intent.putExtra("GALLERY_TITLE", gallery.title) + intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)) //TODO: Maybe sprinke some transitions will be nice :D startActivity(intent) diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt index bf5d8b3b..8e1184dd 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt @@ -1,21 +1,40 @@ package xyz.quaver.pupil -import android.content.Intent -import android.os.Process +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 android.util.SparseArray import com.finotes.android.finotescore.Fn import com.finotes.android.finotescore.ObservableApplication import com.finotes.android.finotescore.Severity +import kotlinx.coroutines.Job class Pupil : ObservableApplication() { override fun onCreate() { + val preference = PreferenceManager.getDefaultSharedPreferences(this) + super.onCreate() Fn.init(this) Fn.enableFrameDetection() - Thread.setDefaultUncaughtExceptionHandler { t, e -> - Fn.reportException(t, Exception(e), Severity.FATAL) + if (!preference.getBoolean("channel_created", false)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val channel = NotificationChannel("download", getString(R.string.channel_download), NotificationManager.IMPORTANCE_LOW).apply { + description = getString(R.string.channel_download_description) + enableLights(false) + enableVibration(false) + lockscreenVisibility = Notification.VISIBILITY_SECRET + } + manager.createNotificationChannel(channel) + } + + preference.edit().putBoolean("channel_created", true).apply() } } diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 91e54659..039be0dc 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -1,7 +1,7 @@ package xyz.quaver.pupil +import android.graphics.drawable.Drawable import android.os.Bundle -import android.util.Log import android.view.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity @@ -10,37 +10,33 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.PagerSnapHelper import androidx.recyclerview.widget.RecyclerView +import androidx.vectordrawable.graphics.drawable.Animatable2Compat +import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import kotlinx.android.synthetic.main.activity_reader.* import kotlinx.android.synthetic.main.activity_reader.view.* import kotlinx.android.synthetic.main.dialog_numberpicker.view.* -import kotlinx.coroutines.* -import kotlinx.io.IOException +import kotlinx.coroutines.CoroutineScope +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.Reader -import xyz.quaver.hitomi.ReaderItem -import xyz.quaver.hitomi.getReader -import xyz.quaver.hitomi.getReferer +import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.pupil.adapters.ReaderAdapter +import xyz.quaver.pupil.util.GalleryDownloader import xyz.quaver.pupil.util.ItemClickSupport -import java.io.File -import java.io.FileOutputStream -import java.net.URL -import javax.net.ssl.HttpsURLConnection class ReaderActivity : AppCompatActivity() { private val images = ArrayList() - private var galleryID = 0 - private var gallerySize: Int = 0 - private var currentPage: Int = 0 - private lateinit var reader: Deferred - private var loadJob: Job? = null + private lateinit var galleryBlock: GalleryBlock + private var gallerySize = 0 + private var currentPage = 0 private var isScroll = true private var isFullscreen = false + private lateinit var downloader: GalleryDownloader + private val snapHelper = PagerSnapHelper() private var menu: Menu? = null @@ -54,54 +50,20 @@ class ReaderActivity : AppCompatActivity() { setContentView(R.layout.activity_reader) - supportActionBar?.title = intent.getStringExtra("GALLERY_TITLE") + galleryBlock = Json(JsonConfiguration.Stable).parse( + GalleryBlock.serializer(), + intent.getStringExtra("galleryblock") + ) - galleryID = intent.getIntExtra("GALLERY_ID", 0) - reader = CoroutineScope(Dispatchers.IO).async { - val json = Json(JsonConfiguration.Stable) - val serializer = ReaderItem.serializer().list - val preference = PreferenceManager.getDefaultSharedPreferences(this@ReaderActivity) - val isHiyobi = preference.getBoolean("use_hiyobi", false) + supportActionBar?.title = galleryBlock.title + supportActionBar?.setDisplayHomeAsUpEnabled(false) - val cache = when { - isHiyobi -> File(cacheDir, "imageCache/$galleryID/reader-hiyobi.json") - else -> File(cacheDir, "imageCache/$galleryID/reader.json") - } - - if (cache.exists()) { - val cached = json.parse(serializer, cache.readText()) - - if (cached.isNotEmpty()) - return@async cached - } - - val reader = when { - isHiyobi -> { - xyz.quaver.hiyobi.getReader(galleryID).let { - when { - it.isEmpty() -> getReader(galleryID) - else -> it - } - } - } - else -> { - getReader(galleryID) - } - } - - if (reader.isEmpty()) - finish() - - if (!cache.parentFile.exists()) - cache.parentFile.mkdirs() - - cache.writeText(json.stringify(serializer, reader)) - - reader - } + initDownloader() initView() - loadImages() + + if (!downloader.notify) + downloader.start() } override fun onResume() { @@ -149,6 +111,9 @@ class ReaderActivity : AppCompatActivity() { override fun onDestroy() { super.onDestroy() + + if (!downloader.notify) + downloader.cancel() } override fun onBackPressed() { @@ -166,6 +131,85 @@ class ReaderActivity : AppCompatActivity() { } } + private fun initDownloader() { + var d: GalleryDownloader? = GalleryDownloader.get(galleryBlock.id) + + if (d == null) { + d = GalleryDownloader(this, galleryBlock) + } + + downloader = d.apply { + onReaderLoadedHandler = { + CoroutineScope(Dispatchers.Main).launch { + with(reader_progressbar) { + max = it.size + progress = 0 + + visibility = View.VISIBLE + } + + gallerySize = it.size + menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.size}" + } + } + onProgressHandler = { + CoroutineScope(Dispatchers.Main).launch { + reader_progressbar.progress = it + } + } + onDownloadedHandler = { + CoroutineScope(Dispatchers.Main).launch { + if (images.isEmpty()) { + images.addAll(it) + reader_recyclerview.adapter?.notifyDataSetChanged() + } else { + images.add(it.last()) + reader_recyclerview.adapter?.notifyItemInserted(images.size-1) + } + } + } + onErrorHandler = { + downloader.notify = false + } + onCompleteHandler = { + CoroutineScope(Dispatchers.Main).launch { + reader_progressbar.visibility = View.GONE + } + } + onNotifyChangedHandler = { notify -> + val fab = reader_fab_download + + if (notify) { + val icon = AnimatedVectorDrawableCompat.create(this, R.drawable.ic_downloading) + icon?.registerAnimationCallback(object: Animatable2Compat.AnimationCallback() { + override fun onAnimationEnd(drawable: Drawable?) { + if (downloader.notify) + fab.post { + icon.start() + fab.labelText = getString(R.string.reader_fab_download_cancel) + } + else + fab.post { + fab.setImageResource(R.drawable.ic_download) + fab.labelText = getString(R.string.reader_fab_download) + } + } + }) + + fab.setImageDrawable(icon) + icon?.start() + } else { + fab.setImageResource(R.drawable.ic_download) + } + } + } + + if (downloader.notify) { + downloader.invokeOnReaderLoaded() + downloader.invokeOnNotifyChanged() + } + } + private fun initView() { with(reader_recyclerview) { adapter = ReaderAdapter(images) @@ -208,6 +252,13 @@ class ReaderActivity : AppCompatActivity() { reader_fab.close(true) } + + reader_fab_download.setOnClickListener { + downloader.notify = !downloader.notify + + if (!downloader.notify) + downloader.clearNotification() + } } private fun fullscreen(isFullscreen: Boolean) { @@ -237,68 +288,4 @@ class ReaderActivity : AppCompatActivity() { (reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0) } - - private fun loadImages() { - fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp" - - loadJob = CoroutineScope(Dispatchers.Default).launch { - val reader = reader.await() - - launch(Dispatchers.Main) { - with(reader_progressbar) { - max = reader.size - progress = 0 - - visibility = View.VISIBLE - } - - gallerySize = reader.size - menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/$gallerySize" - } - - reader.chunked(4).forEach { chunked -> - chunked.map { - async(Dispatchers.IO) { - val url = if (it.galleryInfo?.haswebp == 1) webpUrlFromUrl(it.url) else it.url - - val fileName: String - - with(url) { - fileName = substring(lastIndexOf('/')+1) - } - - val cache = File(cacheDir, "/imageCache/$galleryID/$fileName") - - if (!cache.exists()) - try { - with(URL(url).openConnection() as HttpsURLConnection) { - setRequestProperty("Referer", getReferer(galleryID)) - - if (!cache.parentFile.exists()) - cache.parentFile.mkdirs() - - inputStream.copyTo(FileOutputStream(cache)) - } - } catch (e: Exception) { - cache.delete() - } - - cache.absolutePath - } - }.forEach { - val cache = it.await() - - launch(Dispatchers.Main) { - images.add(cache) - reader_recyclerview.adapter?.notifyItemInserted(images.size - 1) - reader_progressbar.progress++ - } - } - } - - launch(Dispatchers.Main) { - reader_progressbar.visibility = View.GONE - } - } - } } \ 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 c7c4f67e..54166f8e 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -1,6 +1,7 @@ package xyz.quaver.pupil.adapters import android.graphics.BitmapFactory +import android.util.SparseArray import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -14,9 +15,17 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred 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.pupil.R import xyz.quaver.pupil.types.Tag +import java.io.File +import java.util.* +import kotlin.collections.ArrayList +import kotlin.concurrent.schedule class GalleryBlockAdapter(private val galleries: List>>) : RecyclerView.Adapter() { @@ -35,9 +44,12 @@ class GalleryBlockAdapter(private val galleries: List() - class ViewHolder(val view: CardView) : RecyclerView.ViewHolder(view) - class ProgressViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view) + private var onChipClickedHandler = ArrayList<((Tag) -> Unit)>() + + class ViewHolder(val view: CardView, var galleryID: Int? = null) : RecyclerView.ViewHolder(view) + class ProgressViewHolder(val view: LinearLayout) : RecyclerView.ViewHolder(view) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { when(viewType) { @@ -71,16 +83,62 @@ class GalleryBlockAdapter(private val galleries: List View.GONE + false -> View.VISIBLE + } + } } - override fun getItemCount() = if (galleries.isEmpty()) 0 else galleries.size+(if (noMore) 0 else 1) + override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { + super.onViewDetachedFromWindow(holder) + + if (holder is ViewHolder) { + val galleryID = holder.galleryID ?: return + val task = refreshTasks.get(galleryID) ?: return + + refreshTasks.remove(galleryID) + task.cancel() + } + } + + override fun getItemCount() = if (galleries.isEmpty()) 0 else galleries.size+1 override fun getItemViewType(position: Int): Int { return when { diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt new file mode 100644 index 00000000..ef36b282 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -0,0 +1,232 @@ +package xyz.quaver.pupil.util + +import android.app.PendingIntent +import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.util.SparseArray +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.app.TaskStackBuilder +import androidx.preference.PreferenceManager +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.pupil.R +import xyz.quaver.pupil.ReaderActivity +import java.io.File +import java.io.FileOutputStream +import java.net.URL +import javax.net.ssl.HttpsURLConnection + +class GalleryDownloader( + base: Context, + private val galleryBlock: GalleryBlock +) : ContextWrapper(base) { + + var notify: Boolean = false + set(value) { + if (value) { + field = true + notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + + if (downloadJob?.isActive != true) + field = false + } else { + field = false + } + + onNotifyChangedHandler?.invoke(value) + } + + private val reader: Deferred + private var downloadJob: Job? = null + + private lateinit var notificationBuilder: NotificationCompat.Builder + private lateinit var notificationManager: NotificationManagerCompat + + var onReaderLoadedHandler: ((Reader) -> Unit)? = null + var onProgressHandler: ((Int) -> Unit)? = null + var onDownloadedHandler: ((List) -> Unit)? = null + var onErrorHandler: (() -> Unit)? = null + var onCompleteHandler: (() -> Unit)? = null + var onNotifyChangedHandler: ((Boolean) -> Unit)? = null + + companion object : SparseArray() + + init { + put(galleryBlock.id, this) + + initNotification() + + reader = CoroutineScope(Dispatchers.IO).async { + val json = Json(JsonConfiguration.Stable) + val serializer = ReaderItem.serializer().list + val preference = PreferenceManager.getDefaultSharedPreferences(this@GalleryDownloader) + val useHiyobi = preference.getBoolean("use_hiyobi", false) + + //Check cache + val cache = File(cacheDir, "imageCache/${galleryBlock.id}/reader.json") + + if (cache.exists()) { + val cached = json.parse(serializer, cache.readText()) + + if (cached.isNotEmpty()) + return@async cached + } + + //Cache doesn't exist. Load from internet + val reader = when { + useHiyobi -> { + xyz.quaver.hiyobi.getReader(galleryBlock.id).let { + when { + it.isEmpty() -> getReader(galleryBlock.id) + else -> it + } + } + } + else -> { + getReader(galleryBlock.id) + } + } + + //Could not retrieve reader + if (reader.isEmpty()) + throw IOException("Can't retrieve Reader") + + //Save cache + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() + + cache.writeText(json.stringify(serializer, reader)) + + reader + } + } + + private fun webpUrlFromUrl(url: String) = url.replace("/galleries/", "/webp/") + ".webp" + + fun start() { + downloadJob = CoroutineScope(Dispatchers.Default).launch { + val reader = reader.await() + + val list = ArrayList() + + onReaderLoadedHandler?.invoke(reader) + + notificationBuilder + .setProgress(reader.size, 0, false) + .setContentText("0/${reader.size}") + + reader.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 (notify) + 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(cacheDir, "/imageCache/${galleryBlock.id}/images/$name.$ext") + + if (!cache.exists()) + try { + with(URL(url).openConnection() as HttpsURLConnection) { + setRequestProperty("Referer", getReferer(galleryBlock.id)) + + if (!cache.parentFile.exists()) + cache.parentFile.mkdirs() + + inputStream.copyTo(FileOutputStream(cache)) + } + } catch (e: Exception) { + cache.delete() + + onErrorHandler?.invoke() + + notificationBuilder + .setContentTitle(galleryBlock.title) + .setContentText(getString(R.string.reader_notification_error)) + .setProgress(0, 0, false) + + notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + } + + cache.absolutePath + } + }.forEach { + list.add(it.await()) + onDownloadedHandler?.invoke(list) + } + } + + onCompleteHandler?.invoke() + + notificationBuilder + .setContentTitle(galleryBlock.title) + .setContentText(getString(R.string.reader_notification_complete)) + .setProgress(0, 0, false) + + if (notify) + notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + + notify = false + + remove(galleryBlock.id) + } + } + + fun cancel() { + downloadJob?.cancel() + + remove(galleryBlock.id) + } + + fun invokeOnReaderLoaded() { + CoroutineScope(Dispatchers.Default).launch { + onReaderLoadedHandler?.invoke(reader.await()) + } + } + + fun clearNotification() { + notificationManager.cancel(galleryBlock.id) + } + + fun invokeOnNotifyChanged() { + onNotifyChangedHandler?.invoke(notify) + } + + private fun initNotification() { + val intent = Intent(this, ReaderActivity::class.java).apply { + putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), galleryBlock)) + } + val pendingIntent = TaskStackBuilder.create(this).run { + addNextIntentWithParentStack(intent) + getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) + } + + notificationBuilder = NotificationCompat.Builder(this, "download").apply { + setContentTitle(galleryBlock.title) + 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) + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/history.kt b/app/src/main/java/xyz/quaver/pupil/util/history.kt index b0ad4395..a7fdce46 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/history.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/history.kt @@ -1,6 +1,5 @@ package xyz.quaver.pupil.util -import kotlinx.io.IOException import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration @@ -8,7 +7,6 @@ import kotlinx.serialization.parseList import kotlinx.serialization.stringify import java.io.File - class Histories(private val file: File) : ArrayList() { init { diff --git a/app/src/main/java/xyz/quaver/pupil/util/progress.kt b/app/src/main/java/xyz/quaver/pupil/util/progress.kt deleted file mode 100644 index 554cfe05..00000000 --- a/app/src/main/java/xyz/quaver/pupil/util/progress.kt +++ /dev/null @@ -1,2 +0,0 @@ -package xyz.quaver.pupil.util - diff --git a/app/src/main/res/drawable-anydpi/ic_downloading.xml b/app/src/main/res/drawable-anydpi/ic_downloading.xml new file mode 100644 index 00000000..9188c5f3 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_downloading.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_download.xml b/app/src/main/res/drawable/ic_download.xml new file mode 100644 index 00000000..c95797c1 --- /dev/null +++ b/app/src/main/res/drawable/ic_download.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index 0e0c6bd7..59ff93c1 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -41,6 +41,15 @@ android:layout_gravity="bottom|end" app:menu_colorNormal="@color/colorAccent"> + + - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_galleryblock.xml b/app/src/main/res/layout/item_galleryblock.xml index 69883a53..594363a9 100644 --- a/app/src/main/res/layout/item_galleryblock.xml +++ b/app/src/main/res/layout/item_galleryblock.xml @@ -18,6 +18,14 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + - - - \ No newline at end of file + app:chipCornerRadius="100dp"/> \ 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 a0018929..eff3b9be 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -44,4 +44,11 @@ 準備中 準備中です。 フルスクリーン + ダウンロード + ダウンロードの進行を通知 + バックグラウンドダウンロード + ダウンロード中… + ダウンロード完了 + ダウンロードエラー + バックグラウンドダウンロード中止 \ 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 ed39440d..40800583 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -44,4 +44,11 @@ 준비 중 준비중입니다. 전체 화면 + 다운로드 + 다운로드 상태 알림 + 백그라운드 다운로드 + 다운로드 중… + 다운로드 완료 + 다운로드 오류 + 백그라운드 다운로드 취소 \ 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 e004b06b..b137f139 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - - Pupil + + Pupil https://api.github.com/repos/tom5079/Pupil-issue/releases Pupil-v(\\d+\\.)+\\d+\\.apk @@ -22,6 +22,9 @@ Denying any permission can deactivate some functions + Download + Shows download status + Search No result @@ -49,6 +52,11 @@ Go to page Fullscreen + Background download + Cancel background download + Downloading… + Download complete + Download error Settings Search Settings diff --git a/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt b/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt index fec18cac..c4a08536 100644 --- a/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt +++ b/app/src/test/java/xyz/quaver/pupil/ExampleUnitTest.kt @@ -12,7 +12,6 @@ import org.junit.Test class ExampleUnitTest { @Test - @ImplicitReflectionSerializer fun test() { } diff --git a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt index b573e193..e85582af 100644 --- a/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt +++ b/libpupil/src/test/java/xyz/quaver/hitomi/UnitTest.kt @@ -7,9 +7,7 @@ import java.net.URL class UnitTest { @Test fun test() { - val f = File("C:/Users/tom50/Workspace/Pupil/nodir/nodir/asdf.txt") - - f.delete() + print(File("C:\\asdf").list()?.size ?: 0) } @Test From 8de3eace8ab3d599e16b67a76604574c50654dba Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sat, 18 May 2019 10:51:54 +0900 Subject: [PATCH 7/8] Tag search added --- .../java/xyz/quaver/pupil/MainActivity.kt | 25 ++++++++++++++++++- .../pupil/adapters/GalleryBlockAdapter.kt | 2 +- .../main/java/xyz/quaver/pupil/types/Tags.kt | 4 +++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt index 951ff28a..e02e7429 100644 --- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt @@ -8,6 +8,7 @@ import android.text.* import android.text.style.AlignmentSpan import android.view.View import android.view.WindowManager +import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat @@ -127,8 +128,8 @@ class MainActivity : AppCompatActivity() { true } - setupRecyclerView() setupSearchBar() + setupRecyclerView() fetchGalleries(query) loadBlocks() } @@ -136,6 +137,17 @@ class MainActivity : AppCompatActivity() { override fun onBackPressed() { if (main_drawer_layout.isDrawerOpen(GravityCompat.START)) main_drawer_layout.closeDrawer(GravityCompat.START) + else if (query.isNotEmpty()) { + runOnUiThread { + query = "" + findViewById(R.id.search_bar_text).setText(query, TextView.BufferType.EDITABLE) + + cancelFetch() + clearGalleries() + fetchGalleries(query) + loadBlocks() + } + } else super.onBackPressed() } @@ -219,7 +231,18 @@ class MainActivity : AppCompatActivity() { private fun setupRecyclerView() { with(main_recyclerview) { adapter = GalleryBlockAdapter(galleries).apply { + onChipClickedHandler.add { + post { + query = it.toQuery() + this@MainActivity.findViewById(R.id.search_bar_text) + .setText(query, TextView.BufferType.EDITABLE) + cancelFetch() + clearGalleries() + fetchGalleries(query) + loadBlocks() + } + } } addOnScrollListener( object: RecyclerView.OnScrollListener() { 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 54166f8e..a7a3f4d9 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -46,7 +46,7 @@ class GalleryBlockAdapter(private val galleries: List() - private var onChipClickedHandler = ArrayList<((Tag) -> Unit)>() + val onChipClickedHandler = ArrayList<((Tag) -> Unit)>() class ViewHolder(val view: CardView, var galleryID: Int? = null) : RecyclerView.ViewHolder(view) class ProgressViewHolder(val view: LinearLayout) : RecyclerView.ViewHolder(view) 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 083674bd..b7e8039b 100644 --- a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt +++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt @@ -27,6 +27,10 @@ data class Tag(val area: String?, val tag: String, val isNegative: Boolean = fal } } + fun toQuery(): String { + return toString().replace(' ', '_') + } + override fun equals(other: Any?): Boolean { if (other !is Tag) return false From fd83f987dd145da98430c0a40b17512d6d4d88b3 Mon Sep 17 00:00:00 2001 From: tom5079 Date: Sat, 18 May 2019 23:20:06 +0900 Subject: [PATCH 8/8] Bug fixed --- .../java/xyz/quaver/pupil/MainActivity.kt | 95 ++++++++++++++++--- .../java/xyz/quaver/pupil/ReaderActivity.kt | 1 + .../pupil/adapters/GalleryBlockAdapter.kt | 57 ++++++++--- .../quaver/pupil/util/GalleryDownloader.kt | 33 +++++-- .../res/drawable-anydpi/ic_downloading.xml | 6 +- .../ic_progressbar_complete.xml | 18 ++++ app/src/main/res/drawable/ic_progressbar.xml | 21 ++++ .../main/res/layout/dialog_galleryblock.xml | 23 +++++ app/src/main/res/layout/item_galleryblock.xml | 9 ++ app/src/main/res/values-ja/strings.xml | 3 +- app/src/main/res/values-ko/strings.xml | 9 +- app/src/main/res/values/colors.xml | 5 +- app/src/main/res/values/strings.xml | 2 + 13 files changed, 237 insertions(+), 45 deletions(-) create mode 100644 app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml create mode 100644 app/src/main/res/drawable/ic_progressbar.xml create mode 100644 app/src/main/res/layout/dialog_galleryblock.xml diff --git a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt index e02e7429..a355b261 100644 --- a/app/src/main/java/xyz/quaver/pupil/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/MainActivity.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.preference.PreferenceManager import android.text.* import android.text.style.AlignmentSpan +import android.view.LayoutInflater import android.view.View import android.view.WindowManager import android.widget.TextView @@ -21,6 +22,7 @@ import com.arlib.floatingsearchview.util.view.SearchInputView import com.google.android.material.appbar.AppBarLayout import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main_content.* +import kotlinx.android.synthetic.main.dialog_galleryblock.view.* import kotlinx.coroutines.* import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration @@ -30,10 +32,7 @@ import ru.noties.markwon.Markwon import xyz.quaver.hitomi.* import xyz.quaver.pupil.adapters.GalleryBlockAdapter import xyz.quaver.pupil.types.TagSuggestion -import xyz.quaver.pupil.util.Histories -import xyz.quaver.pupil.util.ItemClickSupport -import xyz.quaver.pupil.util.SetLineOverlap -import xyz.quaver.pupil.util.checkUpdate +import xyz.quaver.pupil.util.* import java.io.File import java.io.FileOutputStream import java.net.URL @@ -47,6 +46,8 @@ class MainActivity : AppCompatActivity() { private var query = "" + private val SETTINGS = 45162 + private var galleryIDs: Deferred>? = null private var loadingJob: Job? = null @@ -164,6 +165,20 @@ class MainActivity : AppCompatActivity() { super.onResume() } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when(requestCode) { + SETTINGS -> { + runOnUiThread { + cancelFetch() + clearGalleries() + fetchGalleries(query) + loadBlocks() + } + } + } + } + private fun checkUpdate() { fun extractReleaseNote(update: JsonObject, locale: String) : String { @@ -257,16 +272,63 @@ class MainActivity : AppCompatActivity() { } } ) - ItemClickSupport.addTo(this).setOnItemClickListener { _, position, _ -> - val intent = Intent(this@MainActivity, ReaderActivity::class.java) - val gallery = galleries[position].first - intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)) + ItemClickSupport.addTo(this) + .setOnItemClickListener { _, position, _ -> + val intent = Intent(this@MainActivity, ReaderActivity::class.java) + val gallery = galleries[position].first + intent.putExtra("galleryblock", Json(JsonConfiguration.Stable).stringify(GalleryBlock.serializer(), gallery)) - //TODO: Maybe sprinke some transitions will be nice :D - startActivity(intent) + //TODO: Maybe sprinke some transitions will be nice :D + startActivity(intent) - Histories.default.add(gallery.id) - } + Histories.default.add(gallery.id) + }.setOnItemLongClickListener { recyclerView, position, v -> + val galleryBlock = galleries[position].first + val view = LayoutInflater.from(this@MainActivity) + .inflate(R.layout.dialog_galleryblock, recyclerView, false) + + val dialog = AlertDialog.Builder(this@MainActivity).apply { + setView(view) + }.create() + + with(view.main_dialog_download) { + text = when(GalleryDownloader.get(galleryBlock.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) + setOnClickListener { + val downloader = GalleryDownloader.get(galleryBlock.id) + if (downloader == null) { + GalleryDownloader(context, galleryBlock, true).start() + Histories.default.add(galleryBlock.id) + } else { + downloader.cancel() + downloader.clearNotification() + } + + dialog.dismiss() + } + } + + view.main_dialog_delete.setOnClickListener { + CoroutineScope(Dispatchers.Default).launch { + with(GalleryDownloader[galleryBlock.id]) { + this?.cancelAndJoin() + this?.clearNotification() + } + val cache = File(cacheDir, "imageCache/${galleryBlock.id}/images/") + cache.deleteRecursively() + + dialog.dismiss() + (adapter as GalleryBlockAdapter).completeFlag.put(galleryBlock.id, false) + } + } + + dialog.show() + + true + } } } @@ -294,7 +356,7 @@ class MainActivity : AppCompatActivity() { with(main_searchview as FloatingSearchView) { setOnMenuItemClickListener { when(it.itemId) { - R.id.main_menu_settings -> startActivity(Intent(this@MainActivity, SettingsActivity::class.java)) + R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), SETTINGS) R.id.main_menu_search -> setSearchFocused(true) } } @@ -404,7 +466,12 @@ class MainActivity : AppCompatActivity() { private fun clearGalleries() { galleries.clear() - main_recyclerview.adapter?.notifyDataSetChanged() + with(main_recyclerview.adapter as GalleryBlockAdapter?) { + this ?: return@with + + this.completeFlag.clear() + this.notifyDataSetChanged() + } main_noresult.visibility = View.INVISIBLE main_progressbar.show() diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 039be0dc..b2022c2b 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -2,6 +2,7 @@ package xyz.quaver.pupil import android.graphics.drawable.Drawable import android.os.Bundle +import android.util.Log import android.view.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity 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 a7a3f4d9..08f73716 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -1,7 +1,10 @@ package xyz.quaver.pupil.adapters import android.graphics.BitmapFactory +import android.graphics.PorterDuff +import android.util.Log import android.util.SparseArray +import android.util.SparseBooleanArray import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -9,6 +12,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.AnimatedVectorDrawableCompat import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.coroutines.CoroutineScope @@ -45,6 +49,7 @@ class GalleryBlockAdapter(private val galleries: List() + val completeFlag = SparseBooleanArray() val onChipClickedHandler = ArrayList<((Tag) -> Unit)>() @@ -121,16 +126,46 @@ class GalleryBlockAdapter(private val galleries: List { - chip.setChipBackgroundColorResource(R.color.material_blue_100) + chip.setChipBackgroundColorResource(R.color.material_blue_700) chip.setTextColor(ContextCompat.getColor(context, android.R.color.white)) ContextCompat.getDrawable(context, R.drawable.ic_gender_male_white) } "female" -> { - chip.setChipBackgroundColorResource(R.color.material_pink_100) + chip.setChipBackgroundColorResource(R.color.material_pink_600) chip.setTextColor(ContextCompat.getColor(context, android.R.color.white)) ContextCompat.getDrawable(context, R.drawable.ic_gender_female_white) } 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 ef36b282..c7c57ab6 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryDownloader.kt @@ -20,11 +20,15 @@ import xyz.quaver.pupil.ReaderActivity import java.io.File import java.io.FileOutputStream import java.net.URL +import java.util.* import javax.net.ssl.HttpsURLConnection +import kotlin.collections.ArrayList +import kotlin.concurrent.schedule class GalleryDownloader( base: Context, - private val galleryBlock: GalleryBlock + private val galleryBlock: GalleryBlock, + _notify: Boolean = false ) : ContextWrapper(base) { var notify: Boolean = false @@ -33,7 +37,7 @@ class GalleryDownloader( field = true notificationManager.notify(galleryBlock.id, notificationBuilder.build()) - if (downloadJob?.isActive != true) + if (!reader.isActive && downloadJob?.isActive != true) field = false } else { field = false @@ -63,6 +67,7 @@ class GalleryDownloader( initNotification() reader = CoroutineScope(Dispatchers.IO).async { + notify = _notify val json = Json(JsonConfiguration.Stable) val serializer = ReaderItem.serializer().list val preference = PreferenceManager.getDefaultSharedPreferences(this@GalleryDownloader) @@ -74,8 +79,10 @@ class GalleryDownloader( if (cache.exists()) { val cached = json.parse(serializer, cache.readText()) - if (cached.isNotEmpty()) + if (cached.isNotEmpty()) { + onReaderLoadedHandler?.invoke(cached) return@async cached + } } //Cache doesn't exist. Load from internet @@ -175,15 +182,17 @@ class GalleryDownloader( onCompleteHandler?.invoke() - notificationBuilder - .setContentTitle(galleryBlock.title) - .setContentText(getString(R.string.reader_notification_complete)) - .setProgress(0, 0, false) + Timer(false).schedule(1000) { + notificationBuilder + .setContentTitle(galleryBlock.title) + .setContentText(getString(R.string.reader_notification_complete)) + .setProgress(0, 0, false) - if (notify) - notificationManager.notify(galleryBlock.id, notificationBuilder.build()) + if (notify) + notificationManager.notify(galleryBlock.id, notificationBuilder.build()) - notify = false + notify = false + } remove(galleryBlock.id) } @@ -195,6 +204,10 @@ class GalleryDownloader( remove(galleryBlock.id) } + suspend fun cancelAndJoin() { + downloadJob?.cancelAndJoin() + } + fun invokeOnReaderLoaded() { CoroutineScope(Dispatchers.Default).launch { onReaderLoadedHandler?.invoke(reader.await()) diff --git a/app/src/main/res/drawable-anydpi/ic_downloading.xml b/app/src/main/res/drawable-anydpi/ic_downloading.xml index 9188c5f3..ac96e761 100644 --- a/app/src/main/res/drawable-anydpi/ic_downloading.xml +++ b/app/src/main/res/drawable-anydpi/ic_downloading.xml @@ -33,7 +33,7 @@ android:valueFrom="0" android:valueTo="0.8" android:valueType="floatType" - android:interpolator="@android:anim/accelerate_decelerate_interpolator"/> + android:interpolator="@android:interpolator/fast_out_slow_in"/> + android:interpolator="@android:interpolator/fast_out_slow_in"/> + android:interpolator="@android:interpolator/fast_out_slow_in"/> diff --git a/app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml b/app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml new file mode 100644 index 00000000..85cae265 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_progressbar_complete.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_progressbar.xml b/app/src/main/res/drawable/ic_progressbar.xml new file mode 100644 index 00000000..3899286b --- /dev/null +++ b/app/src/main/res/drawable/ic_progressbar.xml @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_galleryblock.xml b/app/src/main/res/layout/dialog_galleryblock.xml new file mode 100644 index 00000000..3c9bc82a --- /dev/null +++ b/app/src/main/res/layout/dialog_galleryblock.xml @@ -0,0 +1,23 @@ + + + +