From c7b3ae7ed131ed2f6f6bd4eba259508faf4879cc Mon Sep 17 00:00:00 2001 From: tom5079 Date: Fri, 29 Jan 2021 19:44:09 +0900 Subject: [PATCH] Gallery Dialog --- app/build.gradle | 12 +- app/src/main/java/xyz/quaver/pupil/Pupil.kt | 4 + .../quaver/pupil/adapters/ThumbnailAdapter.kt | 27 +- .../pupil/adapters/ThumbnailPageAdapter.kt | 9 +- .../java/xyz/quaver/pupil/sources/Common.kt | 5 + .../java/xyz/quaver/pupil/sources/Hitomi.kt | 4 +- .../java/xyz/quaver/pupil/ui/MainActivity.kt | 14 +- .../quaver/pupil/ui/dialog/GalleryDialog.kt | 246 ----------------- .../pupil/ui/dialog/GalleryDialogFragment.kt | 254 ++++++++++++++++++ .../ui/viewmodel/GalleryDialogViewModel.kt | 59 ++++ app/src/main/res/layout/gallery_dialog.xml | 21 +- app/src/main/res/values/dimen.xml | 2 + build.gradle | 2 +- 13 files changed, 368 insertions(+), 291 deletions(-) delete mode 100644 app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt create mode 100644 app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt diff --git a/app/build.gradle b/app/build.gradle index e9961c06..d0f26fa7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -92,19 +92,19 @@ dependencies { implementation "androidx.appcompat:appcompat:1.2.0" implementation "androidx.activity:activity-ktx:1.2.0-rc01" - implementation "androidx.fragment:fragment-ktx:1.3.0-rc01" + implementation "androidx.fragment:fragment-ktx:1.3.0-rc02" implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.gridlayout:gridlayout:1.0.0" - implementation "androidx.biometric:biometric:1.0.1" - implementation "androidx.work:work-runtime-ktx:2.4.0" + implementation "androidx.biometric:biometric:1.1.0" + implementation "androidx.work:work-runtime-ktx:2.5.0" implementation 'org.kodein.di:kodein-di-framework-android-x:7.1.0' implementation "com.daimajia.swipelayout:library:1.2.0@aar" - implementation "com.google.android.material:material:1.3.0-beta01" + implementation "com.google.android.material:material:1.3.0-rc01" implementation platform("com.google.firebase:firebase-bom:26.1.0") implementation "com.google.firebase:firebase-analytics-ktx" @@ -138,7 +138,9 @@ dependencies { implementation "xyz.quaver:documentfilex:0.4-alpha02" implementation "xyz.quaver:floatingsearchview:1.1.1" - // debugImplementation"com.squareup.leakcanary:leakcanary-android:2.6" + implementation "com.orhanobut:logger:2.2.0" + + debugImplementation "com.squareup.leakcanary:leakcanary-android:2.6" testImplementation "junit:junit:4.13.1" androidTestImplementation "androidx.test.ext:junit:1.1.2" diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt index b0cea2e5..36b46417 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt @@ -38,6 +38,8 @@ import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.ktx.analytics import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.ktx.Firebase +import com.orhanobut.logger.AndroidLogAdapter +import com.orhanobut.logger.Logger import okhttp3.* import org.kodein.di.* import org.kodein.di.android.x.androidXModule @@ -96,6 +98,8 @@ class Pupil : Application(), DIAware { firebaseAnalytics = Firebase.analytics FirebaseCrashlytics.getInstance().setUserId(userID) + Logger.addLogAdapter(AndroidLogAdapter()) + val proxyInfo = getProxyInfo() clientBuilder = OkHttpClient.Builder() diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailAdapter.kt index 3b9a081b..50a4e11b 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailAdapter.kt @@ -18,29 +18,40 @@ package xyz.quaver.pupil.adapters -import android.net.Uri import android.view.ViewGroup -import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView -import com.github.piasy.biv.view.BigImageView +import com.facebook.drawee.drawable.ScalingUtils +import com.facebook.drawee.view.SimpleDraweeView import xyz.quaver.pupil.R class ThumbnailAdapter(var thumbnails: List) : RecyclerView.Adapter() { - class ViewHolder(val view: BigImageView) : RecyclerView.ViewHolder(view) { + class ViewHolder(val view: SimpleDraweeView) : RecyclerView.ViewHolder(view) { + + init { + view.hierarchy.actualImageScaleType = ScalingUtils.ScaleType.FIT_CENTER + } + + fun bind(image: String) { + view.setImageURI(image) + } + fun clear() { - view.ssiv?.recycle() + view.controller = null } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(BigImageView(parent.context).apply { - setFailureImage(ContextCompat.getDrawable(context, R.drawable.image_broken_variant)) + return ViewHolder(SimpleDraweeView(parent.context).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + resources.getDimensionPixelSize(R.dimen.gallery_dialog_preview_height) + ) }) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.view.showImage(Uri.parse(thumbnails[position])) + holder.bind(thumbnails[position]) } override fun getItemCount() = thumbnails.size diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailPageAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailPageAdapter.kt index 22667059..46bb5efe 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailPageAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ThumbnailPageAdapter.kt @@ -29,11 +29,8 @@ class ThumbnailPageAdapter(private val thumbnails: List) : RecyclerView. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder(RecyclerView(parent.context).apply { - val layoutManager = GridLayoutManager(parent.context, 3) - val adapter = ThumbnailAdapter(listOf()) - - this.layoutManager = layoutManager - this.adapter = adapter + this.layoutManager = GridLayoutManager(parent.context, 3) + this.adapter = ThumbnailAdapter(listOf()) layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) }) } @@ -42,8 +39,6 @@ class ThumbnailPageAdapter(private val thumbnails: List) : RecyclerView. (holder.view.adapter as ThumbnailAdapter).apply { thumbnails = this@ThumbnailPageAdapter.thumbnails.slice(9*position until min(9*position+9, this@ThumbnailPageAdapter.thumbnails.size)) notifyDataSetChanged() - - (holder.view.layoutManager as GridLayoutManager).scrollToPosition(8) } } diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt index f9306c89..5a038d38 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/Common.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/Common.kt @@ -97,6 +97,11 @@ data class ItemInfo( } } + val isReady: Boolean + get() = extra.all { it.value.isCompleted } + + suspend fun awaitAll() = extra.values.awaitAll() + companion object { val extraTypeMap = mapOf( ExtraType.SERIES to R.string.galleryblock_series, diff --git a/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt b/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt index 845e2553..7f7869c3 100644 --- a/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt +++ b/app/src/main/java/xyz/quaver/pupil/sources/Hitomi.kt @@ -120,7 +120,7 @@ class Hitomi : Source() { mapOf( ExtraType.TYPE to async { it.type.wordCapitalize() }, ExtraType.GROUP to async { it.groups.joinToString { it.wordCapitalize() } }, - ExtraType.LANGUAGE to async { languageMap[it.language] ?: it.language }, + ExtraType.LANGUAGE to async { it.language }, ExtraType.SERIES to async { it.series.joinToString { it.wordCapitalize() } }, ExtraType.CHARACTER to async { it.characters.joinToString { it.wordCapitalize() } }, ExtraType.TAGS to async { it.tags.joinToString() }, @@ -227,7 +227,7 @@ class Hitomi : Source() { }.getOrDefault("") }, ExtraType.SERIES to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.series.joinToString { it.wordCapitalize() } }, ExtraType.TYPE to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.type.wordCapitalize() }, - ExtraType.LANGUAGE to CoroutineScope(Dispatchers.Unconfined).async { languageMap[galleryBlock.language] ?: galleryBlock.language }, + ExtraType.LANGUAGE to CoroutineScope(Dispatchers.Unconfined).async { galleryBlock.language }, ExtraType.PAGECOUNT to CoroutineScope(Dispatchers.IO).async { kotlin.runCatching { getGalleryInfo(galleryBlock.id).files.size.toString() }.getOrNull() }, diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index f56442d2..3be2bee5 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -56,7 +56,7 @@ import xyz.quaver.pupil.sources.sourceIcons import xyz.quaver.pupil.sources.sources import xyz.quaver.pupil.types.* import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment -import xyz.quaver.pupil.ui.dialog.GalleryDialog +import xyz.quaver.pupil.ui.dialog.GalleryDialogFragment import xyz.quaver.pupil.ui.dialog.SourceSelectDialog import xyz.quaver.pupil.ui.view.ProgressCardView import xyz.quaver.pupil.ui.view.SwipePageTurnView @@ -264,7 +264,7 @@ class MainActivity : launch(Dispatchers.Main) { setImageResource(R.drawable.shuffle_variant) - GalleryDialog(this@MainActivity, randomResult.id).apply { + GalleryDialogFragment(source.name, randomResult.id).apply { onChipClickedHandler.add { query = it.toQuery() currentPage = 1 @@ -272,7 +272,7 @@ class MainActivity : query() dismiss() } - }.show() + }.show(supportFragmentManager, "GalleryDialogFragment") } } } @@ -292,7 +292,7 @@ class MainActivity : setPositiveButton(android.R.string.ok) { _, _ -> val galleryID = editText.text.toString() - GalleryDialog(this@MainActivity, galleryID).apply { + GalleryDialogFragment(source.name, galleryID).apply { onChipClickedHandler.add { query = it.toQuery() currentPage = 1 @@ -300,7 +300,7 @@ class MainActivity : query() dismiss() } - }.show() + }.show(supportFragmentManager, "GalleryDialogFragment") } }.show() } @@ -380,7 +380,7 @@ class MainActivity : val result = searchResults.getOrNull(position) ?: return@listener true - GalleryDialog(this@MainActivity, result.id).apply { + GalleryDialogFragment(source.name, result.id).apply { onChipClickedHandler.add { query = it.toQuery() currentPage = 1 @@ -388,7 +388,7 @@ class MainActivity : query() dismiss() } - }.show() + }.show(supportFragmentManager, "GalleryDialogFragment") true } 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 deleted file mode 100644 index 21ad0e3f..00000000 --- a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialog.kt +++ /dev/null @@ -1,246 +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.ui.dialog - -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.View -import android.view.ViewGroup -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 -import androidx.viewpager2.widget.ViewPager2 -import com.google.android.material.snackbar.Snackbar -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import xyz.quaver.hitomi.Gallery -import xyz.quaver.hitomi.getGallery -import xyz.quaver.pupil.R -import xyz.quaver.pupil.adapters.SearchResultsAdapter -import xyz.quaver.pupil.adapters.ThumbnailPageAdapter -import xyz.quaver.pupil.databinding.* -import xyz.quaver.pupil.favoriteTags -import xyz.quaver.pupil.sources.ItemInfo -import xyz.quaver.pupil.types.Tag -import xyz.quaver.pupil.ui.ReaderActivity -import xyz.quaver.pupil.ui.view.TagChip -import xyz.quaver.pupil.util.ItemClickSupport -import xyz.quaver.pupil.util.wordCapitalize -import java.util.* -import kotlin.collections.ArrayList - -class GalleryDialog(context: Context, private val galleryID: String) : AlertDialog(context) { - - val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>() - - private lateinit var binding: GalleryDialogBinding - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = GalleryDialogBinding.inflate(layoutInflater) - setContentView(binding.root) - - window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) - - with (binding.fab) { - setImageDrawable(ContextCompat.getDrawable(context, R.drawable.arrow_right)) - setOnClickListener { - context.startActivity(Intent(context, ReaderActivity::class.java).apply { - putExtra("galleryID", galleryID) - }) - } - } - - CoroutineScope(Dispatchers.IO).launch { - try { - val gallery = getGallery(galleryID.toInt()) - - launch (Dispatchers.Main) { - binding.progressbar.visibility = View.GONE - binding.title.text = gallery.title - binding.artist.text = gallery.artists.joinToString(", ") { it.wordCapitalize() } - - with (binding.type) { - text = gallery.type.wordCapitalize() - setOnClickListener { - gallery.type.let { - when (it) { - "artist CG" -> "artistcg" - "game CG" -> "gamecg" - else -> it - } - }.let { - onChipClickedHandler.forEach { handler -> - handler.invoke(Tag("type", it)) - } - } - } - } - - binding.cover.showImage(Uri.parse(gallery.cover)) - - addDetails(gallery) - addThumbnails(gallery) - addRelated(gallery) - } - } catch (e: Exception) { - Snackbar.make(binding.root, R.string.unable_to_connect, Snackbar.LENGTH_INDEFINITE).apply { - if (Locale.getDefault().language == "ko") - setAction(context.getText(R.string.https_text)) { - context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(context.getString(R.string.https)))) - } - }.show() - } - } - } - - private fun addDetails(gallery: Gallery) { - GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { - type.setText(R.string.gallery_details) - - listOf( - R.string.gallery_artists, - R.string.gallery_groups, - R.string.gallery_language, - R.string.gallery_series, - R.string.gallery_characters, - R.string.gallery_tags - ).zip( - listOf( - gallery.artists.map { Tag("artist", it) }, - gallery.groups.map { Tag("group", it) }, - listOf(gallery.language).map { Tag("language", it) }, - gallery.series.map { Tag("series", it) }, - gallery.characters.map { Tag("character", it) }, - gallery.tags.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).let { tag -> - when { - tag.area != null -> tag - else -> Tag("tag", it) - } - } - } - ) - ).filter { - (_, content) -> content.isNotEmpty() - }.forEach { (title, content) -> - GalleryDialogTagsBinding.inflate(layoutInflater, contents, true).apply { - type.setText(title) - - content.forEach { tag -> - tags.addView( - TagChip(context, tag).apply { - setOnClickListener { - onChipClickedHandler.forEach { handler -> - handler.invoke(tag) - } - } - } - ) - } - } - } - } - } - - private fun addThumbnails(gallery: Gallery) { - GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { - type.setText(R.string.gallery_thumbnails) - - val pager = ViewPager2(context).apply { - adapter = ThumbnailPageAdapter(gallery.thumbnails) - layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) - } - - contents.addView( - pager, - LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) - ) - - // TODO: Change to direct allocation - GalleryDialogDotindicatorBinding.inflate(layoutInflater, contents, true).apply { - dotindicator.setViewPager2(pager) - } - } - } - - private fun addRelated(gallery: Gallery) { - val galleries = mutableListOf() - - val adapter = SearchResultsAdapter(galleries).apply { - onChipClickedHandler = { tag -> - this@GalleryDialog.onChipClickedHandler.forEach { handler -> - handler.invoke(tag) - } - } - } - - GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { - type.setText(R.string.gallery_related) - - contents.addView(RecyclerView(context).apply { - layoutManager = LinearLayoutManager(context) - this.adapter = adapter - - ItemClickSupport.addTo(this).apply { - onItemClickListener = { _, position, _ -> - context.startActivity(Intent(context, ReaderActivity::class.java).apply { - putExtra("galleryID", galleries[position].id) - }) - } - onItemLongClickListener = { _, position, _ -> - GalleryDialog(context, galleries[position].id).apply { - onChipClickedHandler.add { tag -> - this@GalleryDialog.onChipClickedHandler.forEach { it.invoke(tag) } - } - }.show() - - true - } - } - }) - - CoroutineScope(Dispatchers.IO).launch { - gallery.related.forEach { galleryID -> - withContext(Dispatchers.Main) { - adapter.notifyDataSetChanged() - } - } - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt new file mode 100644 index 00000000..5ba43263 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/dialog/GalleryDialogFragment.kt @@ -0,0 +1,254 @@ +/* + * 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.ui.dialog + +import android.app.Dialog +import android.content.Intent +import android.graphics.drawable.Animatable +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout.LayoutParams +import androidx.appcompat.app.AlertDialog +import androidx.core.content.ContextCompat +import androidx.core.view.forEach +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.controller.BaseControllerListener +import com.facebook.imagepipeline.image.ImageInfo +import com.orhanobut.logger.Logger +import kotlinx.coroutines.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import xyz.quaver.pupil.R +import xyz.quaver.pupil.adapters.SearchResultsAdapter +import xyz.quaver.pupil.adapters.ThumbnailPageAdapter +import xyz.quaver.pupil.databinding.* +import xyz.quaver.pupil.favoriteTags +import xyz.quaver.pupil.sources.ItemInfo +import xyz.quaver.pupil.types.Tag +import xyz.quaver.pupil.ui.ReaderActivity +import xyz.quaver.pupil.ui.view.TagChip +import xyz.quaver.pupil.ui.viewmodel.GalleryDialogViewModel +import xyz.quaver.pupil.util.ItemClickSupport +import xyz.quaver.pupil.util.wordCapitalize +import java.util.* +import kotlin.collections.ArrayList + +class GalleryDialogFragment(private val source: String, private val itemID: String) : DialogFragment() { + + val onChipClickedHandler = ArrayList<((Tag) -> (Unit))>() + + private var _binding: GalleryDialogBinding? = null + private val binding get() = _binding!! + + private val viewModel: GalleryDialogViewModel by viewModels() + + private val controllerListener = object: BaseControllerListener() { + override fun onIntermediateImageSet(id: String?, imageInfo: ImageInfo?) { + imageInfo?.let { + binding.cover.aspectRatio = it.width / it.height.toFloat() + } + } + + override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) { + imageInfo?.let { + binding.cover.aspectRatio = it.width / it.height.toFloat() + } + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + _binding = GalleryDialogBinding.inflate(layoutInflater) + + with (binding.fab) { + setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.arrow_right)) + setOnClickListener { + context?.startActivity(Intent(requireContext(), ReaderActivity::class.java).apply { + putExtra("source", source) + putExtra("id", itemID) + }) + } + } + + val lilMutex = Mutex(true) + viewModel.info.observe(this) { + binding.progressbar.visibility = View.GONE + binding.title.text = it.title + binding.artist.text = it.artists + + binding.cover.controller = Fresco.newDraweeControllerBuilder() + .setUri(it.thumbnail) + .setControllerListener(controllerListener) + .setOldController(binding.cover.controller) + .build() + + MainScope().launch { + binding.type.text = it.extra[ItemInfo.ExtraType.TYPE]?.await()?.wordCapitalize() + addDetails(it) + addPreviews(it) + + lilMutex.unlock() + } + } + + viewModel.related.observe(this) { + if (it != null) { + MainScope().launch { + lilMutex.withLock { + addRelated(it) + } + } + } + } + + viewModel.load(source, itemID) + + return AlertDialog.Builder(requireContext()) + .setView(binding.root) + .create() + } + + private suspend fun addDetails(info: ItemInfo) { + GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { + type.setText(R.string.gallery_details) + + listOf( + R.string.gallery_artists, + R.string.gallery_groups, + R.string.gallery_language, + R.string.gallery_series, + R.string.gallery_characters, + R.string.gallery_tags + ).zip( + listOf( + info.artists.split(", ").map { Tag("artist", it) }, + info.extra[ItemInfo.ExtraType.GROUP]?.await()?.split(", ")?.filterNot { it.isEmpty() }?.map { Tag("group", it) }, + info.extra[ItemInfo.ExtraType.LANGUAGE]?.await()?.split(", ")?.filterNot { it.isEmpty() }?.map { Tag("language", it) }, + info.extra[ItemInfo.ExtraType.SERIES]?.await()?.split(", ")?.filterNot { it.isEmpty() }?.map { Tag("series", it) }, + info.extra[ItemInfo.ExtraType.CHARACTER]?.await()?.split(", ")?.filterNot { it.isEmpty() }?.map { Tag("character", it) }, + info.extra[ItemInfo.ExtraType.TAGS]?.await()?.split(", ")?.filterNot { it.isEmpty() }?.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).let { tag -> + when { + tag.area != null -> tag + else -> Tag("tag", it) + } + } + } + ) + ).filterNot { (_, content) -> + content.isNullOrEmpty() + }.forEach { (title, content) -> + GalleryDialogTagsBinding.inflate(layoutInflater, contents, true).apply { + type.setText(title) + + content!!.forEach { tag -> + tags.addView( + TagChip(requireContext(), tag).apply { + setOnClickListener { + onChipClickedHandler.forEach { handler -> + handler.invoke(tag) + } + } + } + ) + } + } + } + } + } + + private suspend fun addPreviews(info: ItemInfo) { + val previews = info.extra[ItemInfo.ExtraType.PREVIEW]?.await()?.split(", ") ?: return + + GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { + type.setText(R.string.gallery_thumbnails) + + val pager = ViewPager2(requireContext()).apply { + adapter = ThumbnailPageAdapter(previews) + layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + } + + contents.addView(pager) + + // TODO: Change to direct allocation + GalleryDialogDotindicatorBinding.inflate(layoutInflater, contents, true).apply { + dotindicator.setViewPager2(pager) + } + } + } + + private fun addRelated(relatedItems: List) { + val adapter = SearchResultsAdapter(relatedItems).apply { + onChipClickedHandler = { tag -> + this@GalleryDialogFragment.onChipClickedHandler.forEach { handler -> + handler.invoke(tag) + } + } + } + + GalleryDialogDetailsBinding.inflate(layoutInflater, binding.contents, true).apply { + type.setText(R.string.gallery_related) + + contents.addView(RecyclerView(requireContext()).apply { + layoutManager = LinearLayoutManager(context) + this.adapter = adapter + + ItemClickSupport.addTo(this).apply { + onItemClickListener = { _, position, _ -> + requireContext().startActivity(Intent(requireContext(), ReaderActivity::class.java).apply { + putExtra("source", source) + putExtra("id", relatedItems[position].id) + }) + } + onItemLongClickListener = { _, position, _ -> + GalleryDialogFragment(source, relatedItems[position].id).apply { + onChipClickedHandler.add { tag -> + this@GalleryDialogFragment.onChipClickedHandler.forEach { it.invoke(tag) } + } + }.show(parentFragmentManager, "") + + true + } + } + }) + } + } + + override fun onDestroyView() { + binding.contents.forEach { if (it is RecyclerView) ItemClickSupport.removeFrom(it) } + super.onDestroyView() + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt new file mode 100644 index 00000000..fd54f932 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/GalleryDialogViewModel.kt @@ -0,0 +1,59 @@ +/* + * Pupil, Hitomi.la viewer for Android + * Copyright (C) 2021 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.ui.viewmodel + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.* +import org.kodein.di.DIAware +import org.kodein.di.android.x.di +import org.kodein.di.instance +import xyz.quaver.pupil.sources.AnySource +import xyz.quaver.pupil.sources.ItemInfo + +class GalleryDialogViewModel(app: Application) : AndroidViewModel(app), DIAware { + + override val di by di() + + private val _info = MutableLiveData() + val info: LiveData = _info + + private val _related = MutableLiveData>() + val related: LiveData> = _related + + fun load(source: String, itemID: String) { + val source: AnySource by instance(tag = source) + + viewModelScope.launch { + _info.value = withContext(Dispatchers.IO) { + source.info(itemID).also { it.awaitAll() } + }.also { + _related.value = it.extra[ItemInfo.ExtraType.RELATED_ITEM]?.await()?.split(", ")?.map { related -> + async(Dispatchers.IO) { + source.info(related) + } + }?.awaitAll() + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/gallery_dialog.xml b/app/src/main/res/layout/gallery_dialog.xml index cb7c017f..a48248f2 100644 --- a/app/src/main/res/layout/gallery_dialog.xml +++ b/app/src/main/res/layout/gallery_dialog.xml @@ -39,7 +39,7 @@ android:layout_height="wrap_content" android:padding="8dp"> - - - - - - + 72dp 300dp + + 150dp \ No newline at end of file diff --git a/build.gradle b/build.gradle index a965719d..29cb9a8f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:4.1.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"