diff --git a/app/build.gradle b/app/build.gradle index cb27808e..e68026de 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,7 +38,7 @@ android { minSdkVersion 16 targetSdkVersion 30 versionCode 61 - versionName "5.1.2-hotfix2" + versionName "5.1.2-hotfix3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } @@ -84,8 +84,8 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0-RC2" implementation "androidx.appcompat:appcompat:1.2.0" - implementation "androidx.activity:activity-ktx:1.2.0-alpha08" - implementation "androidx.fragment:fragment-ktx:1.3.0-alpha08" + implementation "androidx.activity:activity-ktx:1.2.0-beta01" + implementation "androidx.fragment:fragment-ktx:1.3.0-beta01" implementation "androidx.preference:preference:1.1.1" implementation "androidx.constraintlayout:constraintlayout:2.0.1" implementation "androidx.gridlayout:gridlayout:1.0.0" @@ -94,12 +94,12 @@ dependencies { implementation "com.daimajia.swipelayout:library:1.2.0@aar" - implementation "com.google.android.material:material:1.3.0-alpha02" + implementation "com.google.android.material:material:1.3.0-alpha03" implementation "com.google.firebase:firebase-core:17.5.0" implementation "com.google.firebase:firebase-analytics:17.5.0" - implementation "com.google.firebase:firebase-crashlytics:17.2.1" - implementation "com.google.firebase:firebase-perf:19.0.8" + implementation "com.google.firebase:firebase-crashlytics:17.2.2" + implementation "com.google.firebase:firebase-perf:19.0.9" implementation "com.google.android.gms:play-services-oss-licenses:17.0.0" implementation "com.google.android.gms:play-services-mlkit-face-detection:16.1.1" @@ -126,7 +126,7 @@ dependencies { implementation "ru.noties.markwon:core:3.1.0" implementation "xyz.quaver:libpupil:1.7.2" - implementation "xyz.quaver:documentfilex:0.3" + implementation "xyz.quaver:documentfilex:0.3.1" implementation "xyz.quaver:floatingsearchview:1.0.7" testImplementation "junit:junit:4.13" diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 45e5bd75..9407c54b 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -12,7 +12,7 @@ "filters": [], "properties": [], "versionCode": 61, - "versionName": "5.1.2-hotfix2", + "versionName": "5.1.2-hotfix3", "enabled": true, "outputFile": "app-release.apk" } 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 f6bc889f..41b06e1a 100644 --- a/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt +++ b/app/src/main/java/xyz/quaver/pupil/services/DownloadService.kt @@ -37,10 +37,7 @@ import okhttp3.Callback import okhttp3.Response import okhttp3.ResponseBody import okio.* -import xyz.quaver.pupil.PupilInterceptor -import xyz.quaver.pupil.R -import xyz.quaver.pupil.client -import xyz.quaver.pupil.interceptors +import xyz.quaver.pupil.* import xyz.quaver.pupil.ui.ReaderActivity import xyz.quaver.pupil.util.cleanCache import xyz.quaver.pupil.util.downloader.Cache @@ -290,7 +287,7 @@ class DownloadService : Service() { fun delete(galleryID: Int, startId: Int? = null) = CoroutineScope(Dispatchers.IO).launch { cancel(galleryID) DownloadManager.getInstance(this@DownloadService).deleteDownloadFolder(galleryID) - Cache.delete(galleryID) + Cache.delete(this@DownloadService, galleryID) startId?.let { stopSelf(it) } } @@ -314,6 +311,8 @@ class DownloadService : Service() { return@launch } + histories.add(galleryID) + progress[galleryID] = MutableList(reader.galleryInfo.files.size) { 0F } cache.metadata.imageList?.let { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index dd73fba2..9539d8ee 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -292,8 +292,6 @@ class ReaderActivity : BaseActivity() { return@launch } - histories.add(galleryID) - reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0 reader_download_progressbar.progress = downloader.progress[galleryID]?.count { it.isInfinite() } ?: 0 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 8c869a36..76384193 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 @@ -77,7 +77,6 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog( context.startActivity(Intent(context, ReaderActivity::class.java).apply { putExtra("galleryID", galleryID) }) - histories.add(galleryID) } } @@ -238,7 +237,6 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog( context.startActivity(Intent(context, ReaderActivity::class.java).apply { putExtra("galleryID", galleries[position]) }) - histories.add(galleries[position]) } onItemLongClickListener = { _, position, _ -> GalleryDialog(context, galleries[position]).apply { diff --git a/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt b/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt index 5c4aa5f1..60d8ef25 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/downloader/Cache.kt @@ -24,6 +24,8 @@ import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString @@ -37,6 +39,7 @@ import xyz.quaver.io.FileX import xyz.quaver.io.util.* import xyz.quaver.pupil.client import xyz.quaver.pupil.util.Preferences +import java.io.File import java.io.IOException import java.util.concurrent.ConcurrentHashMap @@ -60,8 +63,8 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW } @Synchronized - fun delete(galleryID: Int) { - instances[galleryID]?.cacheFolder?.deleteRecursively() + fun delete(context: Context, galleryID: Int) { + File(context.cacheDir, "imageCache/$galleryID").deleteRecursively() instances.remove(galleryID) } } @@ -142,7 +145,12 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW client.newCall(request).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.bytes() } }.getOrNull()?.let { thumbnail -> kotlin.runCatching { - cacheFolder.getChild(".thumbnail").also { it.writeBytes(thumbnail) } + cacheFolder.getChild(".thumbnail").also { + if (!it.exists()) + it.createNewFile() + + it.writeBytes(thumbnail) + } }.getOrNull()?.uri } } } ?: Uri.EMPTY @@ -198,56 +206,64 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW setMetadata { metadata -> metadata.imageList!![index] = fileName } } + private val lock = ConcurrentHashMap() @Suppress("BlockingMethodInNonBlockingContext") fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch { val downloadFolder = downloadFolder ?: return@launch - val cacheMetadata = cacheFolder.getChild(".metadata") - val downloadMetadata = downloadFolder.getChild(".metadata") - - if (downloadMetadata.exists() || !cacheMetadata.exists()) + if (lock[galleryID]?.isLocked == true) return@launch - if (cacheMetadata.exists()) { - kotlin.runCatching { - downloadMetadata.createNewFile() - downloadMetadata.writeText(Json.encodeToString(metadata)) + (lock[galleryID] ?: Mutex().also { lock[galleryID] = it }).withLock { + val cacheMetadata = cacheFolder.getChild(".metadata") + val downloadMetadata = downloadFolder.getChild(".metadata") - cacheMetadata.delete() + if (!cacheMetadata.exists()) + return@launch + + if (cacheMetadata.exists()) { + kotlin.runCatching { + if (!downloadMetadata.exists()) + downloadMetadata.createNewFile() + + downloadMetadata.writeText(Json.encodeToString(metadata)) + } } - } - val cacheThumbnail = cacheFolder.getChild(".thumbnail") - val downloadThumbnail = downloadFolder.getChild(".thumbnail") + val cacheThumbnail = cacheFolder.getChild(".thumbnail") + val downloadThumbnail = downloadFolder.getChild(".thumbnail") - if (cacheThumbnail.exists() && !downloadThumbnail.exists()) { - kotlin.runCatching { - if (!downloadThumbnail.exists()) - downloadThumbnail.createNewFile() + if (cacheThumbnail.exists()) { + kotlin.runCatching { + if (!downloadThumbnail.exists()) + downloadThumbnail.createNewFile() - downloadThumbnail.outputStream()?.use { target -> cacheThumbnail.inputStream()?.use { source -> - source.copyTo(target) - } } - cacheThumbnail.delete() + downloadThumbnail.outputStream()?.use { target -> target.channel.truncate(0L); cacheThumbnail.inputStream()?.use { source -> + source.copyTo(target) + } } + cacheThumbnail.delete() + } } - } - metadata.imageList?.forEach { imageName -> - imageName ?: return@forEach - val target = downloadFolder.getChild(imageName) - val source = cacheFolder.getChild(imageName) + metadata.imageList?.forEach { imageName -> + imageName ?: return@forEach + val target = downloadFolder.getChild(imageName) + val source = cacheFolder.getChild(imageName) - if (!source.exists() || target.exists()) - return@forEach + if (!source.exists()) + return@forEach - kotlin.runCatching { - if (!target.exists()) - target.createNewFile() + kotlin.runCatching { + if (!target.exists()) + target.createNewFile() - target.outputStream()?.use { target -> source.inputStream()?.use { source -> - source.copyTo(target) - } } + target.outputStream()?.use { target -> target.channel.truncate(0L); source.inputStream()?.use { source -> + source.copyTo(target) + } } + } } + + cacheFolder.deleteRecursively() } } } \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/file.kt b/app/src/main/java/xyz/quaver/pupil/util/file.kt index 52022248..c7aec1e8 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/file.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/file.kt @@ -37,14 +37,6 @@ fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch { val cacheFolder = File(context.cacheDir, "imageCache") val downloadManager = DownloadManager.getInstance(context) - DownloadManager.getInstance(context).downloadFolderMap.keys.forEach { - val folder = File(cacheFolder, it.toString()) - - if (!downloadManager.isDownloading(it) && folder.exists()) { - Cache.delete(it) - } - } - val limit = (Preferences.get("cache_limit").toLongOrNull() ?: 0L)*1024*1024*1024 if (limit == 0L) return@withLock @@ -66,7 +58,7 @@ fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch { (histories.firstOrNull { caches.contains(it.toString()) && !downloadManager.isDownloading(it) } ?: return@withLock).let { - Cache.delete(it) + Cache.delete(context, it) } } } diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt index c6a711b7..3c8d3894 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/update.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt @@ -313,7 +313,7 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() { ) synchronized(Cache) { - Cache.delete(galleryID) + Cache.delete(this@migrate, galleryID) } downloadFolderMap[galleryID] = folder.name