@@ -19,8 +19,8 @@ android {
|
||||
applicationId "xyz.quaver.pupil"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionCode 53
|
||||
versionName "4.18"
|
||||
versionCode 54
|
||||
versionName "4.18.1"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
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 {
|
||||
*** rewind();
|
||||
}
|
||||
}
|
||||
|
||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl
|
||||
@@ -11,8 +11,8 @@
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"properties": [],
|
||||
"versionCode": 53,
|
||||
"versionName": "53",
|
||||
"versionCode": 54,
|
||||
"versionName": "54",
|
||||
"enabled": true,
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
|
||||
@@ -22,9 +22,12 @@ 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.bumptech.glide.load.model.GlideUrl
|
||||
import com.bumptech.glide.load.model.LazyHeaders
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import kotlinx.android.synthetic.main.activity_reader.view.*
|
||||
@@ -32,8 +35,15 @@ 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
|
||||
@@ -88,58 +98,89 @@ 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.progress =
|
||||
if (progress?.isInfinite() == true)
|
||||
100
|
||||
else
|
||||
progress?.roundToInt() ?: 0
|
||||
holder.view.reader_item_progressbar.visibility = View.VISIBLE
|
||||
|
||||
holder.view.image.setImageDrawable(null)
|
||||
}
|
||||
glide.clear(holder.view.image)
|
||||
|
||||
timer.schedule(1000) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
notifyItemChanged(position)
|
||||
if (progress?.isNaN() == true) {
|
||||
FirebaseCrashlytics.getInstance().recordException(
|
||||
DownloadWorker.getInstance(holder.view.context).exception[galleryID]?.get(position)!!
|
||||
)
|
||||
|
||||
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.progress =
|
||||
if (progress?.isInfinite() == true)
|
||||
100
|
||||
else
|
||||
progress?.roundToInt() ?: 0
|
||||
|
||||
holder.view.image.setImageDrawable(null)
|
||||
}
|
||||
|
||||
timer.schedule(1000) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,30 +144,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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -34,8 +34,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
|
||||
@@ -250,7 +252,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 +262,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()
|
||||
|
||||
@@ -143,7 +143,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
* null -> Download in progress / Loading
|
||||
*/
|
||||
val exception = SparseArray<MutableList<Throwable?>?>()
|
||||
val results = SparseArray<MutableList<ByteArray?>?>()
|
||||
val notification = SparseArray<NotificationCompat.Builder>()
|
||||
|
||||
private val loop = loop()
|
||||
@@ -196,7 +195,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
|
||||
progress.clear()
|
||||
exception.clear()
|
||||
results.clear()
|
||||
notification.clear()
|
||||
notificationManager.cancelAll()
|
||||
}
|
||||
@@ -213,7 +211,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
|
||||
progress.remove(galleryID)
|
||||
exception.remove(galleryID)
|
||||
results.remove(galleryID)
|
||||
notification.remove(galleryID)
|
||||
notificationManager.cancel(galleryID)
|
||||
|
||||
@@ -262,7 +259,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
if (reader == null) {
|
||||
progress.put(galleryID, null)
|
||||
exception.put(galleryID, null)
|
||||
results.put(galleryID, null)
|
||||
|
||||
Cache(this@DownloadWorker).setDownloading(galleryID, false)
|
||||
return@launch
|
||||
@@ -277,9 +273,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
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)
|
||||
@@ -329,19 +322,13 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -146,8 +146,6 @@
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -19,6 +19,5 @@ package xyz.quaver
|
||||
|
||||
enum class Code {
|
||||
HITOMI,
|
||||
HIYOBI,
|
||||
SORALA
|
||||
HIYOBI
|
||||
}
|
||||
Reference in New Issue
Block a user