diff --git a/app/build.gradle b/app/build.gradle index 8135e931..e9fdb8bd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,7 +38,7 @@ android { minSdkVersion 16 targetSdkVersion 31 versionCode 69 - versionName "5.2.10" + versionName "5.2.11" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true } diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 7eae0443..850699a2 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -12,7 +12,7 @@ "filters": [], "attributes": [], "versionCode": 69, - "versionName": "5.2.10", + "versionName": "5.2.11", "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 f540b9d9..8542cfd5 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/GalleryBlockAdapter.kt @@ -104,168 +104,171 @@ class GalleryBlockAdapter(private val galleries: List) : RecyclerSwipeAdapt val cache = Cache.getInstance(itemView.context, galleryID) - val galleryBlock = runBlocking { - cache.getGalleryBlock() - } ?: return - - val resources = itemView.context.resources - val languages = resources.getStringArray(R.array.languages).map { - it.split("|").let { split -> - Pair(split[0], split[1]) - } - }.toMap() - - val artists = galleryBlock.artists - val series = galleryBlock.series - - binding.galleryblockThumbnail.apply { - setOnClickListener { - itemView.performClick() - } - setOnLongClickListener { - itemView.performLongClick() - } - setFailureImage(ContextCompat.getDrawable(context, R.drawable.image_broken_variant)) - setImageLoaderCallback(object: ImageLoader.Callback { - override fun onFail(error: Exception?) { - Cache.getInstance(context, galleryID).let { cache -> - cache.cacheFolder.getChild(".thumbnail").let { if (it.exists()) it.delete() } - cache.downloadFolder?.getChild(".thumbnail")?.let { if (it.exists()) it.delete() } - } - } - - override fun onCacheHit(imageType: Int, image: File?) {} - override fun onCacheMiss(imageType: Int, image: File?) {} - override fun onFinish() {} - override fun onProgress(progress: Int) {} - override fun onStart() {} - override fun onSuccess(image: File?) {} - }) - ssiv?.recycle() - CoroutineScope(Dispatchers.IO).launch { - cache.getThumbnail().let { launch(Dispatchers.Main) { - showImage(it) - } } - } - } - - binding.galleryblockTitle.text = galleryBlock.title - with(binding.galleryblockArtist) { - text = artists.joinToString { it.wordCapitalize() } - visibility = when { - artists.isNotEmpty() -> View.VISIBLE - else -> View.GONE - } - - CoroutineScope(Dispatchers.IO).launch { - val gallery = runCatching { - getGallery(galleryID) - }.getOrNull() - - if (gallery?.groups?.isNotEmpty() != true) - return@launch - - launch(Dispatchers.Main) { - text = context.getString( - R.string.galleryblock_artist_with_group, - artists.joinToString { it.wordCapitalize() }, - gallery.groups.joinToString { it.wordCapitalize() } - ) - } - } - } - with(binding.galleryblockSeries) { - text = - resources.getString( - R.string.galleryblock_series, - series.joinToString(", ") { it.wordCapitalize() }) - visibility = when { - series.isNotEmpty() -> View.VISIBLE - else -> View.GONE - } - } - binding.galleryblockType.text = resources.getString(R.string.galleryblock_type, galleryBlock.type).wordCapitalize() - with(binding.galleryblockLanguage) { - text = - resources.getString(R.string.galleryblock_language, languages[galleryBlock.language]) - visibility = when { - galleryBlock.language.isNotEmpty() -> View.VISIBLE - else -> View.GONE - } - } - - with(binding.galleryblockTagGroup) { - onClickListener = { - onChipClickedHandler.forEach { callback -> - callback.invoke(it) - } - } - - tags.clear() - - CoroutineScope(Dispatchers.IO).launch { - tags.addAll( - galleryBlock.relatedTags.sortedBy { - val tag = Tag.parse(it) - - if (favoriteTags.contains(tag)) - -1 - else - when(Tag.parse(it).area) { - "female" -> 0 - "male" -> 1 - else -> 2 - } - }.map { - Tag.parse(it) - } - ) - - launch(Dispatchers.Main) { - refresh() - } - } - } - - binding.galleryblockId.text = galleryBlock.id.toString() - binding.galleryblockPagecount.text = "-" CoroutineScope(Dispatchers.IO).launch { - val pageCount = kotlin.runCatching { - getGalleryInfo(galleryBlock.id).files.size - }.getOrNull() ?: return@launch - withContext(Dispatchers.Main) { - binding.galleryblockPagecount.text = itemView.context.getString(R.string.galleryblock_pagecount, pageCount) - } - } + val galleryBlock = cache.getGalleryBlock() ?: return@launch - with(binding.galleryblockFavorite) { - setImageResource(if (favorites.contains(galleryBlock.id)) R.drawable.ic_star_filled else R.drawable.ic_star_empty) - setOnClickListener { - when { - favorites.contains(galleryBlock.id) -> { - favorites.remove(galleryBlock.id) - - setImageResource(R.drawable.ic_star_empty) + launch(Dispatchers.Main) { + val resources = itemView.context.resources + val languages = resources.getStringArray(R.array.languages).map { + it.split("|").let { split -> + Pair(split[0], split[1]) } - else -> { - favorites.add(galleryBlock.id) + }.toMap() - setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star).apply { - this ?: return@apply + val artists = galleryBlock.artists + val series = galleryBlock.series - registerAnimationCallback(object: Animatable2Compat.AnimationCallback() { - override fun onAnimationEnd(drawable: Drawable?) { - setImageResource(R.drawable.ic_star_filled) - } - }) - start() - }) + binding.galleryblockThumbnail.apply { + setOnClickListener { + itemView.performClick() + } + setOnLongClickListener { + itemView.performLongClick() + } + setFailureImage(ContextCompat.getDrawable(context, R.drawable.image_broken_variant)) + setImageLoaderCallback(object: ImageLoader.Callback { + override fun onFail(error: Exception?) { + Cache.getInstance(context, galleryID).let { cache -> + cache.cacheFolder.getChild(".thumbnail").let { if (it.exists()) it.delete() } + cache.downloadFolder?.getChild(".thumbnail")?.let { if (it.exists()) it.delete() } + } + } + + override fun onCacheHit(imageType: Int, image: File?) {} + override fun onCacheMiss(imageType: Int, image: File?) {} + override fun onFinish() {} + override fun onProgress(progress: Int) {} + override fun onStart() {} + override fun onSuccess(image: File?) {} + }) + ssiv?.recycle() + CoroutineScope(Dispatchers.IO).launch { + cache.getThumbnail().let { launch(Dispatchers.Main) { + showImage(it) + } } } } + + binding.galleryblockTitle.text = galleryBlock.title + with(binding.galleryblockArtist) { + text = artists.joinToString { it.wordCapitalize() } + visibility = when { + artists.isNotEmpty() -> View.VISIBLE + else -> View.GONE + } + + CoroutineScope(Dispatchers.IO).launch { + val gallery = runCatching { + getGallery(galleryID) + }.getOrNull() + + if (gallery?.groups?.isNotEmpty() != true) + return@launch + + launch(Dispatchers.Main) { + text = context.getString( + R.string.galleryblock_artist_with_group, + artists.joinToString { it.wordCapitalize() }, + gallery.groups.joinToString { it.wordCapitalize() } + ) + } + } + } + with(binding.galleryblockSeries) { + text = + resources.getString( + R.string.galleryblock_series, + series.joinToString(", ") { it.wordCapitalize() }) + visibility = when { + series.isNotEmpty() -> View.VISIBLE + else -> View.GONE + } + } + binding.galleryblockType.text = resources.getString(R.string.galleryblock_type, galleryBlock.type).wordCapitalize() + with(binding.galleryblockLanguage) { + text = + resources.getString(R.string.galleryblock_language, languages[galleryBlock.language]) + visibility = when { + galleryBlock.language.isNotEmpty() -> View.VISIBLE + else -> View.GONE + } + } + + with(binding.galleryblockTagGroup) { + onClickListener = { + onChipClickedHandler.forEach { callback -> + callback.invoke(it) + } + } + + tags.clear() + + CoroutineScope(Dispatchers.IO).launch { + tags.addAll( + galleryBlock.relatedTags.sortedBy { + val tag = Tag.parse(it) + + if (favoriteTags.contains(tag)) + -1 + else + when(Tag.parse(it).area) { + "female" -> 0 + "male" -> 1 + else -> 2 + } + }.map { + Tag.parse(it) + } + ) + + launch(Dispatchers.Main) { + refresh() + } + } + } + + binding.galleryblockId.text = galleryBlock.id.toString() + binding.galleryblockPagecount.text = "-" + CoroutineScope(Dispatchers.IO).launch { + val pageCount = kotlin.runCatching { + getGalleryInfo(galleryBlock.id).files.size + }.getOrNull() ?: return@launch + withContext(Dispatchers.Main) { + binding.galleryblockPagecount.text = itemView.context.getString(R.string.galleryblock_pagecount, pageCount) + } + } + + with(binding.galleryblockFavorite) { + setImageResource(if (favorites.contains(galleryBlock.id)) R.drawable.ic_star_filled else R.drawable.ic_star_empty) + setOnClickListener { + when { + favorites.contains(galleryBlock.id) -> { + favorites.remove(galleryBlock.id) + + setImageResource(R.drawable.ic_star_empty) + } + else -> { + favorites.add(galleryBlock.id) + + setImageDrawable(AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star).apply { + this ?: return@apply + + registerAnimationCallback(object: Animatable2Compat.AnimationCallback() { + override fun onAnimationEnd(drawable: Drawable?) { + setImageResource(R.drawable.ic_star_filled) + } + }) + start() + }) + } + } + } + } + + } } - // Make some views invisible to make it thinner if (thin) { binding.galleryblockTagGroup.visibility = View.GONE diff --git a/app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt b/app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt index 27e160c0..451106ec 100644 --- a/app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt +++ b/app/src/main/java/xyz/quaver/pupil/hitomi/Utils.kt @@ -16,21 +16,11 @@ package xyz.quaver -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.plus -import kotlinx.serialization.modules.polymorphic -import kotlinx.serialization.modules.subclass -import okhttp3.OkHttpClient import okhttp3.Request import xyz.quaver.pupil.client import java.io.IOException import java.net.URL -import java.util.concurrent.TimeUnit -import kotlin.time.Duration /** * kotlinx.serialization.json.Json object for global use diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadFolderNameDialogFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadFolderNameDialogFragment.kt index 673b4769..86af6cc9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadFolderNameDialogFragment.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/DownloadFolderNameDialogFragment.kt @@ -24,7 +24,9 @@ import android.view.ViewGroup import androidx.core.widget.addTextChangedListener import androidx.fragment.app.DialogFragment import com.google.android.material.snackbar.Snackbar -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import xyz.quaver.pupil.R import xyz.quaver.pupil.databinding.DownloadFolderNameDialogBinding import xyz.quaver.pupil.util.Preferences @@ -56,15 +58,16 @@ class DownloadFolderNameDialogFragment : DialogFragment() { private fun initView() { val galleryID = Cache.instances.let { if (it.size == 0) 1199708 else it.keys.elementAt((0 until it.size).random()) } - val galleryBlock = runBlocking { - Cache.getInstance(requireContext(), galleryID).getGalleryBlock() + CoroutineScope(Dispatchers.IO).launch { + val galleryBlock = Cache.getInstance(requireContext(), galleryID).getGalleryBlock() + + binding.message.text = getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolder() ?: "") + binding.edittext.addTextChangedListener { + binding.message.text = requireContext().getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolderTest(it.toString()) ?: "") + } } - binding.message.text = getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolder() ?: "") binding.edittext.setText(Preferences["download_folder_name", "[-id-] -title-"]) - binding.edittext.addTextChangedListener { - binding.message.text = requireContext().getString(R.string.settings_download_folder_name_message, formatMap.keys.toString(), galleryBlock?.formatDownloadFolderTest(it.toString()) ?: "") - } binding.okButton.setOnClickListener { val newValue = binding.edittext.text.toString() diff --git a/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt b/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt index 9b2e209c..07f871f6 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/downloader/DownloadManager.kt @@ -20,8 +20,9 @@ package xyz.quaver.pupil.util.downloader import android.content.Context import android.content.ContextWrapper -import android.util.Log -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -96,16 +97,13 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con fun getDownloadFolder(galleryID: Int): FileX? = downloadFolderMap[galleryID]?.let { downloadFolder.getChild(it) } - @Synchronized - fun addDownloadFolder(galleryID: Int) { - val name = runBlocking { - Cache.getInstance(this@DownloadManager, galleryID).getGalleryBlock() - }?.formatDownloadFolder() ?: return + fun addDownloadFolder(galleryID: Int) = CoroutineScope(Dispatchers.IO).launch { + val name = Cache.getInstance(this@DownloadManager, galleryID).getGalleryBlock() + ?.formatDownloadFolder() ?: return@launch val folder = downloadFolder.getChild(name) - if (folder.exists()) - return + if (folder.exists()) return@launch folder.mkdir()