Compare commits

...

5 Commits

Author SHA1 Message Date
tom5079
f408a91176 Bug fix 2020-09-07 10:00:10 +09:00
tom5079
6f6956ce27 Fixed DownloadLocationDialogFragment keep showing up when any button is clicked 2020-09-06 17:19:18 +09:00
tom5079
4ecad8eccc Fixed migration 2020-09-05 18:31:53 +09:00
tom5079
486fbe46a0 Fixed migration 2020-09-05 18:11:20 +09:00
tom5079
1ddb636dd0 Fixed migration 2020-09-05 18:00:15 +09:00
9 changed files with 92 additions and 39 deletions

View File

@@ -20,7 +20,7 @@ android {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 57 versionCode 57
versionName "5.0-beta2" versionName "5.0-beta4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }

View File

@@ -12,7 +12,7 @@
"filters": [], "filters": [],
"properties": [], "properties": [],
"versionCode": 57, "versionCode": 57,
"versionName": "5.0-beta2", "versionName": "5.0-beta4",
"enabled": true, "enabled": true,
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }

View File

@@ -35,7 +35,11 @@ import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.Animatable2Compat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.bumptech.glide.RequestManager import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.daimajia.swipe.SwipeLayout import com.daimajia.swipe.SwipeLayout
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
import com.daimajia.swipe.interfaces.SwipeAdapterInterface import com.daimajia.swipe.interfaces.SwipeAdapterInterface
@@ -46,6 +50,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import xyz.quaver.hitomi.getReader import xyz.quaver.hitomi.getReader
import xyz.quaver.io.util.getChild
import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.BuildConfig
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.favorites import xyz.quaver.pupil.favorites
@@ -88,11 +93,10 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
val imageList = cache.metadata.imageList!! val imageList = cache.metadata.imageList!!
progress = imageList.filterNotNull().size progress = imageList.filterNotNull().size
max = imageList.size
if (visibility == View.GONE) { if (visibility == View.GONE)
visibility = View.VISIBLE visibility = View.VISIBLE
max = imageList.size
}
if (progress == max) { if (progress == max) {
val downloadManager = DownloadManager.getInstance(context) val downloadManager = DownloadManager.getInstance(context)
@@ -158,6 +162,28 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
.skipMemoryCache(true) .skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.error(R.drawable.image_broken_variant) .error(R.drawable.image_broken_variant)
.listener(object: RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
Cache.getInstance(context, galleryID).let {
it.cacheFolder.getChild(".thumbnail").let { if (it.exists()) it.delete() }
it.downloadFolder?.getChild(".thumbnail")?.let { if (it.exists()) it.delete() }
}
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean = false
})
.apply { .apply {
if (BuildConfig.CENSOR) if (BuildConfig.CENSOR)
override(5, 8) override(5, 8)

View File

@@ -328,9 +328,11 @@ class DownloadService : Service() {
} }
} }
reader.requestBuilders.filterIndexed { index, _ -> progress[galleryID]?.get(index)?.isInfinite() != true }.forEachIndexed { index, it -> reader.requestBuilders.forEachIndexed { index, it ->
val request = it.tag(Tag(galleryID, index, startId)).build() if (progress[galleryID]?.get(index)?.isInfinite() != true) {
client.newCall(request).enqueue(callback) val request = it.tag(Tag(galleryID, index, startId)).build()
client.newCall(request).enqueue(callback)
}
} }
queued.forEach { download(it) } queued.forEach { download(it) }

View File

@@ -1034,7 +1034,7 @@ class MainActivity : AppCompatActivity() {
val downloads = DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList() val downloads = DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList()
when { when {
query.isEmpty() -> downloads.also { query.isEmpty() -> downloads.reversed().also {
totalItems = it.size totalItems = it.size
} }
else -> { else -> {

View File

@@ -122,6 +122,9 @@ class DownloadLocationDialogFragment : DialogFragment() {
.setTitle(R.string.settings_download_folder) .setTitle(R.string.settings_download_folder)
.setView(build()) .setView(build())
.setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ -> .setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ ->
if (Preferences["download_folder", ""].isEmpty())
Preferences["download_folder"] = context?.getExternalFilesDir(null)?.canonicalPath ?: ""
DownloadManager.getInstance(requireContext()).migrate() DownloadManager.getInstance(requireContext()).migrate()
} }

View File

@@ -82,7 +82,7 @@ class MirrorDialog(context: Context) : AlertDialog(context) {
} }
onItemMoved = { onItemMoved = {
Preferences["mirrors", it.joinToString(">")] Preferences["mirrors"] = it.joinToString(">")
} }
} }
} }

View File

@@ -60,6 +60,7 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
instances[galleryID] ?: Cache(context, galleryID).also { instances.put(galleryID, it) } instances[galleryID] ?: Cache(context, galleryID).also { instances.put(galleryID, it) }
} }
@Synchronized
fun delete(galleryID: Int) { fun delete(galleryID: Int) {
instances[galleryID]?.cacheFolder?.deleteRecursively() instances[galleryID]?.cacheFolder?.deleteRecursively()
instances.delete(galleryID) instances.delete(galleryID)
@@ -86,11 +87,11 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
} }
fun findFile(fileName: String): FileX? = fun findFile(fileName: String): FileX? =
cacheFolder.getChild(fileName).let { downloadFolder?.let { downloadFolder -> downloadFolder.getChild(fileName).let {
if (it.exists()) it else null if (it.exists()) it else null
} ?: downloadFolder?.let { downloadFolder -> downloadFolder.getChild(fileName).let { } } ?: cacheFolder.getChild(fileName).let {
if (it.exists()) it else null if (it.exists()) it else null
} } }
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
fun setMetadata(change: (Metadata) -> Unit) { fun setMetadata(change: (Metadata) -> Unit) {

View File

@@ -24,6 +24,7 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
@@ -32,11 +33,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import okhttp3.Call import okhttp3.Call
import okhttp3.Callback import okhttp3.Callback
@@ -45,6 +48,8 @@ import okhttp3.Response
import ru.noties.markwon.Markwon import ru.noties.markwon.Markwon
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getGalleryBlock
import xyz.quaver.hitomi.getReader
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.getChild
import xyz.quaver.io.util.* import xyz.quaver.io.util.*
@@ -53,6 +58,7 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.Metadata
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
@@ -226,12 +232,15 @@ private val receiver = object: BroadcastReceiver() {
ACTION_CANCEL -> { ACTION_CANCEL -> {
job?.cancel() job?.cancel()
NotificationManagerCompat.from(context).cancel(R.id.notification_id_import) NotificationManagerCompat.from(context).cancel(R.id.notification_id_import)
context.unregisterReceiver(this)
} }
} }
} }
} }
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() { fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
registerReceiver(receiver, IntentFilter().apply { addAction(receiver.ACTION_CANCEL) })
val notificationManager = NotificationManagerCompat.from(this) val notificationManager = NotificationManagerCompat.from(this)
val action = NotificationCompat.Action.Builder(0, getText(android.R.string.cancel), val action = NotificationCompat.Action.Builder(0, getText(android.R.string.cancel),
PendingIntent.getBroadcast(this, R.id.notification_import_cancel_action.normalizeID(), Intent(receiver.ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.getBroadcast(this, R.id.notification_import_cancel_action.normalizeID(), Intent(receiver.ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT)
@@ -247,55 +256,65 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
job?.cancel() job?.cancel()
job = CoroutineScope(Dispatchers.IO).launch { job = CoroutineScope(Dispatchers.IO).launch {
val folders = downloadFolder.listFiles { folder -> val downloadFolders = downloadFolder.listFiles { folder ->
(folder as? FileX)?.isDirectory == true && !downloadFolderMap.values.contains(folder.name) (folder as? FileX)?.isDirectory == true && !downloadFolderMap.values.contains(folder.name)
} }
if (folders.isNullOrEmpty()) return@launch
folders.forEachIndexed { index, folder -> if (downloadFolders.isNullOrEmpty()) return@launch
downloadFolders.forEachIndexed { index, folder ->
notification notification
.setContentText(getString(R.string.import_old_galleries_notification_text, index, folders.size)) .setContentText(getString(R.string.import_old_galleries_notification_text, index, downloadFolders.size))
.setProgress(index, folders.size, false) .setProgress(index, downloadFolders.size, false)
notificationManager.notify(R.id.notification_id_import, notification.build()) notificationManager.notify(R.id.notification_id_import, notification.build())
kotlin.runCatching { kotlin.runCatching {
val folder = (folder as? FileX) ?: return@runCatching if (folder !is FileX) return@runCatching
val metadata = folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it).jsonObject } ?: return@runCatching val metadata = kotlin.runCatching {
folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it).jsonObject }
}.getOrNull()
val galleryBlock: GalleryBlock? = val galleryID = folder.name.toIntOrNull() ?: return@runCatching
metadata["galleryBlock"]?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
val reader: Reader? =
metadata["reader"]?.let { Json.decodeFromJsonElement<Reader>(it) }
val galleryID = galleryBlock?.id ?: reader?.galleryInfo?.id ?: folder.name.toIntOrNull() ?: return@runCatching val galleryBlock: GalleryBlock? = kotlin.runCatching {
metadata?.get("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
}.getOrNull() ?: getGalleryBlock(galleryID)
val reader: Reader? = kotlin.runCatching {
metadata?.get("reader")?.let { Json.decodeFromJsonElement<Reader>(it) }
}.getOrNull() ?: getReader(galleryID)
metadata["thumbnail"]?.jsonPrimitive?.contentOrNull.let { thumbnail -> metadata?.get("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail ->
val file = folder.getChild(".thumbnail").also { val file = folder.getChild(".thumbnail").also {
if (!it.exists()) if (it.exists())
it.createNewFile() it.delete()
it.createNewFile()
} }
file.writeBytes(Base64.decode(thumbnail, Base64.DEFAULT)) file.writeBytes(Base64.decode(thumbnail, Base64.DEFAULT))
} }
downloadFolderMap[galleryID] = folder.name
val cache = Cache.getInstance(this@migrate, galleryID)
val list: MutableList<String?> = val list: MutableList<String?> =
MutableList(cache.getReader()!!.galleryInfo.files.size) { null } MutableList(reader!!.galleryInfo.files.size) { null }
folder.listFiles { dir -> folder.listFiles { file ->
dir?.nameWithoutExtension?.toIntOrNull() != null file?.nameWithoutExtension?.let {
Regex("""\d{5}""").matches(it) && it.toIntOrNull() != null
} == true
}?.forEach { }?.forEach {
list[it.nameWithoutExtension.toInt()] = it.name list[it.nameWithoutExtension.toInt()] = it.name
} }
cache.setMetadata { folder.getChild(".metadata").also { if (it.exists()) it.delete(); it.createNewFile() }.writeText(
it.galleryBlock = galleryBlock Json.encodeToString(Metadata(galleryBlock, reader, list))
it.reader = reader )
it.imageList = list
synchronized(Cache) {
Cache.delete(galleryID)
} }
downloadFolderMap[galleryID] = folder.name
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile(); it.writeText(Json.encodeToString(downloadFolderMap)) }
} }
} }
@@ -305,5 +324,7 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
.setOngoing(false) .setOngoing(false)
.mActions.clear() .mActions.clear()
notificationManager.notify(R.id.notification_id_import, notification.build()) notificationManager.notify(R.id.notification_id_import, notification.build())
unregisterReceiver(receiver)
} }
} }