Compare commits

..

23 Commits
4.18 ... 4.19

Author SHA1 Message Date
tom5079
08672d10ac oops 2020-07-23 20:48:24 +09:00
tom5079
b563dae3a8 App rebuilt 2020-07-23 20:37:38 +09:00
tom5079
917f9672dd bug fix - unable to run on older devices
Version 4.18.4
2020-07-23 20:32:45 +09:00
Pupil
9ddb19530b Merge pull request #101 from tom5079/dev
Version 4.18.3
2020-07-12 20:09:12 +09:00
tom5079
431e56a9f1 Version 4.18.3 2020-07-12 20:07:59 +09:00
Pupil
71093aac4c Update README.md 2020-07-12 19:53:28 +09:00
tom5079
47c9e8127e Remove logic for resetting low quality setting 2020-07-07 10:21:30 +09:00
tom5079
24b801b346 resolves #52 2020-07-05 19:45:55 +09:00
tom5079
70608c3ed9 resolves #52 2020-07-05 19:40:14 +09:00
tom5079
f185196e85 Version Update 2020-07-05 19:33:56 +09:00
tom5079
a8766a8bbe Drawer logo fixed 2020-06-23 20:37:20 +09:00
Pupil
27a8c93cfe Merge pull request #97 from tom5079/Pupil-95
Pupil 95
2020-06-23 18:49:57 +09:00
tom5079
a3cd29fda9 Merge remote-tracking branch 'origin/Pupil-95' into Pupil-95 2020-06-23 18:48:24 +09:00
tom5079
adda8ab640 Pupil-95 Search tag icon not visible in light theme 2020-06-23 18:48:13 +09:00
tom5079
1538ea5fc8 Merge remote-tracking branch 'origin/dev' into dev 2020-06-23 18:47:58 +09:00
tom5079
2367a97a54 App built 2020-06-23 18:47:48 +09:00
tom5079
090ec0e4af Auto-Retry
Bug fix
2020-06-23 18:47:48 +09:00
tom5079
de7f552e5c Pupil-95 Search tag icon not visible in light theme 2020-06-23 18:40:43 +09:00
tom5079
d763f5dca0 App built 2020-06-22 09:52:25 +09:00
tom5079
9f41116241 Auto-Retry
Bug fix
2020-06-22 09:48:04 +09:00
Pupil
57faada201 Merge pull request #93 from tom5079/dev
Version 4.18.1
2020-06-21 23:03:59 +09:00
tom5079
1edb95f0c5 Prevent OOM when cache is disabled 2020-06-21 23:03:10 +09:00
tom5079
9f363d8900 idk wtf i'm doin' 2020-06-21 22:43:47 +09:00
29 changed files with 324 additions and 208 deletions

View File

@@ -4,7 +4,6 @@
*Pupil, Hitomi.la viewer for Android*
[![](https://discordapp.com/api/guilds/610452916612104194/embed.png?style=banner2)](https://discord.gg/Stj4b5v)
I can speak English, Japanese and Korean. If you have any questions, head over to my discord server or DM me!
# Screenshot
![Main Screen](https://github.com/tom5079/Pupil/blob/gh-pages/assets/images/main-screenshot.png?raw=true)

View File

@@ -19,8 +19,8 @@ android {
applicationId "xyz.quaver.pupil"
minSdkVersion 16
targetSdkVersion 29
versionCode 53
versionName "4.18"
versionCode 56
versionName "4.18.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
@@ -65,10 +65,10 @@ dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation 'com.google.android.material:material:1.3.0-alpha01'
implementation 'com.google.firebase:firebase-core:17.4.3'
implementation 'com.google.firebase:firebase-analytics:17.4.3'
implementation 'com.google.firebase:firebase-crashlytics:17.1.0'
implementation 'com.google.firebase:firebase-perf:19.0.7'
implementation 'com.google.firebase:firebase-core:17.4.4'
implementation 'com.google.firebase:firebase-analytics:17.4.4'
implementation 'com.google.firebase:firebase-crashlytics:17.1.1'
implementation 'com.google.firebase:firebase-perf:19.0.8'
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
implementation 'com.github.clans:fab:1.6.4'
implementation 'com.github.bumptech.glide:glide:4.11.0'

View File

@@ -32,4 +32,7 @@
}
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
*** rewind();
}
}
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl

View File

@@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"properties": [],
"versionCode": 53,
"versionName": "53",
"versionCode": 56,
"versionName": "56",
"enabled": true,
"outputFile": "app-release.apk"
}

View File

@@ -99,7 +99,7 @@ class ExampleInstrumentedTest {
while(worker.progress.indexOfKey(galleryID) < 0 || worker.progress[galleryID] != null) {
Log.i("PUPILD", worker.progress[galleryID]?.joinToString(" ") ?: "null")
if (worker.progress[galleryID]?.all { !it.isFinite() } == true)
if (worker.progress[galleryID]?.all { it.isInfinite() } == true)
break
}

View File

@@ -71,13 +71,6 @@ class Pupil : MultiDexApplication() {
preference.edit().remove("dl_location").apply()
}
if (!preference.getBoolean("low_quality_reset", false)) {
preference.edit()
.putBoolean("low_quality", true)
.putBoolean("low_quality_reset", true)
.apply()
}
histories = Histories(File(ContextCompat.getDataDir(this), "histories.json"))
favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json"))

View File

@@ -18,29 +18,38 @@
package xyz.quaver.pupil.adapters
import android.app.Activity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.android.synthetic.main.activity_reader.view.*
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import kotlinx.android.synthetic.main.item_reader.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import xyz.quaver.Code
import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.R
import xyz.quaver.pupil.util.download.Cache
import xyz.quaver.pupil.util.download.DownloadWorker
import java.util.*
import kotlin.concurrent.schedule
import kotlin.math.roundToInt
class ReaderAdapter(private val glide: RequestManager,
private val galleryID: Int) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
private val galleryID: Int,
private val activity: Activity) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
var reader: Reader? = null
val timer = Timer()
@@ -88,46 +97,57 @@ class ReaderAdapter(private val glide: RequestManager,
holder.view.reader_index.text = (position+1).toString()
val image = downloadWorker!!.results[galleryID]?.get(position)
val progress = downloadWorker!!.progress[galleryID]?.get(position)
if (progress?.isInfinite() == true && image != null) {
holder.view.reader_item_progressbar.visibility = View.INVISIBLE
val preferences = PreferenceManager.getDefaultSharedPreferences(holder.view.context)
if (preferences.getBoolean("cache_disable", false)) {
val lowQuality = preferences.getBoolean("low_quality", false)
val url = when (reader!!.code) {
Code.HITOMI ->
GlideUrl(
imageUrlFromImage(
galleryID,
reader!!.galleryInfo.files[position],
!lowQuality
)
, LazyHeaders.Builder().addHeader("Referer", getReferer(galleryID)).build())
Code.HIYOBI ->
GlideUrl(createImgList(galleryID, reader!!, lowQuality)[position].path, LazyHeaders.Builder()
.addHeader("User-Agent", user_agent)
.addHeader("Cookie", cookie)
.build())
else -> null
}
holder.view.image.post {
glide
.load(image)
.load(url!!)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.skipMemoryCache(false)
.fitCenter()
.error(R.drawable.image_broken_variant)
.into(holder.view.image)
}
} else {
holder.view.reader_item_progressbar.visibility = View.VISIBLE
val image = Cache(holder.view.context).getImage(galleryID, position)
val progress = downloadWorker!!.progress[galleryID]?.get(position)
glide.clear(holder.view.image)
if (progress?.isInfinite() == true && image != null) {
holder.view.reader_item_progressbar.visibility = View.INVISIBLE
if (progress?.isNaN() == true) {
FirebaseCrashlytics.getInstance().recordException(
DownloadWorker.getInstance(holder.view.context).exception[galleryID]?.get(position)!!
)
holder.view.image.post {
glide
.load(image)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.fitCenter()
.error(R.drawable.image_broken_variant)
.into(holder.view.image)
}
glide
.load(R.drawable.image_broken_variant)
.into(holder.view.image)
Snackbar.make(holder.view.reader_layout, R.string.reader_error_retry, Snackbar.LENGTH_SHORT).apply {
setAction(android.R.string.no) { }
setAction(android.R.string.yes) {
downloadWorker!!.cancel(galleryID)
downloadWorker!!.queue.add(galleryID)
}
}.show()
return
} else {
holder.view.reader_item_progressbar.visibility = View.VISIBLE
glide.clear(holder.view.image)
holder.view.reader_item_progressbar.progress =
if (progress?.isInfinite() == true)
100
@@ -135,11 +155,11 @@ class ReaderAdapter(private val glide: RequestManager,
progress?.roundToInt() ?: 0
holder.view.image.setImageDrawable(null)
}
timer.schedule(1000) {
CoroutineScope(Dispatchers.Main).launch {
notifyItemChanged(position)
timer.schedule(1000) {
CoroutineScope(Dispatchers.Main).launch {
notifyItemChanged(position)
}
}
}
}

View File

@@ -26,6 +26,7 @@ import android.net.Uri
import android.os.Bundle
import android.text.*
import android.text.style.AlignmentSpan
import android.util.TypedValue
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
@@ -144,30 +145,6 @@ class MainActivity : AppCompatActivity() {
preference.edit().putBoolean("https_block_alert", true).apply()
}
if (!preference.getBoolean("apcjsa_option", false)) {
android.app.AlertDialog.Builder(this).apply {
setTitle(R.string.apcjsa_option_title)
setMessage(R.string.apcjsa_option_message)
setPositiveButton(android.R.string.yes) { _, _ ->
val tags = Tags.parse(
preference.getString("default_query", "") ?: ""
)
tags.add("-female:loli")
tags.add("-male:shota")
preference.edit()
.putString("default_query", tags.toString())
.putBoolean("cache_disable", true)
.putBoolean("apcjsa_option", true)
.apply()
}
setNegativeButton(android.R.string.no) { _, _ -> }
}.show()
preference.edit().putBoolean("apcjsa_option", true).apply()
}
}
with(application as Pupil) {
@@ -464,8 +441,10 @@ class MainActivity : AppCompatActivity() {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("cache_disable", false))
Toast.makeText(context, R.string.settings_download_when_cache_disable_warning, Toast.LENGTH_SHORT).show()
else {
if (Cache(context).isDownloading(galleryID)) //download in progress
if (worker.progress.indexOfKey(galleryID) >= 0 && Cache(context).isDownloading(galleryID)) { //download in progress
Cache(context).setDownloading(galleryID, false)
worker.cancel(galleryID)
}
else {
Cache(context).setDownloading(galleryID, true)
@@ -867,6 +846,9 @@ class MainActivity : AppCompatActivity() {
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}"
val color = TypedValue()
theme.resolveAttribute(R.attr.colorControlNormal, color, true)
leftIcon.setImageDrawable(
ResourcesCompat.getDrawable(
resources,
@@ -880,7 +862,7 @@ class MainActivity : AppCompatActivity() {
"artist" -> R.drawable.brush
else -> R.drawable.tag
},
null)
context.theme)
)
with(suggestionView.findViewById<ImageView>(R.id.right_icon)) {

View File

@@ -39,6 +39,9 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
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.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import xyz.quaver.Code
import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R
@@ -99,7 +102,36 @@ class ReaderActivity : AppCompatActivity() {
}
initView()
initDownloader()
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("cache_disable", false)) {
reader_download_progressbar.visibility = View.GONE
CoroutineScope(Dispatchers.IO).launch {
val reader = Cache(this@ReaderActivity).getReader(galleryID)
launch(Dispatchers.Main) initDownloader@{
if (reader == null) {
Snackbar
.make(reader_layout, R.string.reader_failed_to_find_gallery, Snackbar.LENGTH_INDEFINITE)
.show()
return@initDownloader
}
(reader_recyclerview.adapter as ReaderAdapter).apply {
this.reader = reader
notifyDataSetChanged()
}
title = reader.galleryInfo.title ?: ""
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${reader.galleryInfo.files.size}"
menu?.findItem(R.id.reader_type)?.icon = ContextCompat.getDrawable(this@ReaderActivity,
when (reader.code) {
Code.HITOMI -> R.drawable.hitomi
Code.HIYOBI -> R.drawable.ic_hiyobi
else -> android.R.color.transparent
})
}
}
} else
initDownloader()
}
override fun onNewIntent(intent: Intent) {
@@ -232,6 +264,7 @@ class ReaderActivity : AppCompatActivity() {
private fun initDownloader() {
val worker = DownloadWorker.getInstance(this).apply {
cancel(galleryID)
queue.add(galleryID)
}
@@ -248,7 +281,7 @@ class ReaderActivity : AppCompatActivity() {
runOnUiThread {
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
reader_download_progressbar.progress = worker.progress[galleryID]?.count { !it.isFinite() } ?: 0
reader_download_progressbar.progress = worker.progress[galleryID]?.count { it.isInfinite() } ?: 0
reader_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
if (title == getString(R.string.reader_loading)) {
@@ -273,7 +306,7 @@ class ReaderActivity : AppCompatActivity() {
}
}
if (worker.progress[galleryID]?.all { !it.isFinite() } == true) { //Download finished
if (worker.progress[galleryID]?.all { it.isInfinite() } == true) { //Download finished
reader_download_progressbar.visibility = View.GONE
animateDownloadFAB(false)
@@ -284,7 +317,7 @@ class ReaderActivity : AppCompatActivity() {
private fun initView() {
with(reader_recyclerview) {
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID).apply {
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID, this@ReaderActivity).apply {
onItemClickListener = {
if (isScroll) {
isScroll = false
@@ -396,7 +429,7 @@ class ReaderActivity : AppCompatActivity() {
icon?.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) {
val worker = DownloadWorker.getInstance(context)
if (worker.progress[galleryID]?.all { !it.isFinite() } == true) // If download is finished, stop animating
if (worker.progress[galleryID]?.all { it.isInfinite() } == true) // If download is finished, stop animating
post {
setImageResource(R.drawable.ic_download)
labelText = getString(R.string.reader_fab_download_cancel)

View File

@@ -21,7 +21,6 @@ package xyz.quaver.pupil.util.download
import android.content.Context
import android.content.ContextWrapper
import android.util.Base64
import android.util.Log
import android.util.SparseArray
import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
@@ -34,8 +33,10 @@ import xyz.quaver.pupil.util.getCachedGallery
import xyz.quaver.pupil.util.getDownloadDirectory
import xyz.quaver.pupil.util.isParentOf
import xyz.quaver.pupil.util.json
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.net.URL
import java.util.*
import java.util.concurrent.locks.Lock
@@ -67,7 +68,7 @@ class Cache(context: Context) : ContextWrapper(context) {
// Search in this order
// Download -> Cache
fun getCachedGallery(galleryID: Int) = getCachedGallery(this, galleryID).also {
if (!it.exists() && !preference.getBoolean("cache_disable", false))
if (!it.exists())
it.mkdirs()
}
@@ -250,7 +251,7 @@ class Cache(context: Context) : ContextWrapper(context) {
}
fun putImage(galleryID: Int, index: Int, ext: String, data: ByteArray) {
fun putImage(galleryID: Int, index: Int, ext: String, data: InputStream) {
if (preference.getBoolean("cache_disable", false))
return
@@ -260,8 +261,10 @@ class Cache(context: Context) : ContextWrapper(context) {
}
try {
FileOutputStream(cache).use {
it.write(data)
BufferedInputStream(data).use { inputStream ->
FileOutputStream(cache).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
} catch (e: Exception) {
cache.delete()
@@ -285,17 +288,17 @@ class Cache(context: Context) : ContextWrapper(context) {
if (download.isParentOf(cache))
return@launch
Log.i("PUPILD", "MOVING ${cache.canonicalPath} --> ${download.canonicalPath}")
FirebaseCrashlytics.getInstance().log("MOVING ${cache.canonicalPath} --> ${download.canonicalPath}")
cache.copyRecursively(download, true) { file, err ->
Log.i("PUPILD", "MOVING ERROR ${file.canonicalPath} ${err.message}")
FirebaseCrashlytics.getInstance().log("MOVING ERROR ${file.canonicalPath} ${err.message}")
OnErrorAction.SKIP
}
Log.i("PUPILD", "MOVED ${cache.canonicalPath}")
FirebaseCrashlytics.getInstance().log("MOVED ${cache.canonicalPath}")
Log.i("PUPILD", "DELETING ${cache.canonicalPath}")
FirebaseCrashlytics.getInstance().log("DELETING ${cache.canonicalPath}")
cache.deleteRecursively()
Log.i("PUPILD", "DELETED ${cache.canonicalPath}")
FirebaseCrashlytics.getInstance().log("DELETED ${cache.canonicalPath}")
}
}

View File

@@ -128,22 +128,8 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
* SECONDARY VALUE
* 0 <= value < 100 -> Download in progress
* Float.POSITIVE_INFINITY -> Download completed
* Float.NaN -> Exception
*/
val progress = SparseArray<MutableList<Float>?>()
/*
* KEY
* primary galleryID
* secondary index
* PRIMARY VALUE
* MutableList -> Download in progress / Loading
* null -> Gallery doesn't exist
* SECONDARY VALUE
* Throwable -> Exception
* null -> Download in progress / Loading
*/
val exception = SparseArray<MutableList<Throwable?>?>()
val results = SparseArray<MutableList<ByteArray?>?>()
val notification = SparseArray<NotificationCompat.Builder>()
private val loop = loop()
@@ -195,8 +181,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
}
progress.clear()
exception.clear()
results.clear()
notification.clear()
notificationManager.cancelAll()
}
@@ -212,16 +196,11 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
}
progress.remove(galleryID)
exception.remove(galleryID)
results.remove(galleryID)
notification.remove(galleryID)
notificationManager.cancel(galleryID)
if (progress.indexOfKey(galleryID) >= 0)
Cache(this@DownloadWorker).setDownloading(galleryID, false)
}
fun isCompleted(galleryID: Int) = progress[galleryID]?.all { !it.isFinite() } == true
fun isCompleted(galleryID: Int) = progress[galleryID]?.all { it.isInfinite() } == true
private fun queueDownload(galleryID: Int, reader: Reader, index: Int, callback: Callback) {
val lowQuality = preferences.getBoolean("low_quality", false)
@@ -251,8 +230,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
}.build()
client.newCall(request).enqueue(callback)
Log.i("PUPILD", "DOWNLOADING ($galleryID, $index) from ${request.url()}")
}
private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {
@@ -261,8 +238,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
//gallery doesn't exist
if (reader == null) {
progress.put(galleryID, null)
exception.put(galleryID, null)
results.put(galleryID, null)
Cache(this@DownloadWorker).setDownloading(galleryID, false)
return@launch
@@ -276,10 +251,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
else
0F
}.toMutableList())
exception.put(galleryID, reader.galleryInfo.files.map { null }.toMutableList())
results.put(galleryID, reader.galleryInfo.files.indices.map { index ->
cache?.firstOrNull { it?.nameWithoutExtension?.toIntOrNull() == index }?.readBytes()
}.toMutableList())
if (notification[galleryID] == null)
initNotification(galleryID)
@@ -301,47 +272,32 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
for (i in reader.galleryInfo.files.indices) {
val callback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (e.message?.contains("cancel", true) != false)
return
Log.i("PUPILD", "FAIL ${call.request().tag()} (${e.message})")
if (e.message?.contains("cancel", true) != true)
FirebaseCrashlytics.getInstance().recordException(e)
progress[galleryID]?.set(i, Float.NaN)
exception[galleryID]?.set(i, e)
notify(galleryID)
CoroutineScope(Dispatchers.IO).launch {
if (isCompleted(galleryID)) {
with(Cache(this@DownloadWorker)) {
if (isDownloading(galleryID)) {
moveToDownload(galleryID)
setDownloading(galleryID, false)
}
}
}
FirebaseCrashlytics.getInstance().apply {
log("FAIL ${call.request().tag()} (${e.message})")
setCustomKey("POS", "FAIL")
recordException(e)
}
cancel(galleryID)
queue.add(galleryID)
}
override fun onResponse(call: Call, response: Response) {
Log.i("PUPILD", "OK ${call.request().tag()}")
val ext = call.request().url().encodedPath().split('.').last()
try {
response.body().use {
it!!
results[galleryID]?.set(i, it.source().readByteArray())
Cache(this@DownloadWorker).putImage(galleryID, i, ext, it!!.byteStream())
}
progress[galleryID]?.set(i, Float.POSITIVE_INFINITY)
notify(galleryID)
CoroutineScope(Dispatchers.IO).launch {
results[galleryID]?.get(i)?.also {
Cache(this@DownloadWorker).putImage(galleryID, i, ext, it)
}
if (isCompleted(galleryID)) {
with(Cache(this@DownloadWorker)) {
if (isDownloading(galleryID)) {
@@ -351,47 +307,29 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
}
}
}
Log.i("PUPILD", "SUCCESS ${call.request().tag()}")
} catch (e: Exception) {
progress[galleryID]?.set(i, Float.NaN)
exception[galleryID]?.set(i, e)
notify(galleryID)
CoroutineScope(Dispatchers.IO).launch {
if (isCompleted(galleryID)) {
with(Cache(this@DownloadWorker)) {
if (isDownloading(galleryID)) {
moveToDownload(galleryID)
setDownloading(galleryID, false)
}
}
}
FirebaseCrashlytics.getInstance().apply {
log("FAIL ON OK ${call.request().tag()} (${e.message})")
setCustomKey("POS", "FAIL ON OK")
recordException(e)
}
File(Cache(this@DownloadWorker).getCachedGallery(galleryID), "%05d.$ext".format(i)).delete()
Log.i("PUPILD", "FAIL ON OK ${call.request().tag()} (${e.message})")
cancel(galleryID)
queue.add(galleryID)
}
}
}
if (progress[galleryID]?.get(i)?.isFinite() == true) {
if (progress[galleryID]?.get(i)?.isFinite() == true)
queueDownload(galleryID, reader, i, callback)
Log.i("PUPILD", "$galleryID QUEUED $i")
} else {
Log.i("PUPILD", "$galleryID SKIPPED $i (${progress[galleryID]?.get(i)})")
}
}
}
private fun notify(galleryID: Int) {
val max = progress[galleryID]?.size ?: 0
val progress = progress[galleryID]?.count { !it.isFinite() } ?: 0
Log.i("PUPILD", "NOTIFY $galleryID ${isCompleted(galleryID)} $progress/$max")
val progress = progress[galleryID]?.count { it.isInfinite() } ?: 0
if (isCompleted(galleryID)) {
notification[galleryID]
@@ -447,8 +385,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
if (Cache(this@DownloadWorker).isDownloading(galleryID))
notificationManager.notify(galleryID, notification[galleryID].build())
Log.i("PUPILD", "QUEUED $galleryID")
worker.put(galleryID, download(galleryID))
queue.poll()
}

View File

@@ -41,7 +41,7 @@ class Histories(private val file: File) : ArrayList<Int>() {
fun load() : Histories {
return apply {
super.clear()
addAll(
super.addAll(
json.parse(
serializer,
file.bufferedReader().use { it.readText() }
@@ -67,6 +67,20 @@ class Histories(private val file: File) : ArrayList<Int>() {
return true
}
override fun addAll(elements: Collection<Int>): Boolean {
load()
for (e in elements) {
if (contains(e))
super.remove(e)
super.add(0, e)
}
save()
return true
}
override fun remove(element: Int): Boolean {
load()
val retval = super.remove(element)

View File

@@ -319,7 +319,7 @@ fun importOldGalleries(context: Context, folder: File) = CoroutineScope(Dispatch
@Suppress("NAME_SHADOWING")
val index = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
Cache(context).putImage(galleryID, index, it.extension, it.readBytes())
Cache(context).putImage(galleryID, index, it.extension, it.inputStream())
}
}

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z" />
</vector>

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M15,14C12.33,14 7,15.33 7,18V20H23V18C23,15.33 17.67,14 15,14M15,12A4,4 0 0,0 19,8A4,4 0 0,0 15,4A4,4 0 0,0 11,8A4,4 0 0,0 15,12M5,13.28L7.45,14.77L6.8,11.96L9,10.08L6.11,9.83L5,7.19L3.87,9.83L1,10.08L3.18,11.96L2.5,14.77L5,13.28Z" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M15,14C12.33,14 7,15.33 7,18V20H23V18C23,15.33 17.67,14 15,14M15,12A4,4 0 0,0 19,8A4,4 0 0,0 15,4A4,4 0 0,0 11,8A4,4 0 0,0 15,12M5,13.28L7.45,14.77L6.8,11.96L9,10.08L6.11,9.83L5,7.19L3.87,9.83L1,10.08L3.18,11.96L2.5,14.77L5,13.28Z" />
</vector>

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M13,12H20V13.5H13M13,9.5H20V11H13M13,14.5H20V16H13M21,4H3A2,2 0 0,0 1,6V19A2,2 0 0,0 3,21H21A2,2 0 0,0 23,19V6A2,2 0 0,0 21,4M21,19H12V6H21" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M13,12H20V13.5H13M13,9.5H20V11H13M13,14.5H20V16H13M21,4H3A2,2 0 0,0 1,6V19A2,2 0 0,0 3,21H21A2,2 0 0,0 23,19V6A2,2 0 0,0 21,4M21,19H12V6H21" />
</vector>

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M20.71,4.63L19.37,3.29C19,2.9 18.35,2.9 17.96,3.29L9,12.25L11.75,15L20.71,6.04C21.1,5.65 21.1,5 20.71,4.63M7,14A3,3 0 0,0 4,17C4,18.31 2.84,19 2,19C2.92,20.22 4.5,21 6,21A4,4 0 0,0 10,17A3,3 0 0,0 7,14Z" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M20.71,4.63L19.37,3.29C19,2.9 18.35,2.9 17.96,3.29L9,12.25L11.75,15L20.71,6.04C21.1,5.65 21.1,5 20.71,4.63M7,14A3,3 0 0,0 4,17C4,18.31 2.84,19 2,19C2.92,20.22 4.5,21 6,21A4,4 0 0,0 10,17A3,3 0 0,0 7,14Z" />
</vector>

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M12,4A6,6 0 0,1 18,10C18,12.97 15.84,15.44 13,15.92V18H15V20H13V22H11V20H9V18H11V15.92C8.16,15.44 6,12.97 6,10A6,6 0 0,1 12,4M12,6A4,4 0 0,0 8,10A4,4 0 0,0 12,14A4,4 0 0,0 16,10A4,4 0 0,0 12,6Z" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12,4A6,6 0 0,1 18,10C18,12.97 15.84,15.44 13,15.92V18H15V20H13V22H11V20H9V18H11V15.92C8.16,15.44 6,12.97 6,10A6,6 0 0,1 12,4M12,6A4,4 0 0,0 8,10A4,4 0 0,0 12,14A4,4 0 0,0 16,10A4,4 0 0,0 12,6Z" />
</vector>

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M9,9C10.29,9 11.5,9.41 12.47,10.11L17.58,5H13V3H21V11H19V6.41L13.89,11.5C14.59,12.5 15,13.7 15,15A6,6 0 0,1 9,21A6,6 0 0,1 3,15A6,6 0 0,1 9,9M9,11A4,4 0 0,0 5,15A4,4 0 0,0 9,19A4,4 0 0,0 13,15A4,4 0 0,0 9,11Z" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M9,9C10.29,9 11.5,9.41 12.47,10.11L17.58,5H13V3H21V11H19V6.41L13.89,11.5C14.59,12.5 15,13.7 15,15A6,6 0 0,1 9,21A6,6 0 0,1 3,15A6,6 0 0,1 9,9M9,11A4,4 0 0,0 5,15A4,4 0 0,0 9,19A4,4 0 0,0 13,15A4,4 0 0,0 9,11Z" />
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M5.5,7A1.5,1.5 0 0,1 4,5.5A1.5,1.5 0 0,1 5.5,4A1.5,1.5 0 0,1 7,5.5A1.5,1.5 0 0,1 5.5,7M21.41,11.58L12.41,2.58C12.05,2.22 11.55,2 11,2H4C2.89,2 2,2.89 2,4V11C2,11.55 2.22,12.05 2.59,12.41L11.58,21.41C11.95,21.77 12.45,22 13,22C13.55,22 14.05,21.77 14.41,21.41L21.41,14.41C21.78,14.05 22,13.55 22,13C22,12.44 21.77,11.94 21.41,11.58Z" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M5.5,7A1.5,1.5 0 0,1 4,5.5A1.5,1.5 0 0,1 5.5,4A1.5,1.5 0 0,1 7,5.5A1.5,1.5 0 0,1 5.5,7M21.41,11.58L12.41,2.58C12.05,2.22 11.55,2 11,2H4C2.89,2 2,2.89 2,4V11C2,11.55 2.22,12.05 2.59,12.41L11.58,21.41C11.95,21.77 12.45,22 13,22C13.55,22 14.05,21.77 14.41,21.41L21.41,14.41C21.78,14.05 22,13.55 22,13C22,12.44 21.77,11.94 21.41,11.58Z" />
</vector>

View File

@@ -4,5 +4,5 @@
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M12.87,15.07L10.33,12.56L10.36,12.53C12.1,10.59 13.34,8.36 14.07,6H17V4H10V2H8V4H1V6H12.17C11.5,7.92 10.44,9.75 9,11.35C8.07,10.32 7.3,9.19 6.69,8H4.69C5.42,9.63 6.42,11.17 7.67,12.56L2.58,17.58L4,19L9,14L12.11,17.11L12.87,15.07M18.5,10H16.5L12,22H14L15.12,19H19.87L21,22H23L18.5,10M15.88,17L17.5,12.67L19.12,17H15.88Z" />
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12.87,15.07L10.33,12.56L10.36,12.53C12.1,10.59 13.34,8.36 14.07,6H17V4H10V2H8V4H1V6H12.17C11.5,7.92 10.44,9.75 9,11.35C8.07,10.32 7.3,9.19 6.69,8H4.69C5.42,9.63 6.42,11.17 7.67,12.56L2.58,17.58L4,19L9,14L12.11,17.11L12.87,15.07M18.5,10H16.5L12,22H14L15.12,19H19.87L21,22H23L18.5,10M15.88,17L17.5,12.67L19.12,17H15.88Z" />
</vector>

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Pupil, Hitomi.la viewer for Android
~ Copyright (C) 2020 tom5079
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout
android:id="@+id/main_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/main_appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:visibility="invisible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<View
android:layout_width="match_parent"
android:layout_height="64dp"
android:visibility="invisible"
android:background="@color/transparent"
app:layout_scrollFlags="scroll|enterAlways"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.ContentLoadingProgressBar
style="?android:attr/progressBarStyle"
android:id="@+id/main_progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"/>
<TextView
android:id="@+id/main_noresult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/main_no_result"
android:visibility="invisible"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="64dp"
android:clipToPadding="false"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
<com.github.clans.fab.FloatingActionMenu
android:id="@+id/main_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:menu_colorNormal="@color/colorAccent">
<com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_label="@string/main_fab_cancel"
app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_jump"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_label="@string/main_jump_title"
app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_random"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_label="@string/main_fab_random"
app:fab_size="mini"/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:fab_label="@string/main_open_gallery_by_id"
app:fab_size="mini"/>
</com.github.clans.fab.FloatingActionMenu>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.arlib.floatingsearchview.FloatingSearchViewDayNight
android:id="@+id/main_searchview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:floatingSearch_backgroundColor="?android:attr/colorBackgroundFloating"
app:floatingSearch_leftActionColor="?attr/colorControlNormal"
app:floatingSearch_menuItemIconColor="?attr/colorControlNormal"
app:floatingSearch_actionMenuOverflowColor="?attr/colorControlNormal"
app:floatingSearch_clearBtnColor="?attr/colorControlNormal"
app:floatingSearch_viewTextColor="?android:attr/textColorPrimary"
app:floatingSearch_suggestionRightIconColor="@color/material_orange_500"
app:floatingSearch_searchBarMarginLeft="8dp"
app:floatingSearch_searchBarMarginRight="8dp"
app:floatingSearch_searchBarMarginTop="8dp"
app:floatingSearch_searchHint="@string/search_hint"
app:floatingSearch_suggestionsListAnimDuration="250"
app:floatingSearch_showSearchKey="true"
app:floatingSearch_leftActionMode="showHamburger"
app:floatingSearch_menu="@menu/main"
app:floatingSearch_dismissOnOutsideTouch="true"
app:floatingSearch_close_search_on_keyboard_dismiss="true"
tools:ignore="NewApi" />
</RelativeLayout>

View File

@@ -122,12 +122,6 @@
android:id="@+id/main_searchview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:floatingSearch_backgroundColor="?android:attr/colorBackgroundFloating"
app:floatingSearch_leftActionColor="?attr/colorControlNormal"
app:floatingSearch_menuItemIconColor="?attr/colorControlNormal"
app:floatingSearch_actionMenuOverflowColor="?attr/colorControlNormal"
app:floatingSearch_clearBtnColor="?attr/colorControlNormal"
app:floatingSearch_viewTextColor="?android:attr/textColorPrimary"
app:floatingSearch_suggestionRightIconColor="@color/material_orange_500"
app:floatingSearch_searchBarMarginLeft="8dp"
app:floatingSearch_searchBarMarginRight="8dp"

View File

@@ -19,11 +19,14 @@
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:layout_height="wrap_content"
android:src="@drawable/side_nav_bar"
android:adjustViewBounds="true"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:gravity="bottom"/>
android:gravity="bottom"
tools:ignore="ContentDescription" />

View File

@@ -146,8 +146,6 @@
<string name="default_query_dialog_filter_loli">登場人物を全て18歳以上にする</string>
<string name="settings_cache_disable">キャッシュを使用しない</string>
<string name="settings_download_when_cache_disable_warning">キャッシュを使用しないため、ダウンロードできません</string>
<string name="apcjsa_option_title">(Korean only)</string>
<string name="apcjsa_option_message">(Korean only)</string>
<string name="settings_user_id">ユーザーID</string>
<string name="settings_user_id_toast">ユーザーIDをクリップボードにコピーしました</string>
<string name="reader_error_retry">ダウンロードエラーが発生しました。リトライしますか?</string>

View File

@@ -146,10 +146,8 @@
<string name="default_query_dialog_filter_loli">판사님 저는 페도가 아닙니다</string>
<string name="settings_cache_disable">캐시 비활성화</string>
<string name="settings_download_when_cache_disable_warning">캐시를 활성화 해야 다운로드를 진행할 수 있습니다</string>
<string name="apcjsa_option_title">아청법 대응 옵션 추가</string>
<string name="apcjsa_option_message">경찰서 정모 확률을 줄여보고자 캐시 비활성화/태그 필터를 추가하였습니다. 적용하시겠습니까?</string>
<string name="settings_user_id">유저 ID</string>
<string name="settings_user_id_toast">유저 ID를 클립보드에 복사했습니다</string>
<string name="reader_error_retry">다운로드 에러가 발생했습니. 재시도 하시겠습니까?</string>
<string name="reader_error_retry">다운로드 에러가 발생했습니. 재시도 하시겠습니까?</string>
<string name="reader_fab_retry">재시도</string>
</resources>

View File

@@ -27,9 +27,6 @@
<string name="https_block_alert_title">(Korean only)</string>
<string name="https_block_alert">(Korean only)</string>
<string name="apcjsa_option_title">(Korean only)</string>
<string name="apcjsa_option_message">(Korean only)</string>
<string name="update_failed">Update failed</string>
<string name="update_failed_message">Please install manually by visiting github release page :{ (or try again!)</string>
<string name="update_no_permission">Cannot auto update because permission is denied. Please download manually from the webpage.</string>
@@ -124,7 +121,7 @@
<string name="reader_notification_complete">Download complete</string>
<string name="reader_notification_error">Download error</string>
<string name="reader_error_retry">Download Error occurred. Retry?</string>
<string name="reader_error_retry">Download Error. Retry?</string>
<string name="reader_help">Help</string>

View File

@@ -19,6 +19,5 @@ package xyz.quaver
enum class Code {
HITOMI,
HIYOBI,
SORALA
HIYOBI
}