Gallery Dialog

This commit is contained in:
tom5079
2021-01-29 19:44:09 +09:00
parent 4aea7d08ce
commit c7b3ae7ed1
13 changed files with 368 additions and 291 deletions

View File

@@ -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"

View File

@@ -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()

View File

@@ -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<String>) : RecyclerView.Adapter<ThumbnailAdapter.ViewHolder>() {
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

View File

@@ -29,11 +29,8 @@ class ThumbnailPageAdapter(private val thumbnails: List<String>) : 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<String>) : 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)
}
}

View File

@@ -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,

View File

@@ -120,7 +120,7 @@ class Hitomi : Source<Hitomi.SortMode, Hitomi.TagSuggestion>() {
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<Hitomi.SortMode, Hitomi.TagSuggestion>() {
}.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() },

View File

@@ -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
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<ItemInfo>()
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()
}
}
}
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<ImageInfo>() {
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<ItemInfo>) {
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()
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<ItemInfo>()
val info: LiveData<ItemInfo> = _info
private val _related = MutableLiveData<List<ItemInfo>>()
val related: LiveData<List<ItemInfo>> = _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()
}
}
}
}

View File

@@ -39,7 +39,7 @@
android:layout_height="wrap_content"
android:padding="8dp">
<com.github.piasy.biv.view.BigImageView
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/cover"
android:layout_width="150dp"
android:layout_height="wrap_content"
@@ -106,20 +106,11 @@
</androidx.core.widget.NestedScrollView>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"

View File

@@ -12,4 +12,6 @@
<dimen name="thumb_height">72dp</dimen>
<dimen name="thumbnail_page_height">300dp</dimen>
<dimen name="gallery_dialog_preview_height">150dp</dimen>
</resources>

View File

@@ -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"