Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ddb19530b | ||
|
|
431e56a9f1 | ||
|
|
71093aac4c | ||
|
|
47c9e8127e | ||
|
|
24b801b346 | ||
|
|
70608c3ed9 | ||
|
|
f185196e85 | ||
|
|
a8766a8bbe | ||
|
|
27a8c93cfe | ||
|
|
a3cd29fda9 | ||
|
|
adda8ab640 | ||
|
|
1538ea5fc8 | ||
|
|
2367a97a54 | ||
|
|
090ec0e4af | ||
|
|
de7f552e5c | ||
|
|
d763f5dca0 | ||
|
|
9f41116241 | ||
|
|
57faada201 | ||
|
|
1edb95f0c5 | ||
|
|
9f363d8900 |
@@ -4,7 +4,6 @@
|
|||||||
*Pupil, Hitomi.la viewer for Android*
|
*Pupil, Hitomi.la viewer for Android*
|
||||||
|
|
||||||
[](https://discord.gg/Stj4b5v)
|
[](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
|
# Screenshot
|
||||||

|

|
||||||
|
|||||||
@@ -19,8 +19,8 @@ android {
|
|||||||
applicationId "xyz.quaver.pupil"
|
applicationId "xyz.quaver.pupil"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 53
|
versionCode 56
|
||||||
versionName "4.18"
|
versionName "4.18.3"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -65,9 +65,9 @@ dependencies {
|
|||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
||||||
implementation 'com.google.firebase:firebase-core:17.4.3'
|
implementation 'com.google.firebase:firebase-core:17.4.4'
|
||||||
implementation 'com.google.firebase:firebase-analytics:17.4.3'
|
implementation 'com.google.firebase:firebase-analytics:17.4.4'
|
||||||
implementation 'com.google.firebase:firebase-crashlytics:17.1.0'
|
implementation 'com.google.firebase:firebase-crashlytics:17.1.1'
|
||||||
implementation 'com.google.firebase:firebase-perf:19.0.7'
|
implementation 'com.google.firebase:firebase-perf:19.0.7'
|
||||||
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
||||||
implementation 'com.github.clans:fab:1.6.4'
|
implementation 'com.github.clans:fab:1.6.4'
|
||||||
|
|||||||
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -32,4 +32,7 @@
|
|||||||
}
|
}
|
||||||
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
||||||
*** rewind();
|
*** rewind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||||
|
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
"type": "SINGLE",
|
"type": "SINGLE",
|
||||||
"filters": [],
|
"filters": [],
|
||||||
"properties": [],
|
"properties": [],
|
||||||
"versionCode": 53,
|
"versionCode": 56,
|
||||||
"versionName": "53",
|
"versionName": "56",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"outputFile": "app-release.apk"
|
"outputFile": "app-release.apk"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class ExampleInstrumentedTest {
|
|||||||
while(worker.progress.indexOfKey(galleryID) < 0 || worker.progress[galleryID] != null) {
|
while(worker.progress.indexOfKey(galleryID) < 0 || worker.progress[galleryID] != null) {
|
||||||
Log.i("PUPILD", worker.progress[galleryID]?.joinToString(" ") ?: "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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,13 +71,6 @@ class Pupil : MultiDexApplication() {
|
|||||||
preference.edit().remove("dl_location").apply()
|
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"))
|
histories = Histories(File(ContextCompat.getDataDir(this), "histories.json"))
|
||||||
favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json"))
|
favorites = Histories(File(ContextCompat.getDataDir(this), "favorites.json"))
|
||||||
|
|
||||||
|
|||||||
@@ -18,29 +18,38 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.adapters
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.RequestManager
|
import com.bumptech.glide.RequestManager
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.bumptech.glide.load.model.GlideUrl
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.bumptech.glide.load.model.LazyHeaders
|
||||||
import kotlinx.android.synthetic.main.activity_reader.view.*
|
|
||||||
import kotlinx.android.synthetic.main.item_reader.view.*
|
import kotlinx.android.synthetic.main.item_reader.view.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import xyz.quaver.Code
|
||||||
import xyz.quaver.hitomi.Reader
|
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.R
|
||||||
|
import xyz.quaver.pupil.util.download.Cache
|
||||||
import xyz.quaver.pupil.util.download.DownloadWorker
|
import xyz.quaver.pupil.util.download.DownloadWorker
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class ReaderAdapter(private val glide: RequestManager,
|
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
|
var reader: Reader? = null
|
||||||
val timer = Timer()
|
val timer = Timer()
|
||||||
@@ -88,46 +97,57 @@ class ReaderAdapter(private val glide: RequestManager,
|
|||||||
|
|
||||||
holder.view.reader_index.text = (position+1).toString()
|
holder.view.reader_index.text = (position+1).toString()
|
||||||
|
|
||||||
val image = downloadWorker!!.results[galleryID]?.get(position)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(holder.view.context)
|
||||||
val progress = downloadWorker!!.progress[galleryID]?.get(position)
|
if (preferences.getBoolean("cache_disable", false)) {
|
||||||
|
val lowQuality = preferences.getBoolean("low_quality", false)
|
||||||
if (progress?.isInfinite() == true && image != null) {
|
|
||||||
holder.view.reader_item_progressbar.visibility = View.INVISIBLE
|
|
||||||
|
|
||||||
|
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 {
|
holder.view.image.post {
|
||||||
glide
|
glide
|
||||||
.load(image)
|
.load(url!!)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.skipMemoryCache(true)
|
.skipMemoryCache(false)
|
||||||
.fitCenter()
|
.fitCenter()
|
||||||
.error(R.drawable.image_broken_variant)
|
.error(R.drawable.image_broken_variant)
|
||||||
.into(holder.view.image)
|
.into(holder.view.image)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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) {
|
holder.view.image.post {
|
||||||
FirebaseCrashlytics.getInstance().recordException(
|
glide
|
||||||
DownloadWorker.getInstance(holder.view.context).exception[galleryID]?.get(position)!!
|
.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 {
|
} else {
|
||||||
|
holder.view.reader_item_progressbar.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
glide.clear(holder.view.image)
|
||||||
|
|
||||||
holder.view.reader_item_progressbar.progress =
|
holder.view.reader_item_progressbar.progress =
|
||||||
if (progress?.isInfinite() == true)
|
if (progress?.isInfinite() == true)
|
||||||
100
|
100
|
||||||
@@ -135,11 +155,11 @@ class ReaderAdapter(private val glide: RequestManager,
|
|||||||
progress?.roundToInt() ?: 0
|
progress?.roundToInt() ?: 0
|
||||||
|
|
||||||
holder.view.image.setImageDrawable(null)
|
holder.view.image.setImageDrawable(null)
|
||||||
}
|
|
||||||
|
|
||||||
timer.schedule(1000) {
|
timer.schedule(1000) {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.net.Uri
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.*
|
import android.text.*
|
||||||
import android.text.style.AlignmentSpan
|
import android.text.style.AlignmentSpan
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -144,30 +145,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
preference.edit().putBoolean("https_block_alert", true).apply()
|
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) {
|
with(application as Pupil) {
|
||||||
@@ -464,8 +441,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("cache_disable", false))
|
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("cache_disable", false))
|
||||||
Toast.makeText(context, R.string.settings_download_when_cache_disable_warning, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.settings_download_when_cache_disable_warning, Toast.LENGTH_SHORT).show()
|
||||||
else {
|
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)
|
worker.cancel(galleryID)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
Cache(context).setDownloading(galleryID, true)
|
Cache(context).setDownloading(galleryID, true)
|
||||||
|
|
||||||
@@ -867,6 +846,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}"
|
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}"
|
||||||
|
|
||||||
|
val color = TypedValue()
|
||||||
|
theme.resolveAttribute(R.attr.colorControlNormal, color, true)
|
||||||
|
|
||||||
leftIcon.setImageDrawable(
|
leftIcon.setImageDrawable(
|
||||||
ResourcesCompat.getDrawable(
|
ResourcesCompat.getDrawable(
|
||||||
resources,
|
resources,
|
||||||
@@ -880,7 +862,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
"artist" -> R.drawable.brush
|
"artist" -> R.drawable.brush
|
||||||
else -> R.drawable.tag
|
else -> R.drawable.tag
|
||||||
},
|
},
|
||||||
null)
|
context.theme)
|
||||||
)
|
)
|
||||||
|
|
||||||
with(suggestionView.findViewById<ImageView>(R.id.right_icon)) {
|
with(suggestionView.findViewById<ImageView>(R.id.right_icon)) {
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|||||||
import kotlinx.android.synthetic.main.activity_reader.*
|
import kotlinx.android.synthetic.main.activity_reader.*
|
||||||
import kotlinx.android.synthetic.main.activity_reader.view.*
|
import kotlinx.android.synthetic.main.activity_reader.view.*
|
||||||
import kotlinx.android.synthetic.main.dialog_numberpicker.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.Code
|
||||||
import xyz.quaver.pupil.Pupil
|
import xyz.quaver.pupil.Pupil
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
@@ -99,7 +102,36 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initView()
|
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) {
|
override fun onNewIntent(intent: Intent) {
|
||||||
@@ -232,6 +264,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun initDownloader() {
|
private fun initDownloader() {
|
||||||
val worker = DownloadWorker.getInstance(this).apply {
|
val worker = DownloadWorker.getInstance(this).apply {
|
||||||
|
cancel(galleryID)
|
||||||
queue.add(galleryID)
|
queue.add(galleryID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +281,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
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
|
reader_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
||||||
|
|
||||||
if (title == getString(R.string.reader_loading)) {
|
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
|
reader_download_progressbar.visibility = View.GONE
|
||||||
|
|
||||||
animateDownloadFAB(false)
|
animateDownloadFAB(false)
|
||||||
@@ -284,7 +317,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
with(reader_recyclerview) {
|
with(reader_recyclerview) {
|
||||||
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID).apply {
|
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID, this@ReaderActivity).apply {
|
||||||
onItemClickListener = {
|
onItemClickListener = {
|
||||||
if (isScroll) {
|
if (isScroll) {
|
||||||
isScroll = false
|
isScroll = false
|
||||||
@@ -396,7 +429,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
icon?.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
|
icon?.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
|
||||||
override fun onAnimationEnd(drawable: Drawable?) {
|
override fun onAnimationEnd(drawable: Drawable?) {
|
||||||
val worker = DownloadWorker.getInstance(context)
|
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 {
|
post {
|
||||||
setImageResource(R.drawable.ic_download)
|
setImageResource(R.drawable.ic_download)
|
||||||
labelText = getString(R.string.reader_fab_download_cancel)
|
labelText = getString(R.string.reader_fab_download_cancel)
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package xyz.quaver.pupil.util.download
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
|
||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
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.getDownloadDirectory
|
||||||
import xyz.quaver.pupil.util.isParentOf
|
import xyz.quaver.pupil.util.isParentOf
|
||||||
import xyz.quaver.pupil.util.json
|
import xyz.quaver.pupil.util.json
|
||||||
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.locks.Lock
|
import java.util.concurrent.locks.Lock
|
||||||
@@ -67,7 +68,7 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
// Search in this order
|
// Search in this order
|
||||||
// Download -> Cache
|
// Download -> Cache
|
||||||
fun getCachedGallery(galleryID: Int) = getCachedGallery(this, galleryID).also {
|
fun getCachedGallery(galleryID: Int) = getCachedGallery(this, galleryID).also {
|
||||||
if (!it.exists() && !preference.getBoolean("cache_disable", false))
|
if (!it.exists())
|
||||||
it.mkdirs()
|
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))
|
if (preference.getBoolean("cache_disable", false))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -260,8 +261,10 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileOutputStream(cache).use {
|
BufferedInputStream(data).use { inputStream ->
|
||||||
it.write(data)
|
FileOutputStream(cache).use { outputStream ->
|
||||||
|
inputStream.copyTo(outputStream)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
cache.delete()
|
cache.delete()
|
||||||
@@ -285,17 +288,17 @@ class Cache(context: Context) : ContextWrapper(context) {
|
|||||||
if (download.isParentOf(cache))
|
if (download.isParentOf(cache))
|
||||||
return@launch
|
return@launch
|
||||||
|
|
||||||
Log.i("PUPILD", "MOVING ${cache.canonicalPath} --> ${download.canonicalPath}")
|
FirebaseCrashlytics.getInstance().log("MOVING ${cache.canonicalPath} --> ${download.canonicalPath}")
|
||||||
|
|
||||||
cache.copyRecursively(download, true) { file, err ->
|
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
|
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()
|
cache.deleteRecursively()
|
||||||
Log.i("PUPILD", "DELETED ${cache.canonicalPath}")
|
FirebaseCrashlytics.getInstance().log("DELETED ${cache.canonicalPath}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,22 +128,8 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
* SECONDARY VALUE
|
* SECONDARY VALUE
|
||||||
* 0 <= value < 100 -> Download in progress
|
* 0 <= value < 100 -> Download in progress
|
||||||
* Float.POSITIVE_INFINITY -> Download completed
|
* Float.POSITIVE_INFINITY -> Download completed
|
||||||
* Float.NaN -> Exception
|
|
||||||
*/
|
*/
|
||||||
val progress = SparseArray<MutableList<Float>?>()
|
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>()
|
val notification = SparseArray<NotificationCompat.Builder>()
|
||||||
|
|
||||||
private val loop = loop()
|
private val loop = loop()
|
||||||
@@ -195,8 +181,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
progress.clear()
|
progress.clear()
|
||||||
exception.clear()
|
|
||||||
results.clear()
|
|
||||||
notification.clear()
|
notification.clear()
|
||||||
notificationManager.cancelAll()
|
notificationManager.cancelAll()
|
||||||
}
|
}
|
||||||
@@ -212,16 +196,11 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
progress.remove(galleryID)
|
progress.remove(galleryID)
|
||||||
exception.remove(galleryID)
|
|
||||||
results.remove(galleryID)
|
|
||||||
notification.remove(galleryID)
|
notification.remove(galleryID)
|
||||||
notificationManager.cancel(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) {
|
private fun queueDownload(galleryID: Int, reader: Reader, index: Int, callback: Callback) {
|
||||||
val lowQuality = preferences.getBoolean("low_quality", false)
|
val lowQuality = preferences.getBoolean("low_quality", false)
|
||||||
@@ -251,8 +230,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
client.newCall(request).enqueue(callback)
|
client.newCall(request).enqueue(callback)
|
||||||
|
|
||||||
Log.i("PUPILD", "DOWNLOADING ($galleryID, $index) from ${request.url()}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch {
|
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
|
//gallery doesn't exist
|
||||||
if (reader == null) {
|
if (reader == null) {
|
||||||
progress.put(galleryID, null)
|
progress.put(galleryID, null)
|
||||||
exception.put(galleryID, null)
|
|
||||||
results.put(galleryID, null)
|
|
||||||
|
|
||||||
Cache(this@DownloadWorker).setDownloading(galleryID, false)
|
Cache(this@DownloadWorker).setDownloading(galleryID, false)
|
||||||
return@launch
|
return@launch
|
||||||
@@ -276,10 +251,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
else
|
else
|
||||||
0F
|
0F
|
||||||
}.toMutableList())
|
}.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)
|
if (notification[galleryID] == null)
|
||||||
initNotification(galleryID)
|
initNotification(galleryID)
|
||||||
@@ -301,47 +272,32 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
for (i in reader.galleryInfo.files.indices) {
|
for (i in reader.galleryInfo.files.indices) {
|
||||||
val callback = object : Callback {
|
val callback = object : Callback {
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
if (e.message?.contains("cancel", true) != false)
|
||||||
|
return
|
||||||
|
|
||||||
Log.i("PUPILD", "FAIL ${call.request().tag()} (${e.message})")
|
Log.i("PUPILD", "FAIL ${call.request().tag()} (${e.message})")
|
||||||
if (e.message?.contains("cancel", true) != true)
|
FirebaseCrashlytics.getInstance().apply {
|
||||||
FirebaseCrashlytics.getInstance().recordException(e)
|
log("FAIL ${call.request().tag()} (${e.message})")
|
||||||
|
setCustomKey("POS", "FAIL")
|
||||||
progress[galleryID]?.set(i, Float.NaN)
|
recordException(e)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancel(galleryID)
|
||||||
|
queue.add(galleryID)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
override fun onResponse(call: Call, response: Response) {
|
||||||
Log.i("PUPILD", "OK ${call.request().tag()}")
|
|
||||||
|
|
||||||
val ext = call.request().url().encodedPath().split('.').last()
|
val ext = call.request().url().encodedPath().split('.').last()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response.body().use {
|
response.body().use {
|
||||||
it!!
|
Cache(this@DownloadWorker).putImage(galleryID, i, ext, it!!.byteStream())
|
||||||
|
|
||||||
results[galleryID]?.set(i, it.source().readByteArray())
|
|
||||||
}
|
}
|
||||||
progress[galleryID]?.set(i, Float.POSITIVE_INFINITY)
|
progress[galleryID]?.set(i, Float.POSITIVE_INFINITY)
|
||||||
|
|
||||||
notify(galleryID)
|
notify(galleryID)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
results[galleryID]?.get(i)?.also {
|
|
||||||
Cache(this@DownloadWorker).putImage(galleryID, i, ext, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCompleted(galleryID)) {
|
if (isCompleted(galleryID)) {
|
||||||
with(Cache(this@DownloadWorker)) {
|
with(Cache(this@DownloadWorker)) {
|
||||||
if (isDownloading(galleryID)) {
|
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) {
|
} catch (e: Exception) {
|
||||||
|
FirebaseCrashlytics.getInstance().apply {
|
||||||
progress[galleryID]?.set(i, Float.NaN)
|
log("FAIL ON OK ${call.request().tag()} (${e.message})")
|
||||||
exception[galleryID]?.set(i, e)
|
setCustomKey("POS", "FAIL ON OK")
|
||||||
|
recordException(e)
|
||||||
notify(galleryID)
|
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
if (isCompleted(galleryID)) {
|
|
||||||
with(Cache(this@DownloadWorker)) {
|
|
||||||
if (isDownloading(galleryID)) {
|
|
||||||
moveToDownload(galleryID)
|
|
||||||
setDownloading(galleryID, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File(Cache(this@DownloadWorker).getCachedGallery(galleryID), "%05d.$ext".format(i)).delete()
|
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)
|
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) {
|
private fun notify(galleryID: Int) {
|
||||||
val max = progress[galleryID]?.size ?: 0
|
val max = progress[galleryID]?.size ?: 0
|
||||||
val progress = progress[galleryID]?.count { !it.isFinite() } ?: 0
|
val progress = progress[galleryID]?.count { it.isInfinite() } ?: 0
|
||||||
|
|
||||||
Log.i("PUPILD", "NOTIFY $galleryID ${isCompleted(galleryID)} $progress/$max")
|
|
||||||
|
|
||||||
if (isCompleted(galleryID)) {
|
if (isCompleted(galleryID)) {
|
||||||
notification[galleryID]
|
notification[galleryID]
|
||||||
@@ -447,8 +385,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
|||||||
if (Cache(this@DownloadWorker).isDownloading(galleryID))
|
if (Cache(this@DownloadWorker).isDownloading(galleryID))
|
||||||
notificationManager.notify(galleryID, notification[galleryID].build())
|
notificationManager.notify(galleryID, notification[galleryID].build())
|
||||||
|
|
||||||
Log.i("PUPILD", "QUEUED $galleryID")
|
|
||||||
|
|
||||||
worker.put(galleryID, download(galleryID))
|
worker.put(galleryID, download(galleryID))
|
||||||
queue.poll()
|
queue.poll()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class Histories(private val file: File) : ArrayList<Int>() {
|
|||||||
fun load() : Histories {
|
fun load() : Histories {
|
||||||
return apply {
|
return apply {
|
||||||
super.clear()
|
super.clear()
|
||||||
addAll(
|
super.addAll(
|
||||||
json.parse(
|
json.parse(
|
||||||
serializer,
|
serializer,
|
||||||
file.bufferedReader().use { it.readText() }
|
file.bufferedReader().use { it.readText() }
|
||||||
@@ -67,6 +67,20 @@ class Histories(private val file: File) : ArrayList<Int>() {
|
|||||||
return true
|
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 {
|
override fun remove(element: Int): Boolean {
|
||||||
load()
|
load()
|
||||||
val retval = super.remove(element)
|
val retval = super.remove(element)
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ fun importOldGalleries(context: Context, folder: File) = CoroutineScope(Dispatch
|
|||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val index = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 67 KiB |
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
@@ -4,5 +4,5 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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>
|
</vector>
|
||||||
@@ -19,11 +19,14 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/nav_header_height"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/side_nav_bar"
|
android:src="@drawable/side_nav_bar"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
android:gravity="bottom"/>
|
android:gravity="bottom"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
@@ -146,8 +146,6 @@
|
|||||||
<string name="default_query_dialog_filter_loli">登場人物を全て18歳以上にする</string>
|
<string name="default_query_dialog_filter_loli">登場人物を全て18歳以上にする</string>
|
||||||
<string name="settings_cache_disable">キャッシュを使用しない</string>
|
<string name="settings_cache_disable">キャッシュを使用しない</string>
|
||||||
<string name="settings_download_when_cache_disable_warning">キャッシュを使用しないため、ダウンロードできません</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">ユーザーID</string>
|
||||||
<string name="settings_user_id_toast">ユーザーIDをクリップボードにコピーしました</string>
|
<string name="settings_user_id_toast">ユーザーIDをクリップボードにコピーしました</string>
|
||||||
<string name="reader_error_retry">ダウンロードエラーが発生しました。リトライしますか?</string>
|
<string name="reader_error_retry">ダウンロードエラーが発生しました。リトライしますか?</string>
|
||||||
|
|||||||
@@ -146,10 +146,8 @@
|
|||||||
<string name="default_query_dialog_filter_loli">판사님 저는 페도가 아닙니다</string>
|
<string name="default_query_dialog_filter_loli">판사님 저는 페도가 아닙니다</string>
|
||||||
<string name="settings_cache_disable">캐시 비활성화</string>
|
<string name="settings_cache_disable">캐시 비활성화</string>
|
||||||
<string name="settings_download_when_cache_disable_warning">캐시를 활성화 해야 다운로드를 진행할 수 있습니다</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">유저 ID</string>
|
||||||
<string name="settings_user_id_toast">유저 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>
|
<string name="reader_fab_retry">재시도</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -27,9 +27,6 @@
|
|||||||
<string name="https_block_alert_title">(Korean only)</string>
|
<string name="https_block_alert_title">(Korean only)</string>
|
||||||
<string name="https_block_alert">(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">Update failed</string>
|
||||||
<string name="update_failed_message">Please install manually by visiting github release page :{ (or try again!)</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>
|
<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_complete">Download complete</string>
|
||||||
<string name="reader_notification_error">Download error</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>
|
<string name="reader_help">Help</string>
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,5 @@ package xyz.quaver
|
|||||||
|
|
||||||
enum class Code {
|
enum class Code {
|
||||||
HITOMI,
|
HITOMI,
|
||||||
HIYOBI,
|
HIYOBI
|
||||||
SORALA
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user