Merge branch 'dev' into master
This commit is contained in:
@@ -38,7 +38,7 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 61
|
versionCode 61
|
||||||
versionName "5.1.2-hotfix2"
|
versionName "5.1.2-hotfix3"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
@@ -84,8 +84,8 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0-RC2"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0-RC2"
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||||
implementation "androidx.activity:activity-ktx:1.2.0-alpha08"
|
implementation "androidx.activity:activity-ktx:1.2.0-beta01"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.3.0-alpha08"
|
implementation "androidx.fragment:fragment-ktx:1.3.0-beta01"
|
||||||
implementation "androidx.preference:preference:1.1.1"
|
implementation "androidx.preference:preference:1.1.1"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.0.1"
|
implementation "androidx.constraintlayout:constraintlayout:2.0.1"
|
||||||
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
||||||
@@ -94,12 +94,12 @@ dependencies {
|
|||||||
|
|
||||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||||
|
|
||||||
implementation "com.google.android.material:material:1.3.0-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-core:17.5.0"
|
||||||
implementation "com.google.firebase:firebase-analytics: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-crashlytics:17.2.2"
|
||||||
implementation "com.google.firebase:firebase-perf:19.0.8"
|
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-oss-licenses:17.0.0"
|
||||||
implementation "com.google.android.gms:play-services-mlkit-face-detection:16.1.1"
|
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 "ru.noties.markwon:core:3.1.0"
|
||||||
|
|
||||||
implementation "xyz.quaver:libpupil:1.7.2"
|
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"
|
implementation "xyz.quaver:floatingsearchview:1.0.7"
|
||||||
|
|
||||||
testImplementation "junit:junit:4.13"
|
testImplementation "junit:junit:4.13"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"filters": [],
|
"filters": [],
|
||||||
"properties": [],
|
"properties": [],
|
||||||
"versionCode": 61,
|
"versionCode": 61,
|
||||||
"versionName": "5.1.2-hotfix2",
|
"versionName": "5.1.2-hotfix3",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"outputFile": "app-release.apk"
|
"outputFile": "app-release.apk"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,10 +37,7 @@ import okhttp3.Callback
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import okio.*
|
import okio.*
|
||||||
import xyz.quaver.pupil.PupilInterceptor
|
import xyz.quaver.pupil.*
|
||||||
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.ui.ReaderActivity
|
||||||
import xyz.quaver.pupil.util.cleanCache
|
import xyz.quaver.pupil.util.cleanCache
|
||||||
import xyz.quaver.pupil.util.downloader.Cache
|
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 {
|
fun delete(galleryID: Int, startId: Int? = null) = CoroutineScope(Dispatchers.IO).launch {
|
||||||
cancel(galleryID)
|
cancel(galleryID)
|
||||||
DownloadManager.getInstance(this@DownloadService).deleteDownloadFolder(galleryID)
|
DownloadManager.getInstance(this@DownloadService).deleteDownloadFolder(galleryID)
|
||||||
Cache.delete(galleryID)
|
Cache.delete(this@DownloadService, galleryID)
|
||||||
|
|
||||||
startId?.let { stopSelf(it) }
|
startId?.let { stopSelf(it) }
|
||||||
}
|
}
|
||||||
@@ -314,6 +311,8 @@ class DownloadService : Service() {
|
|||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
histories.add(galleryID)
|
||||||
|
|
||||||
progress[galleryID] = MutableList(reader.galleryInfo.files.size) { 0F }
|
progress[galleryID] = MutableList(reader.galleryInfo.files.size) { 0F }
|
||||||
|
|
||||||
cache.metadata.imageList?.let {
|
cache.metadata.imageList?.let {
|
||||||
|
|||||||
@@ -292,8 +292,6 @@ class ReaderActivity : BaseActivity() {
|
|||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
histories.add(galleryID)
|
|
||||||
|
|
||||||
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
reader_download_progressbar.max = reader_recyclerview.adapter?.itemCount ?: 0
|
||||||
reader_download_progressbar.progress =
|
reader_download_progressbar.progress =
|
||||||
downloader.progress[galleryID]?.count { it.isInfinite() } ?: 0
|
downloader.progress[galleryID]?.count { it.isInfinite() } ?: 0
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ class GalleryDialog(context: Context, private val galleryID: Int) : AlertDialog(
|
|||||||
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||||
putExtra("galleryID", galleryID)
|
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 {
|
context.startActivity(Intent(context, ReaderActivity::class.java).apply {
|
||||||
putExtra("galleryID", galleries[position])
|
putExtra("galleryID", galleries[position])
|
||||||
})
|
})
|
||||||
histories.add(galleries[position])
|
|
||||||
}
|
}
|
||||||
onItemLongClickListener = { _, position, _ ->
|
onItemLongClickListener = { _, position, _ ->
|
||||||
GalleryDialog(context, galleries[position]).apply {
|
GalleryDialog(context, galleries[position]).apply {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import android.net.Uri
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
@@ -37,6 +39,7 @@ import xyz.quaver.io.FileX
|
|||||||
import xyz.quaver.io.util.*
|
import xyz.quaver.io.util.*
|
||||||
import xyz.quaver.pupil.client
|
import xyz.quaver.pupil.client
|
||||||
import xyz.quaver.pupil.util.Preferences
|
import xyz.quaver.pupil.util.Preferences
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
@@ -60,8 +63,8 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun delete(galleryID: Int) {
|
fun delete(context: Context, galleryID: Int) {
|
||||||
instances[galleryID]?.cacheFolder?.deleteRecursively()
|
File(context.cacheDir, "imageCache/$galleryID").deleteRecursively()
|
||||||
instances.remove(galleryID)
|
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() }
|
client.newCall(request).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.bytes() }
|
||||||
}.getOrNull()?.let { thumbnail -> kotlin.runCatching {
|
}.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 }
|
}.getOrNull()?.uri }
|
||||||
} } ?: Uri.EMPTY
|
} } ?: Uri.EMPTY
|
||||||
|
|
||||||
@@ -198,56 +206,64 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
|
|||||||
setMetadata { metadata -> metadata.imageList!![index] = fileName }
|
setMetadata { metadata -> metadata.imageList!![index] = fileName }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val lock = ConcurrentHashMap<Int, Mutex>()
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch {
|
fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch {
|
||||||
val downloadFolder = downloadFolder ?: return@launch
|
val downloadFolder = downloadFolder ?: return@launch
|
||||||
|
|
||||||
val cacheMetadata = cacheFolder.getChild(".metadata")
|
if (lock[galleryID]?.isLocked == true)
|
||||||
val downloadMetadata = downloadFolder.getChild(".metadata")
|
|
||||||
|
|
||||||
if (downloadMetadata.exists() || !cacheMetadata.exists())
|
|
||||||
return@launch
|
return@launch
|
||||||
|
|
||||||
if (cacheMetadata.exists()) {
|
(lock[galleryID] ?: Mutex().also { lock[galleryID] = it }).withLock {
|
||||||
kotlin.runCatching {
|
val cacheMetadata = cacheFolder.getChild(".metadata")
|
||||||
downloadMetadata.createNewFile()
|
val downloadMetadata = downloadFolder.getChild(".metadata")
|
||||||
downloadMetadata.writeText(Json.encodeToString(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 cacheThumbnail = cacheFolder.getChild(".thumbnail")
|
||||||
val downloadThumbnail = downloadFolder.getChild(".thumbnail")
|
val downloadThumbnail = downloadFolder.getChild(".thumbnail")
|
||||||
|
|
||||||
if (cacheThumbnail.exists() && !downloadThumbnail.exists()) {
|
if (cacheThumbnail.exists()) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
if (!downloadThumbnail.exists())
|
if (!downloadThumbnail.exists())
|
||||||
downloadThumbnail.createNewFile()
|
downloadThumbnail.createNewFile()
|
||||||
|
|
||||||
downloadThumbnail.outputStream()?.use { target -> cacheThumbnail.inputStream()?.use { source ->
|
downloadThumbnail.outputStream()?.use { target -> target.channel.truncate(0L); cacheThumbnail.inputStream()?.use { source ->
|
||||||
source.copyTo(target)
|
source.copyTo(target)
|
||||||
} }
|
} }
|
||||||
cacheThumbnail.delete()
|
cacheThumbnail.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
metadata.imageList?.forEach { imageName ->
|
metadata.imageList?.forEach { imageName ->
|
||||||
imageName ?: return@forEach
|
imageName ?: return@forEach
|
||||||
val target = downloadFolder.getChild(imageName)
|
val target = downloadFolder.getChild(imageName)
|
||||||
val source = cacheFolder.getChild(imageName)
|
val source = cacheFolder.getChild(imageName)
|
||||||
|
|
||||||
if (!source.exists() || target.exists())
|
if (!source.exists())
|
||||||
return@forEach
|
return@forEach
|
||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
if (!target.exists())
|
if (!target.exists())
|
||||||
target.createNewFile()
|
target.createNewFile()
|
||||||
|
|
||||||
target.outputStream()?.use { target -> source.inputStream()?.use { source ->
|
target.outputStream()?.use { target -> target.channel.truncate(0L); source.inputStream()?.use { source ->
|
||||||
source.copyTo(target)
|
source.copyTo(target)
|
||||||
} }
|
} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheFolder.deleteRecursively()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,14 +37,6 @@ fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch {
|
|||||||
val cacheFolder = File(context.cacheDir, "imageCache")
|
val cacheFolder = File(context.cacheDir, "imageCache")
|
||||||
val downloadManager = DownloadManager.getInstance(context)
|
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<String>("cache_limit").toLongOrNull() ?: 0L)*1024*1024*1024
|
val limit = (Preferences.get<String>("cache_limit").toLongOrNull() ?: 0L)*1024*1024*1024
|
||||||
|
|
||||||
if (limit == 0L) return@withLock
|
if (limit == 0L) return@withLock
|
||||||
@@ -66,7 +58,7 @@ fun cleanCache(context: Context) = CoroutineScope(Dispatchers.IO).launch {
|
|||||||
(histories.firstOrNull {
|
(histories.firstOrNull {
|
||||||
caches.contains(it.toString()) && !downloadManager.isDownloading(it)
|
caches.contains(it.toString()) && !downloadManager.isDownloading(it)
|
||||||
} ?: return@withLock).let {
|
} ?: return@withLock).let {
|
||||||
Cache.delete(it)
|
Cache.delete(context, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
synchronized(Cache) {
|
synchronized(Cache) {
|
||||||
Cache.delete(galleryID)
|
Cache.delete(this@migrate, galleryID)
|
||||||
}
|
}
|
||||||
downloadFolderMap[galleryID] = folder.name
|
downloadFolderMap[galleryID] = folder.name
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user