diff --git a/app/build.gradle b/app/build.gradle index 9c121c79..1a65c84b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,7 +21,7 @@ android { minSdkVersion 16 targetSdkVersion 30 versionCode 59 - versionName "5.0.2" + versionName "5.0.2-hotfix1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } diff --git a/app/libs/recyclerviewfastscroller-release.aar b/app/libs/recyclerviewfastscroller-release.aar index 9b121127..d0f8ce00 100644 Binary files a/app/libs/recyclerviewfastscroller-release.aar and b/app/libs/recyclerviewfastscroller-release.aar differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 003d2f82..54de8e06 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -12,7 +12,7 @@ "filters": [], "properties": [], "versionCode": 59, - "versionName": "5.0.1-hotfix2", + "versionName": "5.0.2-hotfix1", "enabled": true, "outputFile": "app-release.apk" } diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt index c37d0003..99408289 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -80,8 +80,8 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri CoroutineScope(Dispatchers.Main).launch { if (cache.metadata.reader == null || Preferences["cache_disable"]) { - view.galleryblock_progressbar.visibility = View.GONE - view.galleryblock_progress_complete.visibility = View.GONE + view.galleryblock_progressbar_layout.visibility = View.GONE + view.galleryblock_progress_complete.visibility = View.INVISIBLE return@launch } @@ -91,8 +91,10 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri progress = imageList.filterNotNull().size max = imageList.size - if (visibility == View.GONE) - visibility = View.VISIBLE + with(view.galleryblock_progressbar_layout) { + if (visibility == View.GONE) + visibility = View.VISIBLE + } if (progress == max) { val downloadManager = DownloadManager.getInstance(context) @@ -126,8 +128,6 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri } fun bind(galleryID: Int) { - val time = System.currentTimeMillis() - val cache = Cache.getInstance(view.context, galleryID) val galleryBlock = cache.metadata.galleryBlock ?: return @@ -223,14 +223,14 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri galleryblock_tag_group.removeAllViews() CoroutineScope(Dispatchers.Default).launch { - galleryBlock.relatedTags.forEach { + galleryBlock.relatedTags.map { TagChip(context, Tag.parse(it)).apply { setOnClickListener { view -> for (callback in onChipClickedHandler) callback.invoke((view as TagChip).tag) } - }.let { launch(Dispatchers.Main) { galleryblock_tag_group.addView(it) } } - } + } + }.let { launch(Dispatchers.Main) { it.forEach { galleryblock_tag_group.addView(it) } } } } galleryblock_id.text = galleryBlock.id.toString() @@ -279,7 +279,6 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri galleryblock_tag_group.visibility = View.GONE } } - Log.i("PUPILD", "${System.currentTimeMillis() - time}") } } class NextViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view) diff --git a/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt b/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt index 92d6a2ad..3b4eb8bc 100644 --- a/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt +++ b/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt @@ -28,6 +28,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.app.TaskStackBuilder import androidx.core.content.ContextCompat +import com.google.firebase.crashlytics.FirebaseCrashlytics import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -42,6 +43,7 @@ import xyz.quaver.pupil.R import xyz.quaver.pupil.client import xyz.quaver.pupil.interceptors import xyz.quaver.pupil.ui.ReaderActivity +import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.DownloadManager import xyz.quaver.pupil.util.ellipsize @@ -309,6 +311,18 @@ class DownloadService : Service() { progress.put(galleryID, MutableList(reader.galleryInfo.files.size) { 0F }) + FirebaseCrashlytics.getInstance().log( + """ + GALLERYID: $galleryID + CACHE: ${cache.findFile(".metadata")} + PATTERN: ${Preferences["download_folder_name", ""]} + READER ID: ${reader.galleryInfo.id} + READER SIZE: ${reader.galleryInfo.files.size} + CACHE READER ID: ${cache.metadata.reader?.galleryInfo?.id}} + CACHE READER SIZE: ${cache.metadata.reader?.galleryInfo?.files?.size} + """.trimIndent() + ) + cache.metadata.imageList?.let { if (progress[galleryID]?.size != it.size) { cache.metadata.imageList?.filterNotNull()?.forEach { file -> diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt index e54ace31..6fe1b42a 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt @@ -18,13 +18,13 @@ package xyz.quaver.pupil.ui.dialog -import android.app.Dialog import android.content.Context import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.widget.LinearLayout.LayoutParams +import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -53,7 +53,7 @@ import xyz.quaver.pupil.util.ItemClickSupport import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.wordCapitalize -class GalleryDialog(context: Context, private val glide: RequestManager, private val galleryID: Int) : Dialog(context) { +class GalleryDialog(context: Context, private val glide: RequestManager, private val galleryID: Int) : AlertDialog(context) { val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>() diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/ProxyDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/ProxyDialog.kt index cd79be1d..ef387396 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/ProxyDialog.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/ProxyDialog.kt @@ -27,6 +27,7 @@ import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter +import androidx.appcompat.app.AlertDialog import kotlinx.android.synthetic.main.dialog_proxy.view.* import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -40,7 +41,7 @@ import xyz.quaver.pupil.util.getProxyInfo import xyz.quaver.pupil.util.proxyInfo import java.net.Proxy -class ProxyDialog(context: Context) : Dialog(context) { +class ProxyDialog(context: Context) : AlertDialog(context) { override fun onCreate(savedInstanceState: Bundle?) { setContentView(build()) diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt b/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt deleted file mode 100644 index 46e7b8e7..00000000 --- a/app/src/main/java/xyz/quaver/pupil/util/download/Cache.kt +++ /dev/null @@ -1,297 +0,0 @@ -/* - * 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 . - */ - -package xyz.quaver.pupil.util.download - -import android.content.Context -import android.content.ContextWrapper -import android.util.Base64 -import android.util.SparseArray -import androidx.preference.PreferenceManager -import com.google.firebase.crashlytics.FirebaseCrashlytics -import kotlinx.coroutines.* -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import xyz.quaver.Code -import xyz.quaver.hitomi.GalleryBlock -import xyz.quaver.hitomi.Reader -import xyz.quaver.pupil.util.getCachedGallery -import xyz.quaver.pupil.util.getDownloadDirectory -import xyz.quaver.pupil.util.isParentOf -import xyz.quaver.readBytes -import java.io.BufferedInputStream -import java.io.File -import java.io.FileOutputStream -import java.io.InputStream -import java.net.URL - -@Suppress("DEPRECATION") -@Deprecated("Use downloader.Cache instead") -class Cache(context: Context) : ContextWrapper(context) { - - companion object { - private val moving = mutableListOf() - private val readers = SparseArray() - } - - private val preference = PreferenceManager.getDefaultSharedPreferences(this) - - // Search in this order - // Download -> Cache - fun getCachedGallery(galleryID: Int) = getCachedGallery(this, galleryID).also { - if (!it.exists()) - it.mkdirs() - } - - fun getCachedMetadata(galleryID: Int) : Metadata? { - val file = File(getCachedGallery(galleryID), ".metadata") - - if (!file.exists()) - return null - - return try { - Json.decodeFromString(file.readText()) - } catch (e: Exception) { - //File corrupted - file.delete() - null - } - } - - fun setCachedMetadata(galleryID: Int, metadata: Metadata) { - if (preference.getBoolean("cache_disable", false)) - return - - val file = File(getCachedGallery(galleryID), ".metadata").also { - if (!it.exists()) - it.createNewFile() - } - - file.writeText(Json.encodeToString(metadata)) - } - - suspend fun getThumbnail(galleryID: Int): String? { - val metadata = Cache(this).getCachedMetadata(galleryID) - - @Suppress("BlockingMethodInNonBlockingContext") - val thumbnail = if (metadata?.thumbnail == null) - withContext(Dispatchers.IO) { - val thumbnail = getGalleryBlock(galleryID)?.thumbnails?.firstOrNull() ?: return@withContext null - try { - val data = URL(thumbnail).readBytes().apply { - if (isEmpty()) return@withContext null - } - Base64.encodeToString(data, Base64.DEFAULT) - } catch (e: Exception) { - null - } - } - else - metadata.thumbnail - - setCachedMetadata( - galleryID, - Metadata(Cache(this).getCachedMetadata(galleryID), thumbnail = thumbnail) - ) - - return thumbnail - } - - suspend fun getGalleryBlock(galleryID: Int): GalleryBlock? { - val metadata = Cache(this).getCachedMetadata(galleryID) - - val sources = listOf( - { xyz.quaver.hitomi.getGalleryBlock(galleryID) }, - { xyz.quaver.hiyobi.getGalleryBlock(galleryID) } - ) - - val galleryBlock = if (metadata?.galleryBlock == null) { - withContext(Dispatchers.IO) { - var galleryBlock: GalleryBlock? = null - - for (source in sources) { - galleryBlock = try { - source.invoke() - } catch (e: Exception) { - null - } - - if (galleryBlock != null) - break - } - - galleryBlock - } ?: return null - } - else - metadata.galleryBlock - - setCachedMetadata( - galleryID, - Metadata(Cache(this).getCachedMetadata(galleryID), galleryBlock = galleryBlock) - ) - - return galleryBlock - } - - fun getReaderOrNull(galleryID: Int): Reader? { - return readers[galleryID] ?: getCachedMetadata(galleryID)?.reader - } - - suspend fun getReader(galleryID: Int): Reader? { - val metadata = getCachedMetadata(galleryID) - val mirrors = preference.getString("mirrors", null)?.split('>') ?: listOf() - - val sources = mapOf( - Code.HITOMI to { xyz.quaver.hitomi.getReader(galleryID) }, - Code.HIYOBI to { xyz.quaver.hiyobi.getReader(galleryID) } - ).let { - if (mirrors.isNotEmpty()) - it.toSortedMap{ o1, o2 -> - mirrors.indexOf(o1.name) - mirrors.indexOf(o2.name) - } - else - it - } - - val reader = - if (readers[galleryID] != null) - return readers[galleryID] - else if (metadata?.reader == null) { - var retval: Reader? = null - - for (source in sources) { - retval = try { - withContext(Dispatchers.IO) { - withTimeoutOrNull(1000) { - source.value.invoke() - } - } - } catch (e: Exception) { - FirebaseCrashlytics.getInstance().recordException(e) - null - } - - if (retval != null) - break - } - - retval - } else - metadata.reader - - readers.put(galleryID, reader) - - setCachedMetadata( - galleryID, - Metadata(Cache(this).getCachedMetadata(galleryID), readers = reader) - ) - - return reader - } - - val imageNameRegex = Regex("""^\d+\..+$""") - fun getImages(galleryID: Int): List? { - val gallery = getCachedGallery(galleryID) - - return gallery.list { _, name -> - imageNameRegex.matches(name) - }?.map { - File(gallery, it) - } - } - - val imageExtensions = listOf( - "png", - "jpg", - "webp", - "gif" - ) - fun getImage(galleryID: Int, index: Int): File? { - val gallery = getCachedGallery(galleryID) - - for (ext in imageExtensions) { - File(gallery, "%05d.$ext".format(index)).let { - if (it.exists()) - return it - } - } - - return null - } - - - fun putImage(galleryID: Int, index: Int, ext: String, data: InputStream) { - if (preference.getBoolean("cache_disable", false)) - return - - val cache = File(getCachedGallery(galleryID), "%05d.$ext".format(index)).also { - if (!it.exists()) - it.createNewFile() - } - - try { - BufferedInputStream(data).use { inputStream -> - FileOutputStream(cache).use { outputStream -> - inputStream.copyTo(outputStream) - } - } - } catch (e: Exception) { - cache.delete() - } - } - - fun moveToDownload(galleryID: Int) { - if (preference.getBoolean("cache_disable", false)) - return - - if (moving.contains(galleryID)) - return - - CoroutineScope(Dispatchers.IO).launch { - val cache = getCachedGallery(galleryID).also { - if (!it.exists()) - return@launch - } - val download = File(getDownloadDirectory(this@Cache), galleryID.toString()) - - if (download.isParentOf(cache)) - return@launch - - FirebaseCrashlytics.getInstance().log("MOVING ${cache.canonicalPath} --> ${download.canonicalPath}") - - cache.copyRecursively(download, true) { file, err -> - FirebaseCrashlytics.getInstance().log("MOVING ERROR ${file.canonicalPath} ${err.message}") - OnErrorAction.SKIP - } - FirebaseCrashlytics.getInstance().log("MOVED ${cache.canonicalPath}") - - FirebaseCrashlytics.getInstance().log("DELETING ${cache.canonicalPath}") - cache.deleteRecursively() - FirebaseCrashlytics.getInstance().log("DELETED ${cache.canonicalPath}") - } - } - - fun isDownloading(galleryID: Int) = getCachedMetadata(galleryID)?.isDownloading == true - - fun setDownloading(galleryID: Int, isDownloading: Boolean) { - setCachedMetadata(galleryID, Metadata(getCachedMetadata(galleryID), isDownloading = isDownloading)) - } - -} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt b/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt deleted file mode 100644 index edd22251..00000000 --- a/app/src/main/java/xyz/quaver/pupil/util/download/DownloadWorker.kt +++ /dev/null @@ -1,389 +0,0 @@ -/* - * 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 . - */ - -package xyz.quaver.pupil.util.download - -import android.app.PendingIntent -import android.content.Context -import android.content.ContextWrapper -import android.content.Intent -import android.content.SharedPreferences -import android.util.Log -import android.util.SparseArray -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import androidx.core.app.TaskStackBuilder -import androidx.preference.PreferenceManager -import com.google.firebase.crashlytics.FirebaseCrashlytics -import kotlinx.coroutines.* -import okhttp3.* -import okio.* -import xyz.quaver.Code -import xyz.quaver.hitomi.Reader -import xyz.quaver.hitomi.getReferer -import xyz.quaver.hitomi.imageUrlFromImage -import xyz.quaver.hiyobi.createImgList -import xyz.quaver.pupil.R -import xyz.quaver.pupil.client -import xyz.quaver.pupil.interceptors -import xyz.quaver.pupil.ui.ReaderActivity -import java.io.File -import java.io.IOException -import java.util.concurrent.LinkedBlockingQueue - -@Suppress("DEPRECATION") -@Deprecated("Use DownloadService instead") -@OptIn(ExperimentalCoroutinesApi::class) -class DownloadWorker private constructor(context: Context) : ContextWrapper(context) { - - private val preferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) - - //region ProgressListener - @Suppress("UNCHECKED_CAST") - private val progressListener = object: ProgressListener { - override fun update(tag: Any?, bytesRead: Long, contentLength: Long, done: Boolean) { - val (galleryID, index) = (tag as? Pair) ?: return - - if (!done && progress[galleryID]?.get(index)?.isFinite() == true) - progress[galleryID]?.set(index, bytesRead * 100F / contentLength) - } - } - - interface ProgressListener { - fun update(tag: Any?, bytesRead : Long, contentLength: Long, done: Boolean) - } - - class ProgressResponseBody( - val tag: Any?, - val responseBody: ResponseBody, - val progressListener : ProgressListener - ) : ResponseBody() { - private var bufferedSource : BufferedSource? = null - - override fun contentLength() = responseBody.contentLength() - override fun contentType() = responseBody.contentType() - - override fun source(): BufferedSource { - if (bufferedSource == null) - bufferedSource = Okio.buffer(source(responseBody.source())) - - return bufferedSource!! - } - - private fun source(source: Source) = object: ForwardingSource(source) { - var totalBytesRead = 0L - - override fun read(sink: Buffer, byteCount: Long): Long { - val bytesRead = super.read(sink, byteCount) - - totalBytesRead += if (bytesRead == -1L) 0L else bytesRead - progressListener.update(tag, totalBytesRead, responseBody.contentLength(), bytesRead == -1L) - - return bytesRead - } - - } - } - - init { - interceptors[Pair::class] = { chain -> - val request = chain.request() - var response = chain.proceed(request) - - var retry = 5 - while (!response.isSuccessful && retry > 0) { - response = chain.proceed(request) - retry-- - } - - response.newBuilder() - .body(response.body()?.let { - ProgressResponseBody(request.tag(), it, progressListener) - }).build() - } - } - //endregion - - //region Singleton - companion object { - - @Volatile private var instance: DownloadWorker? = null - - fun getInstance(context: Context) = - instance ?: synchronized(this) { - instance ?: DownloadWorker(context).also { instance = it } - } - } - //endregion - - val notificationManager = NotificationManagerCompat.from(context) - - val queue = LinkedBlockingQueue() - - /* - * KEY - * primary galleryID - * secondary index - * PRIMARY VALUE - * MutableList -> Download in progress - * null -> Loading / Gallery doesn't exist - * SECONDARY VALUE - * 0 <= value < 100 -> Download in progress - * Float.POSITIVE_INFINITY -> Download completed - */ - val progress = SparseArray?>() - val notification = SparseArray() - - private val loop = loop() - private val worker = SparseArray() - - fun stop() { - queue.clear() - - loop.cancel() - for (i in 0 until worker.size()) { - val galleryID = worker.keyAt(i) - - Cache(this@DownloadWorker).setDownloading(galleryID, false) - worker[galleryID]?.cancel() - } - - client.dispatcher().queuedCalls().filter { - it.request().tag() is Pair<*, *> - }.forEach { - it.cancel() - } - client.dispatcher().runningCalls().filter { - it.request().tag() is Pair<*, *> - }.forEach { - it.cancel() - } - - progress.clear() - notification.clear() - notificationManager.cancelAll() - } - - fun cancel(galleryID: Int) { - queue.remove(galleryID) - worker[galleryID]?.cancel() - - client.dispatcher().queuedCalls().filter { - ((it.request().tag() as Pair<*, *>).first as Int) == galleryID - }.forEach { - it.cancel() - } - client.dispatcher().runningCalls().filter { - ((it.request().tag() as Pair<*, *>).first as Int) == galleryID - }.forEach { - it.cancel() - } - - progress.remove(galleryID) - notification.remove(galleryID) - notificationManager.cancel(galleryID) - } - - fun isCompleted(galleryID: Int) = progress[galleryID]?.all { it.isInfinite() } == true - - private fun queueDownload(galleryID: Int, reader: Reader, index: Int, callback: Callback) { - val lowQuality = preferences.getBoolean("low_quality", false) - - val request = Request.Builder().apply { - when (reader.code) { - Code.HITOMI -> { - url( - imageUrlFromImage( - galleryID, - reader.galleryInfo.files[index], - !lowQuality - ) - ) - addHeader("Referer", getReferer(galleryID)) - } - Code.HIYOBI -> { - url(createImgList(galleryID, reader, lowQuality)[index].path) - } - else -> { - //shouldn't be called anyway - } - } - tag(galleryID to index) - }.build() - - client.newCall(request).enqueue(callback) - } - - private fun download(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch { - val reader = Cache(this@DownloadWorker).getReader(galleryID) - - //gallery doesn't exist - if (reader == null) { - progress.put(galleryID, null) - - Cache(this@DownloadWorker).setDownloading(galleryID, false) - return@launch - } - - val cache = Cache(this@DownloadWorker).getImages(galleryID) - - progress.put(galleryID, reader.galleryInfo.files.indices.map { index -> - if (cache?.firstOrNull { it?.nameWithoutExtension?.toIntOrNull() == index } != null) - Float.POSITIVE_INFINITY - else - 0F - }.toMutableList()) - - if (notification[galleryID] == null) - initNotification(galleryID) - - notification[galleryID]?.setContentTitle(reader.galleryInfo.title) - notify(galleryID) - - if (isCompleted(galleryID)) { - with(Cache(this@DownloadWorker)) { - if (isDownloading(galleryID)) { - moveToDownload(galleryID) - setDownloading(galleryID, false) - } - } - - return@launch - } - - for (i in reader.galleryInfo.files.indices) { - val callback = object : Callback { - override fun onFailure(call: Call, e: IOException) { - if (e.message?.contains("cancel", true) != false) - return - - cancel(galleryID) - queue.add(galleryID) - } - - override fun onResponse(call: Call, response: Response) { - if (response.code() != 200) { - response.close() - onFailure(call, IOException()) - return - } - - val ext = call.request().url().encodedPath().split('.').last() - - try { - response.body()!!.use { - Cache(this@DownloadWorker).putImage(galleryID, i, ext, it.byteStream()) - } - progress[galleryID]?.set(i, Float.POSITIVE_INFINITY) - - notify(galleryID) - - CoroutineScope(Dispatchers.IO).launch { - if (isCompleted(galleryID)) { - with(Cache(this@DownloadWorker)) { - if (isDownloading(galleryID)) { - moveToDownload(galleryID) - setDownloading(galleryID, false) - } - } - } - } - } catch (e: Exception) { - FirebaseCrashlytics.getInstance().apply { - log("FAIL ON OK ${call.request().tag()} (${e.message})") - setCustomKey("POS", "FAIL ON OK") - recordException(e) - } - - File(Cache(this@DownloadWorker).getCachedGallery(galleryID), "%05d.$ext".format(i)).delete() - - cancel(galleryID) - queue.add(galleryID) - } - } - } - - if (progress[galleryID]?.get(i)?.isFinite() == true) - queueDownload(galleryID, reader, i, callback) - } - } - - private fun notify(galleryID: Int) { - val max = progress[galleryID]?.size ?: 0 - val progress = progress[galleryID]?.count { it.isInfinite() } ?: 0 - - if (isCompleted(galleryID)) { - notification[galleryID] - ?.setContentText(getString(R.string.reader_notification_complete)) - ?.setSmallIcon(android.R.drawable.stat_sys_download_done) - ?.setProgress(0, 0, false) - ?.setOngoing(false) - - notificationManager.cancel(galleryID) - } else - notification[galleryID] - ?.setProgress(max, progress, false) - ?.setContentText("$progress/$max") - - if (Cache(this).isDownloading(galleryID) && notification[galleryID] != null) - notification[galleryID]?.let { notificationManager.notify(galleryID, it.build()) } - else - notificationManager.cancel(galleryID) - } - - private fun initNotification(galleryID: Int) { - val intent = Intent(this, ReaderActivity::class.java).apply { - putExtra("galleryID", galleryID) - } - val pendingIntent = TaskStackBuilder.create(this).run { - addNextIntentWithParentStack(intent) - getPendingIntent(galleryID, PendingIntent.FLAG_UPDATE_CURRENT) - } - - notification.put(galleryID, NotificationCompat.Builder(this, "download").apply { - setContentTitle(getString(R.string.reader_loading)) - setContentText(getString(R.string.reader_notification_text)) - setSmallIcon(android.R.drawable.stat_sys_download) // had to use this because old android doesn't support VectorDrawable on Notification :P - setContentIntent(pendingIntent) - setProgress(0, 0, true) - setOngoing(true) - }) - } - - private fun loop() = CoroutineScope(Dispatchers.Default).launch { - while (true) { - if (queue.isEmpty()) - continue - - val galleryID = queue.peek() ?: continue - - if (progress.indexOfKey(galleryID) >= 0) // Gallery already downloading! - cancel(galleryID) - - if (notification[galleryID] == null) - initNotification(galleryID) - - if (Cache(this@DownloadWorker).isDownloading(galleryID)) - notification[galleryID]?.let { notificationManager.notify(galleryID, it.build()) } - - worker.put(galleryID, download(galleryID)) - queue.poll() - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/download/Metadata.kt b/app/src/main/java/xyz/quaver/pupil/util/download/Metadata.kt deleted file mode 100644 index 8dead901..00000000 --- a/app/src/main/java/xyz/quaver/pupil/util/download/Metadata.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 . - */ - -package xyz.quaver.pupil.util.download - -import kotlinx.serialization.Serializable -import xyz.quaver.hitomi.GalleryBlock -import xyz.quaver.hitomi.Reader - -@Suppress("DEPRECATION") -@Deprecated("Use downloader.Cache.Metadata instead") -@Serializable -data class Metadata( - var thumbnail: String? = null, - var galleryBlock: GalleryBlock? = null, - var reader: Reader? = null, - var isDownloading: Boolean? = null -) { - constructor( - metadata: Metadata?, - thumbnail: String? = null, - galleryBlock: GalleryBlock? = null, - readers: Reader? = null, - isDownloading: Boolean? = null - ) : this( - thumbnail ?: metadata?.thumbnail, - galleryBlock ?: metadata?.galleryBlock, - readers ?: metadata?.reader, - isDownloading ?: metadata?.isDownloading - ) -} \ No newline at end of file diff --git a/app/src/main/res/layout/item_galleryblock.xml b/app/src/main/res/layout/item_galleryblock.xml index efab2973..678208f9 100644 --- a/app/src/main/res/layout/item_galleryblock.xml +++ b/app/src/main/res/layout/item_galleryblock.xml @@ -49,7 +49,7 @@ android:background="@android:color/holo_blue_dark" android:textColor="@android:color/white" android:text="@string/main_download" - android:foreground="?attr/selectableItemBackground" + android:foreground="?android:attr/selectableItemBackground" android:focusable="true" android:clickable="true" tools:ignore="UnusedAttribute" /> @@ -64,7 +64,7 @@ android:background="@android:color/holo_red_dark" android:textColor="@android:color/white" android:text="@string/main_delete" - android:foreground="?attr/selectableItemBackground" + android:foreground="?android:attr/selectableItemBackground" android:focusable="true" android:clickable="true" tools:ignore="UnusedAttribute" /> @@ -74,24 +74,38 @@ + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:focusable="true" + android:clickable="true"> - + app:layout_constraintTop_toTopOf="parent"> - + + + + + + app:layout_constraintTop_toBottomOf="@id/galleryblock_progressbar_layout"/>