/* * Pupil, Hitomi.la viewer for Android * Copyright (C) 2019 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.adapters import android.content.Context import android.graphics.drawable.Drawable 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.Animatable2Compat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import com.daimajia.swipe.SwipeLayout import com.daimajia.swipe.adapters.RecyclerSwipeAdapter import com.daimajia.swipe.interfaces.SwipeAdapterInterface import com.github.piasy.biv.loader.ImageLoader import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.coroutines.* import xyz.quaver.hitomi.getGallery import xyz.quaver.hitomi.getReader import xyz.quaver.io.util.getChild import xyz.quaver.pupil.R import xyz.quaver.pupil.favoriteTags import xyz.quaver.pupil.favorites import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.DownloadManager import xyz.quaver.pupil.util.wordCapitalize import java.io.File class GalleryBlockAdapter(private val galleries: List) : RecyclerSwipeAdapter(), SwipeAdapterInterface { enum class ViewType { NEXT, GALLERY, PREV } var updateAll = true var thin: Boolean = Preferences["thin"] inner class GalleryViewHolder(val view: View) : RecyclerView.ViewHolder(view) { private var galleryID: Int = 0 init { CoroutineScope(Dispatchers.Main).launch { while (updateAll) { updateProgress(view.context) delay(1000) } } } private fun updateProgress(context: Context) { val cache = Cache.getInstance(context, galleryID) CoroutineScope(Dispatchers.Main).launch { if (cache.metadata.reader == null) { view.galleryblock_progressbar_layout.visibility = View.GONE view.galleryblock_progress_complete.visibility = View.INVISIBLE return@launch } with(view.galleryblock_progressbar) { val imageList = cache.metadata.imageList!! progress = imageList.count { it != null } max = imageList.size with(view.galleryblock_progressbar_layout) { if (visibility == View.GONE) visibility = View.VISIBLE } if (!imageList.contains(null)) { val downloadManager = DownloadManager.getInstance(context) if (completeFlag.get(galleryID, false)) { with(view.galleryblock_progress_complete) { setImageResource( if (downloadManager.getDownloadFolder(galleryID) != null) R.drawable.ic_progressbar else R.drawable.ic_progressbar_cache ) visibility = View.VISIBLE } } else { with(view.galleryblock_progress_complete) { setImageDrawable(AnimatedVectorDrawableCompat.create(context, if (downloadManager.getDownloadFolder(galleryID) != null) R.drawable.ic_progressbar_complete else R.drawable.ic_progressbar_complete_cache ).apply { this?.start() }) visibility = View.VISIBLE } completeFlag.put(galleryID, true) } } else view.galleryblock_progress_complete.visibility = View.INVISIBLE } } } fun bind(galleryID: Int) { this.galleryID = galleryID val cache = Cache.getInstance(view.context, galleryID) val galleryBlock = runBlocking { cache.getGalleryBlock() } ?: return 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 artists = galleryBlock.artists val series = galleryBlock.series galleryblock_thumbnail.apply { setOnClickListener { view.performClick() } setOnLongClickListener { view.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) } } } } galleryblock_title.text = galleryBlock.title with(galleryblock_artist) { 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(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, galleryBlock.type).wordCapitalize() with(galleryblock_language) { text = resources.getString(R.string.galleryblock_language, languages[galleryBlock.language]) visibility = when { galleryBlock.language.isNotEmpty() -> View.VISIBLE else -> View.GONE } } with(galleryblock_tag_group) { onClickListener = { onChipClickedHandler.forEach { callback -> callback.invoke(it) } } tags.clear() 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) } ) refresh() } galleryblock_id.text = galleryBlock.id.toString() galleryblock_pagecount.text = "-" CoroutineScope(Dispatchers.IO).launch { val pageCount = kotlin.runCatching { getReader(galleryBlock.id).galleryInfo.files.size }.getOrNull() ?: return@launch withContext(Dispatchers.Main) { galleryblock_pagecount.text = context.getString(R.string.galleryblock_pagecount, pageCount) } } with(galleryblock_favorite) { 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) { galleryblock_tag_group.visibility = View.GONE } } } } 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 } } } } val completeFlag = SparseBooleanArray() val onChipClickedHandler = ArrayList<((Tag) -> Unit)>() var onDownloadClickedHandler: ((Int) -> Unit)? = null var onDeleteClickedHandler: ((Int) -> Unit)? = null 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) { val galleryID = galleries[position-(if (showPrev) 1 else 0)] holder.bind(galleryID) with(holder.view.galleryblock_primary) { setOnClickListener { holder.view.performClick() } setOnLongClickListener { holder.view.performLongClick() } } holder.view.galleryblock_download.setOnClickListener { onDownloadClickedHandler?.invoke(position) } holder.view.galleryblock_delete.setOnClickListener { onDeleteClickedHandler?.invoke(position) } mItemManger.bindView(holder.view, position) holder.view.galleryblock_swipe_layout.addSwipeListener(object: SwipeLayout.SwipeListener { override fun onStartOpen(layout: SwipeLayout?) { mItemManger.closeAllExcept(layout) holder.view.galleryblock_download.text = if (DownloadManager.getInstance(holder.view.context).isDownloading(galleryID)) holder.view.context.getString(android.R.string.cancel) else holder.view.context.getString(R.string.main_download) } override fun onClose(layout: SwipeLayout?) {} override fun onHandRelease(layout: SwipeLayout?, xvel: Float, yvel: Float) {} override fun onOpen(layout: SwipeLayout?) {} override fun onStartClose(layout: SwipeLayout?) {} override fun onUpdate(layout: SwipeLayout?, leftOffset: Int, topOffset: Int) {} }) } } override fun getItemCount() = 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 } override fun getSwipeLayoutResourceId(position: Int) = R.id.galleryblock_swipe_layout }