package xyz.quaver.pupil.adapters import android.graphics.BitmapFactory import android.graphics.drawable.Animatable import android.util.Log import android.util.SparseBooleanArray import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import androidx.cardview.widget.CardView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonConfiguration import kotlinx.serialization.list import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.ReaderItem import xyz.quaver.pupil.Pupil import xyz.quaver.pupil.R import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.util.Histories import java.io.File import java.util.* import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.concurrent.schedule class GalleryBlockAdapter(private val galleries: List>>) : RecyclerView.Adapter() { enum class ViewType { NEXT, GALLERY, PREV } private lateinit var favorites: Histories inner class GalleryViewHolder(private val view: CardView) : RecyclerView.ViewHolder(view) { fun bind(item: Pair>) { with(view) { val resources = context.resources val languages = resources.getStringArray(R.array.languages).map { it.split("|").let { split -> Pair(split[0], split[1]) } }.toMap() val (gallery: GalleryBlock, thumbnail: Deferred) = item val artists = gallery.artists val series = gallery.series CoroutineScope(Dispatchers.Default).launch { val cache = thumbnail.await() if (!File(cache).exists()) return@launch val bitmap = BitmapFactory.decodeFile(thumbnail.await()) post { galleryblock_thumbnail.setImageBitmap(bitmap) } } //Check cache val readerCache = { File(ContextCompat.getDataDir(context), "images/${gallery.id}/reader.json").let { when { it.exists() -> it else -> File(context.cacheDir, "imageCache/${gallery.id}/reader.json") } } } val imageCache = { File(ContextCompat.getDataDir(context), "images/${gallery.id}/images").let { when { it.exists() -> it else -> File(context.cacheDir, "imageCache/${gallery.id}/images") } } } if (readerCache.invoke().exists()) { val reader = Json(JsonConfiguration.Stable) .parse(ReaderItem.serializer().list, readerCache.invoke().readText()) with(galleryblock_progressbar) { max = reader.size progress = imageCache.invoke().list()?.size ?: 0 visibility = View.VISIBLE } } else { galleryblock_progressbar.visibility = View.GONE } if (refreshTasks[this@GalleryViewHolder] == null) { val refresh = Timer(false).schedule(0, 1000) { post { with(view.galleryblock_progressbar) { progress = imageCache.invoke().list()?.size ?: 0 if (!readerCache.invoke().exists()) { visibility = View.GONE max = 0 progress = 0 view.galleryblock_progress_complete.visibility = View.INVISIBLE } else { if (visibility == View.GONE) { val reader = Json(JsonConfiguration.Stable) .parse(ReaderItem.serializer().list, readerCache.invoke().readText()) max = reader.size visibility = View.VISIBLE } if (progress == max) { if (completeFlag.get(gallery.id, false)) { with(view.galleryblock_progress_complete) { setImageResource(R.drawable.ic_progressbar) visibility = View.VISIBLE } } else { val drawable = AnimatedVectorDrawableCompat.create(context, R.drawable.ic_progressbar_complete) with(view.galleryblock_progress_complete) { setImageDrawable(drawable) visibility = View.VISIBLE } drawable?.start() completeFlag.put(gallery.id, true) } } else view.galleryblock_progress_complete.visibility = View.INVISIBLE null } } } } refreshTasks[this@GalleryViewHolder] = refresh } galleryblock_title.text = gallery.title with(galleryblock_artist) { text = artists.joinToString(", ") { it.wordCapitalize() } visibility = when { artists.isNotEmpty() -> View.VISIBLE else -> View.GONE } } with(galleryblock_series) { text = resources.getString( R.string.galleryblock_series, series.joinToString(", ") { it.wordCapitalize() }) visibility = when { series.isNotEmpty() -> View.VISIBLE else -> View.GONE } } galleryblock_type.text = resources.getString(R.string.galleryblock_type, gallery.type).wordCapitalize() with(galleryblock_language) { text = resources.getString(R.string.galleryblock_language, languages[gallery.language]) visibility = when { gallery.language.isNotEmpty() -> View.VISIBLE else -> View.GONE } } galleryblock_tag_group.removeAllViews() gallery.relatedTags.forEach { val tag = Tag.parse(it) val chip = LayoutInflater.from(context) .inflate(R.layout.tag_chip, this, false) as Chip val icon = when(tag.area) { "male" -> { chip.setChipBackgroundColorResource(R.color.material_blue_700) chip.setTextColor(ContextCompat.getColor(context, android.R.color.white)) ContextCompat.getDrawable(context, R.drawable.ic_gender_male_white) } "female" -> { chip.setChipBackgroundColorResource(R.color.material_pink_600) chip.setTextColor(ContextCompat.getColor(context, android.R.color.white)) ContextCompat.getDrawable(context, R.drawable.ic_gender_female_white) } else -> null } chip.chipIcon = icon chip.text = tag.tag.wordCapitalize() chip.setOnClickListener { for (callback in onChipClickedHandler) callback.invoke(tag) } galleryblock_tag_group.addView(chip) } if (!::favorites.isInitialized) favorites = (context.applicationContext as Pupil).favorites with(galleryblock_favorite) { post { isChecked = favorites.contains(gallery.id) } setOnClickListener { when { isChecked -> favorites.add(gallery.id) else -> favorites.remove(gallery.id) } } setOnCheckedChangeListener { _, isChecked -> when { isChecked -> (background as Animatable).start() else -> background = AnimatedVectorDrawableCompat.create(context, R.drawable.avd_star) } } } } } } class NextViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view) class PrevViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view) class ViewHolderFactory { companion object { fun getLayoutID(type: Int): Int { return when(ViewType.values()[type]) { ViewType.NEXT -> R.layout.item_next ViewType.PREV -> R.layout.item_prev ViewType.GALLERY -> R.layout.item_galleryblock } } } } private fun String.wordCapitalize() : String { val result = ArrayList() for (word in this.split(" ")) result.add(word.capitalize()) return result.joinToString(" ") } private val refreshTasks = HashMap() val completeFlag = SparseBooleanArray() val onChipClickedHandler = ArrayList<((Tag) -> Unit)>() var showNext = false var showPrev = false override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { fun getViewHolder(type: Int, view: View): RecyclerView.ViewHolder { return when(ViewType.values()[type]) { ViewType.NEXT -> NextViewHolder(view as LinearLayout) ViewType.PREV -> PrevViewHolder(view as LinearLayout) ViewType.GALLERY -> GalleryViewHolder(view as CardView) } } return getViewHolder( viewType, LayoutInflater.from(parent.context).inflate( ViewHolderFactory.getLayoutID(viewType), parent, false ) ) } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (holder is GalleryViewHolder) holder.bind(galleries[position-(if (showPrev) 1 else 0)]) } override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { super.onViewDetachedFromWindow(holder) if (holder is GalleryViewHolder) { val task = refreshTasks[holder] ?: return task.cancel() refreshTasks.remove(holder) } } override fun getItemCount() = (if (galleries.isEmpty()) 0 else galleries.size)+ (if (showNext) 1 else 0)+ (if (showPrev) 1 else 0) override fun getItemViewType(position: Int): Int { return when { showPrev && position == 0 -> ViewType.PREV showNext && position == galleries.size+(if (showPrev) 1 else 0) -> ViewType.NEXT else -> ViewType.GALLERY }.ordinal } }