Applying changed Download routines
This commit is contained in:
@@ -25,6 +25,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
@@ -37,6 +38,7 @@ import xyz.quaver.hiyobi.createImgList
|
||||
import xyz.quaver.hiyobi.getReader
|
||||
import xyz.quaver.hiyobi.user_agent
|
||||
import xyz.quaver.pupil.ui.LockActivity
|
||||
import xyz.quaver.pupil.util.download.Cache
|
||||
import xyz.quaver.pupil.util.download.DownloadWorker
|
||||
import xyz.quaver.pupil.util.getDownloadDirectory
|
||||
import xyz.quaver.pupil.util.updateOldReaderGalleries
|
||||
@@ -139,4 +141,17 @@ class ExampleInstrumentedTest {
|
||||
|
||||
Log.i("PUPILD", "DONE!!")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_getReaderOrNull() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
|
||||
val galleryID = 1561552
|
||||
|
||||
runBlocking {
|
||||
Log.i("PUPILD", Cache(context).getReader(galleryID)?.title ?: "null")
|
||||
}
|
||||
|
||||
Log.i("PUPILD", Cache(context).getReaderOrNull(galleryID)?.title ?: "null")
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@
|
||||
|
||||
package xyz.quaver.pupil.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Base64
|
||||
import android.util.SparseBooleanArray
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@@ -29,7 +31,7 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.bumptech.glide.RequestManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.daimajia.swipe.SwipeLayout
|
||||
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
|
||||
@@ -37,28 +39,22 @@ import com.daimajia.swipe.interfaces.SwipeAdapterInterface
|
||||
import com.google.android.material.chip.Chip
|
||||
import kotlinx.android.synthetic.main.item_galleryblock.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import xyz.quaver.hitomi.GalleryBlock
|
||||
import xyz.quaver.hitomi.Reader
|
||||
import xyz.quaver.pupil.BuildConfig
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.types.Tag
|
||||
import xyz.quaver.pupil.util.GalleryDownloader
|
||||
import xyz.quaver.pupil.util.Histories
|
||||
import xyz.quaver.pupil.util.getCachedGallery
|
||||
import xyz.quaver.pupil.util.download.Cache
|
||||
import xyz.quaver.pupil.util.download.DownloadWorker
|
||||
import xyz.quaver.pupil.util.wordCapitalize
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
class GalleryBlockAdapter(private val glide: RequestManager, private val galleries: List<Pair<GalleryBlock, Deferred<String>>>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
|
||||
class GalleryBlockAdapter(context: Context, private val galleries: List<GalleryBlock>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
|
||||
|
||||
enum class ViewType {
|
||||
NEXT,
|
||||
@@ -66,10 +62,13 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
PREV
|
||||
}
|
||||
|
||||
private val glide = Glide.with(context)
|
||||
private lateinit var favorites: Histories
|
||||
|
||||
inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||
fun bind(item: Pair<GalleryBlock, Deferred<String>>) {
|
||||
var timerTask: TimerTask? = null
|
||||
|
||||
fun bind(galleryBlock: GalleryBlock) {
|
||||
with(view) {
|
||||
val resources = context.resources
|
||||
val languages = resources.getStringArray(R.array.languages).map {
|
||||
@@ -78,16 +77,14 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
val (galleryBlock: GalleryBlock, thumbnail: Deferred<String>) = item
|
||||
|
||||
val artists = galleryBlock.artists
|
||||
val series = galleryBlock.series
|
||||
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val cache = thumbnail.await()
|
||||
val thumbnail = Base64.decode(Cache(context).getThumbnail(galleryBlock.id), Base64.DEFAULT)
|
||||
|
||||
glide
|
||||
.load(cache)
|
||||
.load(thumbnail)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.error(R.drawable.image_broken_variant)
|
||||
@@ -99,75 +96,67 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
}
|
||||
|
||||
//Check cache
|
||||
val readerCache = { File(getCachedGallery(context, galleryBlock.id), "reader.json") }
|
||||
val imageCache = { File(getCachedGallery(context, galleryBlock.id), "images") }
|
||||
val cache = Cache(context).getCachedGallery(galleryBlock.id)
|
||||
val reader = Cache(context).getReaderOrNull(galleryBlock.id)
|
||||
|
||||
try {
|
||||
Json(JsonConfiguration.Stable)
|
||||
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||
} catch(e: Exception) {
|
||||
readerCache.invoke().delete()
|
||||
}
|
||||
|
||||
if (readerCache.invoke().exists()) {
|
||||
val reader = Json(JsonConfiguration.Stable)
|
||||
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||
if (cache != null && reader != null) {
|
||||
val count = cache.listFiles { file ->
|
||||
file.nameWithoutExtension.toIntOrNull() != null
|
||||
}?.size ?: 0
|
||||
|
||||
with(galleryblock_progressbar) {
|
||||
max = reader.galleryInfo.size
|
||||
progress = imageCache.invoke().list()?.size ?: 0
|
||||
progress = count
|
||||
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
galleryblock_progressbar.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (refreshTasks[this@GalleryViewHolder] == null) {
|
||||
val refresh = Timer(false).schedule(0, 1000) {
|
||||
post {
|
||||
if (timerTask == null)
|
||||
timerTask = Timer().schedule(0, 1000) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val _cache = Cache(context).getCachedGallery(galleryBlock.id)
|
||||
val _reader = Cache(context).getReaderOrNull(galleryBlock.id)
|
||||
|
||||
if (_reader == null) {
|
||||
view.galleryblock_progressbar.visibility = View.GONE
|
||||
view.galleryblock_progress_complete.visibility = View.GONE
|
||||
return@launch
|
||||
}
|
||||
|
||||
with(view.galleryblock_progressbar) {
|
||||
progress = imageCache.invoke().list()?.size ?: 0
|
||||
|
||||
if (!readerCache.invoke().exists()) {
|
||||
visibility = View.GONE
|
||||
max = 0
|
||||
progress = 0
|
||||
progress = _cache?.listFiles { file ->
|
||||
file.nameWithoutExtension.toIntOrNull() != null
|
||||
}?.size ?: 0
|
||||
|
||||
view.galleryblock_progress_complete.visibility = View.INVISIBLE
|
||||
} else {
|
||||
if (visibility == View.GONE) {
|
||||
val reader = Json(JsonConfiguration.Stable)
|
||||
.parse(Reader.serializer(), readerCache.invoke().readText())
|
||||
max = reader.galleryInfo.size
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (progress == max) {
|
||||
if (completeFlag.get(galleryBlock.id, false)) {
|
||||
with(view.galleryblock_progress_complete) {
|
||||
setImageResource(R.drawable.ic_progressbar)
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
with(view.galleryblock_progress_complete) {
|
||||
setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.ic_progressbar_complete).apply {
|
||||
this?.start()
|
||||
})
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
completeFlag.put(galleryBlock.id, true)
|
||||
}
|
||||
} else
|
||||
view.galleryblock_progress_complete.visibility = View.INVISIBLE
|
||||
if (visibility == View.GONE) {
|
||||
visibility = View.VISIBLE
|
||||
max = _reader.galleryInfo.size
|
||||
}
|
||||
|
||||
if (progress == max) {
|
||||
if (completeFlag.get(galleryBlock.id, false)) {
|
||||
with(view.galleryblock_progress_complete) {
|
||||
setImageResource(R.drawable.ic_progressbar)
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
with(view.galleryblock_progress_complete) {
|
||||
setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.ic_progressbar_complete).apply {
|
||||
this?.start()
|
||||
})
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
completeFlag.put(galleryBlock.id, true)
|
||||
}
|
||||
} else
|
||||
view.galleryblock_progress_complete.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshTasks[this@GalleryViewHolder] = refresh
|
||||
}
|
||||
|
||||
galleryblock_title.text = galleryBlock.title
|
||||
with(galleryblock_artist) {
|
||||
text = artists.joinToString(", ") { it.wordCapitalize() }
|
||||
@@ -277,7 +266,6 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
}
|
||||
}
|
||||
|
||||
private val refreshTasks = HashMap<GalleryViewHolder, TimerTask>()
|
||||
val completeFlag = SparseBooleanArray()
|
||||
|
||||
val onChipClickedHandler = ArrayList<((Tag) -> Unit)>()
|
||||
@@ -336,10 +324,11 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
override fun onStartOpen(layout: SwipeLayout?) {
|
||||
mItemManger.closeAllExcept(layout)
|
||||
|
||||
holder.view.galleryblock_download.text = when(GalleryDownloader.get(gallery.first.id)) {
|
||||
null -> holder.view.context.getString(R.string.main_download)
|
||||
else -> holder.view.context.getString(android.R.string.cancel)
|
||||
}
|
||||
holder.view.galleryblock_download.text =
|
||||
if (DownloadWorker.getInstance(holder.view.context).progress.indexOfKey(gallery.id) < 0)
|
||||
holder.view.context.getString(R.string.main_download)
|
||||
else
|
||||
holder.view.context.getString(android.R.string.cancel)
|
||||
}
|
||||
|
||||
override fun onClose(layout: SwipeLayout?) {}
|
||||
@@ -354,12 +343,8 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
|
||||
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
|
||||
super.onViewDetachedFromWindow(holder)
|
||||
|
||||
if (holder is GalleryViewHolder) {
|
||||
val task = refreshTasks[holder] ?: return
|
||||
|
||||
task.cancel()
|
||||
refreshTasks.remove(holder)
|
||||
}
|
||||
if (holder is GalleryViewHolder)
|
||||
holder.timerTask?.cancel()
|
||||
}
|
||||
|
||||
override fun getItemCount() =
|
||||
|
||||
@@ -18,30 +18,47 @@
|
||||
|
||||
package xyz.quaver.pupil.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.RequestManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.item_reader.view.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.quaver.hitomi.Reader
|
||||
import xyz.quaver.pupil.BuildConfig
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.util.GalleryDownloader
|
||||
import xyz.quaver.pupil.util.getCachedGallery
|
||||
import java.io.File
|
||||
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,
|
||||
private val images: List<String>) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
|
||||
class ReaderAdapter(private val context: Context,
|
||||
private val galleryID: Int) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
|
||||
|
||||
var isFullScreen = false
|
||||
|
||||
var reader: Reader? = null
|
||||
private val glide = Glide.with(context)
|
||||
|
||||
var onItemClickListener : ((Int) -> (Unit))? = null
|
||||
|
||||
init {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
reader = Cache(context).getReader(galleryID)
|
||||
launch(Dispatchers.Main) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
@@ -60,38 +77,62 @@ class ReaderAdapter(private val glide: RequestManager,
|
||||
else
|
||||
holder.view.layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT
|
||||
|
||||
var reader: Reader? = null
|
||||
with (GalleryDownloader[galleryID]?.reader) {
|
||||
if (reader == null && this?.isCompleted == true)
|
||||
runBlocking {
|
||||
reader = await()
|
||||
}
|
||||
}
|
||||
|
||||
holder.view.image.setOnPhotoTapListener { _, _, _ ->
|
||||
onItemClickListener?.invoke(position)
|
||||
}
|
||||
|
||||
glide
|
||||
.load(File(getCachedGallery(holder.view.context, galleryID), images[position]))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.skipMemoryCache(true)
|
||||
.error(R.drawable.image_broken_variant)
|
||||
.apply {
|
||||
if (BuildConfig.CENSOR)
|
||||
override(5, 8)
|
||||
else {
|
||||
val galleryInfo = reader?.galleryInfo?.get(position)
|
||||
(holder.view.container.layoutParams as ConstraintLayout.LayoutParams)
|
||||
.dimensionRatio = "${reader!!.galleryInfo[position].width}:${reader!!.galleryInfo[position].height}"
|
||||
|
||||
if (galleryInfo != null) {
|
||||
(holder.view.image.layoutParams as ConstraintLayout.LayoutParams)
|
||||
.dimensionRatio = "${galleryInfo.width}:${galleryInfo.height}"
|
||||
}
|
||||
holder.view.reader_item_progressbar.progress = DownloadWorker.getInstance(context).progress[galleryID]?.get(position)?.roundToInt() ?: 0
|
||||
holder.view.reader_index.text = (position+1).toString()
|
||||
|
||||
val progress = DownloadWorker.getInstance(context).progress[galleryID]?.get(position)
|
||||
if (progress?.isFinite() == false) {
|
||||
when {
|
||||
progress.isInfinite() -> {
|
||||
var image = Cache(context).getImages(galleryID)
|
||||
|
||||
while (image?.get(position) == null)
|
||||
image = Cache(context).getImages(galleryID)
|
||||
|
||||
glide
|
||||
.load(image[position])
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.skipMemoryCache(true)
|
||||
.error(R.drawable.image_broken_variant)
|
||||
.apply {
|
||||
if (BuildConfig.CENSOR)
|
||||
override(5, 8)
|
||||
}
|
||||
.into(holder.view.image)
|
||||
}
|
||||
progress.isNaN() -> {
|
||||
glide
|
||||
.load(R.drawable.image_broken_variant)
|
||||
.into(holder.view.image)
|
||||
Snackbar
|
||||
.make(
|
||||
holder.view,
|
||||
DownloadWorker.getInstance(context).exception[galleryID]!![position]?.message
|
||||
?: context.getText(R.string.default_error_msg),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
.into(holder.view.image)
|
||||
|
||||
} else {
|
||||
holder.view.image.setImageDrawable(null)
|
||||
|
||||
Timer().schedule(1000) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = images.size
|
||||
override fun getItemCount() = reader?.galleryInfo?.size ?: 0
|
||||
|
||||
}
|
||||
@@ -45,7 +45,6 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.arlib.floatingsearchview.FloatingSearchView
|
||||
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
import com.arlib.floatingsearchview.util.view.SearchInputView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.activity_main_content.*
|
||||
@@ -64,11 +63,10 @@ import xyz.quaver.pupil.types.TagSuggestion
|
||||
import xyz.quaver.pupil.types.Tags
|
||||
import xyz.quaver.pupil.ui.dialog.GalleryDialog
|
||||
import xyz.quaver.pupil.util.*
|
||||
import xyz.quaver.pupil.util.download.Cache
|
||||
import xyz.quaver.pupil.util.download.DownloadWorker
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.ceil
|
||||
@@ -89,7 +87,7 @@ class MainActivity : AppCompatActivity() {
|
||||
POPULAR
|
||||
}
|
||||
|
||||
private val galleries = ArrayList<Pair<GalleryBlock, Deferred<String>>>()
|
||||
private val galleries = ArrayList<GalleryBlock>()
|
||||
|
||||
private var query = ""
|
||||
set(value) {
|
||||
@@ -386,7 +384,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
with(main_recyclerview) {
|
||||
adapter = GalleryBlockAdapter(Glide.with(this@MainActivity), galleries).apply {
|
||||
adapter = GalleryBlockAdapter(this@MainActivity, galleries).apply {
|
||||
onChipClickedHandler.add {
|
||||
runOnUiThread {
|
||||
query = it.toQuery()
|
||||
@@ -399,20 +397,18 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
onDownloadClickedHandler = { position ->
|
||||
val galleryID = galleries[position].first.id
|
||||
val galleryID = galleries[position].id
|
||||
|
||||
if (!completeFlag.get(galleryID, false)) {
|
||||
val downloader = GalleryDownloader.get(galleryID)
|
||||
val worker = DownloadWorker.getInstance(context)
|
||||
|
||||
if (downloader == null)
|
||||
GalleryDownloader(
|
||||
context,
|
||||
galleryID,
|
||||
true
|
||||
).start()
|
||||
if (worker.progress.indexOfKey(galleryID) >= 0) //download in progress
|
||||
worker.cancel(galleryID)
|
||||
else {
|
||||
downloader.cancel()
|
||||
downloader.clearNotification()
|
||||
Cache(context).moveToDownload(galleryID)
|
||||
|
||||
if (!worker.queue.contains(galleryID))
|
||||
worker.queue.add(galleryID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,39 +416,28 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
onDeleteClickedHandler = { position ->
|
||||
val galleryID = galleries[position].first.id
|
||||
val galleryID = galleries[position].id
|
||||
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
with(GalleryDownloader[galleryID]) {
|
||||
this?.cancelAndJoin()
|
||||
this?.clearNotification()
|
||||
DownloadWorker.getInstance(context).cancel(galleryID)
|
||||
|
||||
var cache = Cache(context).getCachedGallery(galleryID)
|
||||
|
||||
while (cache != null) {
|
||||
cache.deleteRecursively()
|
||||
cache = Cache(context).getCachedGallery(galleryID)
|
||||
}
|
||||
val cache = File(cacheDir, "imageCache/${galleryID}")
|
||||
val data = getCachedGallery(context, galleryID)
|
||||
cache.deleteRecursively()
|
||||
data.deleteRecursively()
|
||||
|
||||
downloads.remove(galleryID)
|
||||
|
||||
if (this@MainActivity.mode == Mode.DOWNLOAD) {
|
||||
runOnUiThread {
|
||||
cancelFetch()
|
||||
clearGalleries()
|
||||
fetchGalleries(query, sortMode)
|
||||
loadBlocks()
|
||||
}
|
||||
}
|
||||
|
||||
histories.remove(galleryID)
|
||||
|
||||
if (this@MainActivity.mode == Mode.HISTORY) {
|
||||
if (this@MainActivity.mode != Mode.SEARCH)
|
||||
runOnUiThread {
|
||||
cancelFetch()
|
||||
clearGalleries()
|
||||
fetchGalleries(query, sortMode)
|
||||
loadBlocks()
|
||||
}
|
||||
}
|
||||
|
||||
completeFlag.put(galleryID, false)
|
||||
}
|
||||
@@ -466,7 +451,7 @@ class MainActivity : AppCompatActivity() {
|
||||
return@setOnItemClickListener
|
||||
|
||||
val intent = Intent(this@MainActivity, ReaderActivity::class.java)
|
||||
val gallery = galleries[position].first
|
||||
val gallery = galleries[position]
|
||||
intent.putExtra("galleryID", gallery.id)
|
||||
|
||||
//TODO: Maybe sprinkling some transitions will be nice :D
|
||||
@@ -478,7 +463,7 @@ class MainActivity : AppCompatActivity() {
|
||||
if (v !is CardView)
|
||||
return@setOnItemLongClickListener true
|
||||
|
||||
val galleryID = galleries[position].first.id
|
||||
val galleryID = galleries[position].id
|
||||
|
||||
GalleryDialog(
|
||||
this@MainActivity,
|
||||
@@ -1030,41 +1015,21 @@ class MainActivity : AppCompatActivity() {
|
||||
val json = Json(JsonConfiguration.Stable)
|
||||
val serializer = GalleryBlock.serializer()
|
||||
|
||||
val galleryBlock =
|
||||
File(getCachedGallery(this@MainActivity, galleryID), "galleryBlock.json").let { cache ->
|
||||
when {
|
||||
cache.exists() -> json.parse(serializer, cache.readText())
|
||||
else -> {
|
||||
getGalleryBlock(galleryID).apply {
|
||||
this ?: return@apply
|
||||
File(getCachedGallery(this@MainActivity, galleryID), "galleryBlock.json").let { cache ->
|
||||
when {
|
||||
cache.exists() -> json.parse(serializer, cache.readText())
|
||||
else -> {
|
||||
getGalleryBlock(galleryID).apply {
|
||||
this ?: return@apply
|
||||
|
||||
if (cache.parentFile?.exists() == false)
|
||||
cache.parentFile!!.mkdirs()
|
||||
if (cache.parentFile?.exists() == false)
|
||||
cache.parentFile!!.mkdirs()
|
||||
|
||||
cache.writeText(json.stringify(serializer, this))
|
||||
}
|
||||
cache.writeText(json.stringify(serializer, this))
|
||||
}
|
||||
}
|
||||
} ?: return@async null
|
||||
|
||||
val thumbnail = async {
|
||||
val ext = galleryBlock.thumbnails[0].split('.').last()
|
||||
File(getCachedGallery(this@MainActivity, galleryBlock.id), "thumbnail.$ext").apply {
|
||||
if (!exists())
|
||||
try {
|
||||
with(URL(galleryBlock.thumbnails[0]).openConnection() as HttpsURLConnection) {
|
||||
if (this@apply.parentFile?.exists() == false)
|
||||
this@apply.parentFile!!.mkdirs()
|
||||
|
||||
inputStream.copyTo(FileOutputStream(this@apply))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
delete()
|
||||
}
|
||||
}.absolutePath
|
||||
}
|
||||
|
||||
Pair(galleryBlock, thumbnail)
|
||||
}
|
||||
} ?: return@async null
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -21,39 +21,39 @@ package xyz.quaver.pupil.ui
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.fabric.sdk.android.Fabric
|
||||
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 kotlinx.serialization.ImplicitReflectionSerializer
|
||||
import xyz.quaver.hitomi.Reader
|
||||
import xyz.quaver.pupil.Pupil
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.adapters.ReaderAdapter
|
||||
import xyz.quaver.pupil.util.GalleryDownloader
|
||||
import xyz.quaver.pupil.util.Histories
|
||||
import xyz.quaver.pupil.util.download.Cache
|
||||
import xyz.quaver.pupil.util.download.DownloadWorker
|
||||
import xyz.quaver.pupil.util.getDownloadDirectory
|
||||
import xyz.quaver.pupil.util.isParentOf
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
private var galleryID = 0
|
||||
private val images = ArrayList<String>()
|
||||
private var gallerySize = 0
|
||||
private var currentPage = 0
|
||||
|
||||
private var isScroll = true
|
||||
@@ -69,7 +69,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var downloader: GalleryDownloader
|
||||
private val timer = Timer()
|
||||
|
||||
private val snapHelper = PagerSnapHelper()
|
||||
|
||||
@@ -101,12 +101,8 @@ class ReaderActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
initDownloader()
|
||||
|
||||
initView()
|
||||
|
||||
if (!downloader.download)
|
||||
downloader.start()
|
||||
initDownloader()
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
@@ -168,7 +164,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false)
|
||||
with(view.dialog_number_picker) {
|
||||
minValue=1
|
||||
maxValue=gallerySize
|
||||
maxValue=reader_recyclerview.adapter?.itemCount ?: 0
|
||||
value=currentPage
|
||||
}
|
||||
val dialog = AlertDialog.Builder(this).apply {
|
||||
@@ -201,8 +197,10 @@ class ReaderActivity : AppCompatActivity() {
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
if (::downloader.isInitialized && !downloader.download)
|
||||
downloader.cancel()
|
||||
timer.cancel()
|
||||
|
||||
if (!getDownloadDirectory(this).isParentOf(Cache(this).getCachedGallery(galleryID)))
|
||||
DownloadWorker.getInstance(this).cancel(galleryID)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
@@ -238,101 +236,54 @@ class ReaderActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun initDownloader() {
|
||||
var d: GalleryDownloader? = GalleryDownloader.get(galleryID)
|
||||
|
||||
if (d == null)
|
||||
d = GalleryDownloader(this, galleryID)
|
||||
|
||||
downloader = d.apply {
|
||||
onReaderLoadedHandler = {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
title = it.title
|
||||
with(reader_download_progressbar) {
|
||||
max = it.galleryInfo.size
|
||||
progress = 0
|
||||
}
|
||||
with(reader_progressbar) {
|
||||
max = it.galleryInfo.size
|
||||
progress = 0
|
||||
}
|
||||
|
||||
gallerySize = it.galleryInfo.size
|
||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${it.galleryInfo.size}"
|
||||
}
|
||||
}
|
||||
onProgressHandler = {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
reader_download_progressbar.progress = it
|
||||
menu?.findItem(R.id.reader_menu_use_hiyobi)?.isVisible = downloader.useHiyobi
|
||||
}
|
||||
}
|
||||
onDownloadedHandler = {
|
||||
val item = it.toList()
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
if (images.isEmpty()) {
|
||||
images.addAll(item)
|
||||
reader_recyclerview.adapter?.notifyDataSetChanged()
|
||||
} else {
|
||||
images.add(item.last())
|
||||
reader_recyclerview.adapter?.notifyItemInserted(images.size-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
onErrorHandler = {
|
||||
Snackbar
|
||||
.make(reader_layout, it.message ?: it.javaClass.name, Snackbar.LENGTH_INDEFINITE)
|
||||
.setAction(R.string.reader_help) {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.error_help))))
|
||||
}
|
||||
.show()
|
||||
downloader.download = false
|
||||
}
|
||||
onCompleteHandler = {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
reader_download_progressbar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
onNotifyChangedHandler = { notify ->
|
||||
val fab = reader_fab_download
|
||||
|
||||
runOnUiThread {
|
||||
if (notify) {
|
||||
val icon = AnimatedVectorDrawableCompat.create(this, R.drawable.ic_downloading)
|
||||
icon?.registerAnimationCallback(object: Animatable2Compat.AnimationCallback() {
|
||||
override fun onAnimationEnd(drawable: Drawable?) {
|
||||
if (downloader.download)
|
||||
fab.post {
|
||||
icon.start()
|
||||
fab.labelText = getString(R.string.reader_fab_download_cancel)
|
||||
}
|
||||
else
|
||||
fab.post {
|
||||
fab.setImageResource(R.drawable.ic_download)
|
||||
fab.labelText = getString(R.string.reader_fab_download)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
fab.setImageDrawable(icon)
|
||||
icon?.start()
|
||||
} else {
|
||||
runOnUiThread {
|
||||
fab.setImageResource(R.drawable.ic_download)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val worker = DownloadWorker.getInstance(this).apply {
|
||||
queue.add(galleryID)
|
||||
}
|
||||
|
||||
if (downloader.download) {
|
||||
downloader.invokeOnReaderLoaded()
|
||||
downloader.invokeOnNotifyChanged()
|
||||
timer.schedule(0, 1000) {
|
||||
if (worker.progress.indexOfKey(galleryID) < 0) //loading
|
||||
return@schedule
|
||||
|
||||
if (worker.progress[galleryID] == null) { //Gallery not found
|
||||
timer.cancel()
|
||||
Snackbar
|
||||
.make(reader_layout, R.string.reader_failed_to_find_gallery, Snackbar.LENGTH_INDEFINITE)
|
||||
.show()
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
||||
reader_download_progressbar.progress = worker.progress[galleryID]!!.count { !it.isFinite() }
|
||||
reader_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
||||
|
||||
if (title == getString(R.string.reader_loading)) {
|
||||
val reader = (reader_recyclerview.adapter as ReaderAdapter).reader
|
||||
|
||||
if (reader != null) {
|
||||
title = reader.title
|
||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${reader.galleryInfo.size}"
|
||||
|
||||
menu?.findItem(R.id.reader_type)?.icon = ContextCompat.getDrawable(this@ReaderActivity,
|
||||
when (reader.code) {
|
||||
Reader.Code.HITOMI -> R.drawable.hitomi
|
||||
Reader.Code.HIYOBI -> R.drawable.ic_hiyobi
|
||||
else -> android.R.color.transparent
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (worker.progress[galleryID]!!.all { !it.isFinite() }) { //Download finished
|
||||
reader_download_progressbar.visibility = View.GONE
|
||||
|
||||
animateDownloadFAB(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
with(reader_recyclerview) {
|
||||
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID, images).apply {
|
||||
adapter = ReaderAdapter(this@ReaderActivity, galleryID).apply {
|
||||
onItemClickListener = {
|
||||
if (isScroll) {
|
||||
isScroll = false
|
||||
@@ -360,19 +311,19 @@ class ReaderActivity : AppCompatActivity() {
|
||||
if (layoutManager.findFirstVisibleItemPosition() == -1)
|
||||
return
|
||||
currentPage = layoutManager.findFirstVisibleItemPosition()+1
|
||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/$gallerySize"
|
||||
menu?.findItem(R.id.reader_menu_page_indicator)?.title = "$currentPage/${recyclerView.adapter!!.itemCount}"
|
||||
this@ReaderActivity.reader_progressbar.progress = currentPage
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
with(reader_fab_download) {
|
||||
setImageResource(R.drawable.ic_download)
|
||||
setOnClickListener {
|
||||
downloader.download = !downloader.download
|
||||
animateDownloadFAB(getDownloadDirectory(context).isParentOf(Cache(context).getCachedGallery(galleryID))) //If download in progress, animate button
|
||||
|
||||
if (!downloader.download)
|
||||
downloader.clearNotification()
|
||||
setOnClickListener {
|
||||
Cache(context).moveToDownload(galleryID)
|
||||
|
||||
animateDownloadFAB(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,4 +365,32 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0)
|
||||
}
|
||||
|
||||
private fun animateDownloadFAB(animate: Boolean) {
|
||||
with(reader_fab_download) {
|
||||
if (animate) {
|
||||
val icon = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_downloading)
|
||||
|
||||
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
|
||||
post {
|
||||
setImageResource(R.drawable.ic_download)
|
||||
labelText = getString(R.string.reader_fab_download)
|
||||
}
|
||||
else // Or continue animating
|
||||
post {
|
||||
icon.start()
|
||||
labelText = getString(R.string.reader_fab_download_cancel)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
setImageDrawable(icon)
|
||||
icon?.start()
|
||||
} else
|
||||
setImageResource(R.drawable.ic_download)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,10 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.dialog_gallery.*
|
||||
import kotlinx.android.synthetic.main.gallery_details.view.*
|
||||
import kotlinx.android.synthetic.main.item_gallery_details.view.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.quaver.hitomi.Gallery
|
||||
import xyz.quaver.hitomi.GalleryBlock
|
||||
import xyz.quaver.hitomi.getGallery
|
||||
@@ -221,9 +224,9 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
|
||||
private fun addRelated(gallery: Gallery) {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val galleries = ArrayList<Pair<GalleryBlock, Deferred<String>>>()
|
||||
val galleries = ArrayList<GalleryBlock>()
|
||||
|
||||
val adapter = GalleryBlockAdapter(glide, galleries).apply {
|
||||
val adapter = GalleryBlockAdapter(context, galleries).apply {
|
||||
onChipClickedHandler.add { tag ->
|
||||
this@GalleryDialog.onChipClickedHandler.forEach { handler ->
|
||||
handler.invoke(tag)
|
||||
@@ -238,7 +241,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
}.let {
|
||||
val galleryBlock = it.await() ?: return@let
|
||||
|
||||
galleries.add(Pair(galleryBlock, GlobalScope.async { galleryBlock.thumbnails.first() }))
|
||||
galleries.add(galleryBlock)
|
||||
adapter.notifyItemInserted(i)
|
||||
}
|
||||
}
|
||||
@@ -254,14 +257,14 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
||||
ItemClickSupport.addTo(this)
|
||||
.setOnItemClickListener { _, position, _ ->
|
||||
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||
putExtra("galleryID", galleries[position].first.id)
|
||||
putExtra("galleryID", galleries[position].id)
|
||||
})
|
||||
(context.applicationContext as Pupil).histories.add(galleries[position].first.id)
|
||||
(context.applicationContext as Pupil).histories.add(galleries[position].id)
|
||||
}
|
||||
.setOnItemLongClickListener { _, position, _ ->
|
||||
GalleryDialog(
|
||||
context,
|
||||
galleries[position].first.id
|
||||
galleries[position].id
|
||||
).apply {
|
||||
onChipClickedHandler.add { tag ->
|
||||
this@GalleryDialog.onChipClickedHandler.forEach { it.invoke(tag) }
|
||||
|
||||
@@ -78,16 +78,14 @@ class Cache(context: Context) : ContextWrapper(context) {
|
||||
|
||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||
fun setCachedMetadata(galleryID: Int, metadata: Metadata) {
|
||||
val file = File(getCachedGallery(galleryID), ".metadata")
|
||||
val file = File(getCachedGallery(galleryID) ?: File(cacheDir, "imageCache/$galleryID"), ".metadata")
|
||||
|
||||
if (!file.exists())
|
||||
return
|
||||
if (file.parentFile?.exists() != true)
|
||||
file.parentFile?.mkdirs()
|
||||
|
||||
try {
|
||||
file.writeText(Json.stringify(metadata))
|
||||
} catch (e: Exception) {
|
||||
file.createNewFile()
|
||||
|
||||
}
|
||||
file.writeText(Json.stringify(metadata))
|
||||
}
|
||||
|
||||
suspend fun getThumbnail(galleryID: Int): String? {
|
||||
@@ -135,6 +133,16 @@ class Cache(context: Context) : ContextWrapper(context) {
|
||||
return galleryBlock
|
||||
}
|
||||
|
||||
fun getReaderOrNull(galleryID: Int): Reader? {
|
||||
val metadata = getCachedMetadata(galleryID)
|
||||
|
||||
val mirrors = preference.getString("mirrors", "")!!.split('>')
|
||||
|
||||
return metadata?.readers?.firstOrNull {
|
||||
mirrors.contains(it.code.name)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReader(galleryID: Int): Reader? {
|
||||
val metadata = getCachedMetadata(galleryID)
|
||||
|
||||
@@ -173,7 +181,7 @@ class Cache(context: Context) : ContextWrapper(context) {
|
||||
gallery.listFiles { file ->
|
||||
file.nameWithoutExtension.toIntOrNull() != null
|
||||
}?.forEach {
|
||||
append(it.nameWithoutExtension.toInt(), it)
|
||||
put(it.nameWithoutExtension.toInt(), it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,14 +205,17 @@ class Cache(context: Context) : ContextWrapper(context) {
|
||||
}
|
||||
|
||||
fun moveToDownload(galleryID: Int) {
|
||||
val cache = getCachedGallery(galleryID) ?: File(cacheDir, "imageCache/$galleryID")
|
||||
val cache = getCachedGallery(galleryID)
|
||||
|
||||
val download = getDownloadDirectory(this)
|
||||
if (cache != null) {
|
||||
val download = getDownloadDirectory(this)
|
||||
|
||||
if (!download.isParentOf(cache)) {
|
||||
cache.copyRecursively(download)
|
||||
cache.deleteRecursively()
|
||||
}
|
||||
if (!download.isParentOf(cache)) {
|
||||
cache.copyRecursively(download, true)
|
||||
cache.deleteRecursively()
|
||||
}
|
||||
} else
|
||||
File(getDownloadDirectory(this), galleryID.toString()).mkdirs()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,8 +49,8 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
override fun update(tag: Any?, bytesRead: Long, contentLength: Long, done: Boolean) {
|
||||
val (galleryID, index) = (tag as? Pair<Int, Int>) ?: return
|
||||
|
||||
if (!done && progress[galleryID]!![index] != Float.POSITIVE_INFINITY)
|
||||
progress[galleryID]!![index] = bytesRead * 100F / contentLength
|
||||
if (!done && progress[galleryID]?.get(index)?.isFinite() == true)
|
||||
progress[galleryID]?.set(index, bytesRead * 100F / contentLength)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,14 +156,20 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
.build()
|
||||
|
||||
fun stop() {
|
||||
queue.clear()
|
||||
|
||||
loop.cancel()
|
||||
for (i in 0..worker.size())
|
||||
worker[worker.keyAt(i)]?.cancel()
|
||||
|
||||
client.dispatcher.cancelAll()
|
||||
|
||||
progress.clear()
|
||||
exception.clear()
|
||||
}
|
||||
|
||||
fun cancel(galleryID: Int) {
|
||||
queue.remove(galleryID)
|
||||
worker[galleryID]?.cancel()
|
||||
|
||||
client.dispatcher.queuedCalls()
|
||||
@@ -171,6 +177,13 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
.forEach {
|
||||
it.cancel()
|
||||
}
|
||||
|
||||
if (progress.indexOfKey(galleryID) >= 0) {
|
||||
progress.remove(galleryID)
|
||||
exception.remove(galleryID)
|
||||
|
||||
nRunners--
|
||||
}
|
||||
}
|
||||
|
||||
private fun queueDownload(galleryID: Int, reader: Reader, index: Int, callback: Callback) {
|
||||
@@ -179,7 +192,7 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
|
||||
//Cache exists :P
|
||||
cache?.get(index)?.let {
|
||||
progress[galleryID]!![index] = Float.POSITIVE_INFINITY
|
||||
progress[galleryID]?.set(index, Float.POSITIVE_INFINITY)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -231,11 +244,15 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
if (Fabric.isInitialized())
|
||||
Crashlytics.logException(e)
|
||||
|
||||
progress[galleryID]!![i] = Float.NaN
|
||||
exception[galleryID]!![i] = e
|
||||
progress[galleryID]?.set(i, Float.NaN)
|
||||
exception[galleryID]?.set(i, e)
|
||||
|
||||
if (progress[galleryID]?.all { !it.isFinite() } == true) {
|
||||
progress.remove(galleryID)
|
||||
exception.remove(galleryID)
|
||||
|
||||
if (progress[galleryID]!!.all { !it.isFinite() })
|
||||
nRunners--
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
@@ -245,11 +262,15 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
|
||||
call.request().url.encodedPath.split('.').last()
|
||||
|
||||
Cache(this@DownloadWorker).putImage(galleryID, "$i.$ext", res)
|
||||
progress[galleryID]!![i] = Float.POSITIVE_INFINITY
|
||||
progress[galleryID]?.set(i, Float.POSITIVE_INFINITY)
|
||||
}
|
||||
|
||||
if (progress[galleryID]!!.all { !it.isFinite() })
|
||||
if (progress[galleryID]?.all { !it.isFinite() } == true) {
|
||||
progress.remove(galleryID)
|
||||
exception.remove(galleryID)
|
||||
|
||||
nRunners--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,4 +64,4 @@ fun URL.download(to: File, onDownloadProgress: ((Long, Long) -> Unit)? = null) {
|
||||
}
|
||||
}
|
||||
|
||||
fun File.isParentOf(file: File) = file.absolutePath.startsWith(this.absolutePath)
|
||||
fun File.isParentOf(file: File?) = file?.absolutePath?.startsWith(this.absolutePath) ?: false
|
||||
BIN
app/src/main/res/drawable/hitomi.png
Normal file
BIN
app/src/main/res/drawable/hitomi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
34
app/src/main/res/drawable/reader_item_boundary.xml
Normal file
34
app/src/main/res/drawable/reader_item_boundary.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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/>.
|
||||
-->
|
||||
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:bottom="1dp"
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:top="1dp">
|
||||
<shape android:shape="rectangle" >
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#555555" />
|
||||
|
||||
<solid android:color="@color/transparent" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -23,15 +23,49 @@
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<com.github.chrisbanes.photoview.PhotoView
|
||||
android:id="@+id/image"
|
||||
android:contentDescription="@string/reader_imageview_description"
|
||||
android:layout_width="0dp"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:paddingBottom="8dp"/>
|
||||
android:background="@drawable/reader_item_boundary">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/reader_item_progressbar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="?android:progressBarStyleHorizontal"
|
||||
android:indeterminate="false"
|
||||
android:progress="0"
|
||||
android:max="100"
|
||||
android:visibility="visible"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reader_index"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.AppCompat.Caption"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.github.chrisbanes.photoview.PhotoView
|
||||
android:id="@+id/image"
|
||||
android:contentDescription="@string/reader_imageview_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="8dp"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -25,9 +25,8 @@
|
||||
android:icon="@drawable/avd_star"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item android:id="@+id/reader_menu_use_hiyobi"
|
||||
<item android:id="@+id/reader_type"
|
||||
android:title=""
|
||||
android:icon="@drawable/ic_hiyobi"
|
||||
app:showAsAction="ifRoom"
|
||||
android:visible="false"/>
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<string name="settings_mirror_summary">ミラーサーバからイメージをロード</string>
|
||||
<string name="main_drawer_favorite">お気に入り</string>
|
||||
<string name="main_open_gallery_by_id">ギャラリー番号で見る</string>
|
||||
<string name="main_open_gallery_by_id_error">エラーが発生しました</string>
|
||||
<string name="reader_failed_to_find_gallery">エラーが発生しました</string>
|
||||
<string name="settings_storage">ストレージ</string>
|
||||
<string name="main_drawer_grouop_contact_discord">ディスコード</string>
|
||||
<string name="settings_app_lock">アプリロック</string>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<string name="settings_clear_downloads_alert_message">다운로드 된 만화를 모두 삭제합니다.\n계속하시겠습니까?</string>
|
||||
<string name="main_drawer_favorite">즐겨찾기</string>
|
||||
<string name="main_open_gallery_by_id">갤러리 번호로 열기</string>
|
||||
<string name="main_open_gallery_by_id_error">갤러리를 찾지 못했습니다</string>
|
||||
<string name="reader_failed_to_find_gallery">갤러리를 찾지 못했습니다</string>
|
||||
<string name="settings_storage">저장 공간</string>
|
||||
<string name="main_drawer_grouop_contact_discord">디스코드</string>
|
||||
<string name="settings_app_lock">앱 잠금</string>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<string name="main_jump_title">Jump to page</string>
|
||||
<string name="main_jump_message">Current page: %1$d\nMaximum page: %2$d</string>
|
||||
<string name="main_open_gallery_by_id">Open Gallery by ID</string>
|
||||
<string name="main_open_gallery_by_id_error">Failed to open gallery</string>
|
||||
<string name="reader_failed_to_find_gallery">Failed to open gallery</string>
|
||||
|
||||
<string name="main_move">Move to page %1$d</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user