mirror of
https://github.com/FossifyOrg/Gallery.git
synced 2024-11-22 04:28:00 +01:00
Rely solely on recyclerview for lazy loading
Closes https://github.com/FossifyOrg/Gallery/issues/136 - Remove manual lazy loading mechanism - Disable cross fade when loading from cache
This commit is contained in:
parent
85f5f43b29
commit
ff836d8c24
3 changed files with 37 additions and 69 deletions
|
@ -4,8 +4,6 @@ import android.content.Intent
|
||||||
import android.content.pm.ShortcutInfo
|
import android.content.pm.ShortcutInfo
|
||||||
import android.content.pm.ShortcutManager
|
import android.content.pm.ShortcutManager
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -38,11 +36,8 @@ import org.fossify.gallery.models.ThumbnailSection
|
||||||
class MediaAdapter(
|
class MediaAdapter(
|
||||||
activity: BaseSimpleActivity, var media: ArrayList<ThumbnailItem>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
|
activity: BaseSimpleActivity, var media: ArrayList<ThumbnailItem>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
|
||||||
val allowMultiplePicks: Boolean, val path: String, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit
|
val allowMultiplePicks: Boolean, val path: String, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit
|
||||||
) :
|
) : MyRecyclerViewAdapter(activity, recyclerView, itemClick), RecyclerViewFastScroller.OnPopupTextUpdate {
|
||||||
MyRecyclerViewAdapter(activity, recyclerView, itemClick), RecyclerViewFastScroller.OnPopupTextUpdate {
|
|
||||||
|
|
||||||
private val INSTANT_LOAD_DURATION = 2000L
|
|
||||||
private val IMAGE_LOAD_DELAY = 100L
|
|
||||||
private val ITEM_SECTION = 0
|
private val ITEM_SECTION = 0
|
||||||
private val ITEM_MEDIUM_VIDEO_PORTRAIT = 1
|
private val ITEM_MEDIUM_VIDEO_PORTRAIT = 1
|
||||||
private val ITEM_MEDIUM_PHOTO = 2
|
private val ITEM_MEDIUM_PHOTO = 2
|
||||||
|
@ -50,10 +45,7 @@ class MediaAdapter(
|
||||||
private val config = activity.config
|
private val config = activity.config
|
||||||
private val viewType = config.getFolderViewType(if (config.showAll) SHOW_ALL else path)
|
private val viewType = config.getFolderViewType(if (config.showAll) SHOW_ALL else path)
|
||||||
private val isListViewType = viewType == VIEW_TYPE_LIST
|
private val isListViewType = viewType == VIEW_TYPE_LIST
|
||||||
private var visibleItemPaths = ArrayList<String>()
|
|
||||||
private var rotatedImagePaths = ArrayList<String>()
|
private var rotatedImagePaths = ArrayList<String>()
|
||||||
private var loadImageInstantly = false
|
|
||||||
private var delayHandler = Handler(Looper.getMainLooper())
|
|
||||||
private var currentMediaHash = media.hashCode()
|
private var currentMediaHash = media.hashCode()
|
||||||
private val hasOTGConnected = activity.hasOTGConnected()
|
private val hasOTGConnected = activity.hasOTGConnected()
|
||||||
|
|
||||||
|
@ -69,7 +61,6 @@ class MediaAdapter(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setupDragListener(true)
|
setupDragListener(true)
|
||||||
enableInstantLoad()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActionMenuId() = R.menu.cab_media
|
override fun getActionMenuId() = R.menu.cab_media
|
||||||
|
@ -97,10 +88,6 @@ class MediaAdapter(
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
|
||||||
val tmbItem = media.getOrNull(position) ?: return
|
val tmbItem = media.getOrNull(position) ?: return
|
||||||
if (tmbItem is Medium) {
|
|
||||||
visibleItemPaths.add(tmbItem.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
val allowLongPress = (!isAGetIntent || allowMultiplePicks) && tmbItem is Medium
|
val allowLongPress = (!isAGetIntent || allowMultiplePicks) && tmbItem is Medium
|
||||||
holder.bindView(tmbItem, tmbItem is Medium, allowLongPress) { itemView, adapterPosition ->
|
holder.bindView(tmbItem, tmbItem is Medium, allowLongPress) { itemView, adapterPosition ->
|
||||||
if (tmbItem is Medium) {
|
if (tmbItem is Medium) {
|
||||||
|
@ -197,7 +184,6 @@ class MediaAdapter(
|
||||||
super.onViewRecycled(holder)
|
super.onViewRecycled(holder)
|
||||||
if (!activity.isDestroyed) {
|
if (!activity.isDestroyed) {
|
||||||
val itemView = holder.itemView
|
val itemView = holder.itemView
|
||||||
visibleItemPaths.remove(itemView.allViews.firstOrNull { it.id == R.id.medium_name }?.tag)
|
|
||||||
val tmb = itemView.allViews.firstOrNull { it.id == R.id.medium_thumbnail }
|
val tmb = itemView.allViews.firstOrNull { it.id == R.id.medium_thumbnail }
|
||||||
if (tmb != null) {
|
if (tmb != null) {
|
||||||
Glide.with(activity).clear(tmb)
|
Glide.with(activity).clear(tmb)
|
||||||
|
@ -254,7 +240,6 @@ class MediaAdapter(
|
||||||
activity.updateDBMediaPath(firstPath, it)
|
activity.updateDBMediaPath(firstPath, it)
|
||||||
|
|
||||||
activity.runOnUiThread {
|
activity.runOnUiThread {
|
||||||
enableInstantLoad()
|
|
||||||
listener?.refreshItems()
|
listener?.refreshItems()
|
||||||
finishActMode()
|
finishActMode()
|
||||||
}
|
}
|
||||||
|
@ -262,7 +247,6 @@ class MediaAdapter(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RenameDialog(activity, getSelectedPaths(), true) {
|
RenameDialog(activity, getSelectedPaths(), true) {
|
||||||
enableInstantLoad()
|
|
||||||
listener?.refreshItems()
|
listener?.refreshItems()
|
||||||
finishActMode()
|
finishActMode()
|
||||||
}
|
}
|
||||||
|
@ -572,7 +556,6 @@ class MediaAdapter(
|
||||||
if (thumbnailItems.hashCode() != currentMediaHash) {
|
if (thumbnailItems.hashCode() != currentMediaHash) {
|
||||||
currentMediaHash = thumbnailItems.hashCode()
|
currentMediaHash = thumbnailItems.hashCode()
|
||||||
media = thumbnailItems
|
media = thumbnailItems
|
||||||
enableInstantLoad()
|
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
finishActMode()
|
finishActMode()
|
||||||
}
|
}
|
||||||
|
@ -580,7 +563,6 @@ class MediaAdapter(
|
||||||
|
|
||||||
fun updateDisplayFilenames(displayFilenames: Boolean) {
|
fun updateDisplayFilenames(displayFilenames: Boolean) {
|
||||||
this.displayFilenames = displayFilenames
|
this.displayFilenames = displayFilenames
|
||||||
enableInstantLoad()
|
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,13 +581,6 @@ class MediaAdapter(
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableInstantLoad() {
|
|
||||||
loadImageInstantly = true
|
|
||||||
delayHandler.postDelayed({
|
|
||||||
loadImageInstantly = false
|
|
||||||
}, INSTANT_LOAD_DURATION)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupThumbnail(view: View, medium: Medium) {
|
private fun setupThumbnail(view: View, medium: Medium) {
|
||||||
val isSelected = selectedKeys.contains(medium.path.hashCode())
|
val isSelected = selectedKeys.contains(medium.path.hashCode())
|
||||||
bindItem(view, medium).apply {
|
bindItem(view, medium).apply {
|
||||||
|
@ -681,46 +656,21 @@ class MediaAdapter(
|
||||||
else -> ROUNDED_CORNERS_NONE
|
else -> ROUNDED_CORNERS_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadImageInstantly) {
|
activity.loadImage(
|
||||||
activity.loadImage(
|
type = medium.type,
|
||||||
type = medium.type,
|
path = path,
|
||||||
path = path,
|
target = mediumThumbnail,
|
||||||
target = mediumThumbnail,
|
horizontalScroll = scrollHorizontally,
|
||||||
horizontalScroll = scrollHorizontally,
|
animateGifs = animateGifs,
|
||||||
animateGifs = animateGifs,
|
cropThumbnails = cropThumbnails,
|
||||||
cropThumbnails = cropThumbnails,
|
roundCorners = roundedCorners,
|
||||||
roundCorners = roundedCorners,
|
signature = medium.getKey(),
|
||||||
signature = medium.getKey(),
|
skipMemoryCacheAtPaths = rotatedImagePaths,
|
||||||
skipMemoryCacheAtPaths = rotatedImagePaths,
|
onError = {
|
||||||
onError = {
|
mediumThumbnail.scaleType = ImageView.ScaleType.CENTER
|
||||||
mediumThumbnail.scaleType = ImageView.ScaleType.CENTER
|
mediumThumbnail.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_vector_warning_colored))
|
||||||
mediumThumbnail.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_vector_warning_colored))
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
mediumThumbnail.setImageDrawable(null)
|
|
||||||
mediumThumbnail.isHorizontalScrolling = scrollHorizontally
|
|
||||||
delayHandler.postDelayed({
|
|
||||||
val isVisible = visibleItemPaths.contains(medium.path)
|
|
||||||
if (isVisible) {
|
|
||||||
activity.loadImage(
|
|
||||||
type = medium.type,
|
|
||||||
path = path,
|
|
||||||
target = mediumThumbnail,
|
|
||||||
horizontalScroll = scrollHorizontally,
|
|
||||||
animateGifs = animateGifs,
|
|
||||||
cropThumbnails = cropThumbnails,
|
|
||||||
roundCorners = roundedCorners,
|
|
||||||
signature = medium.getKey(),
|
|
||||||
skipMemoryCacheAtPaths = rotatedImagePaths,
|
|
||||||
onError = {
|
|
||||||
mediumThumbnail.scaleType = ImageView.ScaleType.CENTER
|
|
||||||
mediumThumbnail.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_vector_warning_colored))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}, IMAGE_LOAD_DELAY)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isListViewType) {
|
if (isListViewType) {
|
||||||
mediumName.setTextColor(textColor)
|
mediumName.setTextColor(textColor)
|
||||||
|
|
|
@ -28,7 +28,6 @@ import com.bumptech.glide.load.engine.GlideException
|
||||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||||
import com.bumptech.glide.load.resource.bitmap.FitCenter
|
import com.bumptech.glide.load.resource.bitmap.FitCenter
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
@ -586,7 +585,7 @@ fun Context.loadImageBase(
|
||||||
.load(path)
|
.load(path)
|
||||||
.apply(options)
|
.apply(options)
|
||||||
.set(WebpDownsampler.USE_SYSTEM_DECODER, false) // CVE-2023-4863
|
.set(WebpDownsampler.USE_SYSTEM_DECODER, false) // CVE-2023-4863
|
||||||
.transition(DrawableTransitionOptions.withCrossFade(crossFadeDuration))
|
.transition(getOptionalCrossFadeTransition(crossFadeDuration))
|
||||||
|
|
||||||
builder = builder.listener(object : RequestListener<Drawable> {
|
builder = builder.listener(object : RequestListener<Drawable> {
|
||||||
override fun onLoadFailed(e: GlideException?, model: Any?, targetBitmap: Target<Drawable>, isFirstResource: Boolean): Boolean {
|
override fun onLoadFailed(e: GlideException?, model: Any?, targetBitmap: Target<Drawable>, isFirstResource: Boolean): Boolean {
|
||||||
|
@ -627,7 +626,7 @@ fun Context.loadSVG(
|
||||||
.listener(SvgSoftwareLayerSetter())
|
.listener(SvgSoftwareLayerSetter())
|
||||||
.load(path)
|
.load(path)
|
||||||
.apply(options)
|
.apply(options)
|
||||||
.transition(DrawableTransitionOptions.withCrossFade(crossFadeDuration))
|
.transition(getOptionalCrossFadeTransition(crossFadeDuration))
|
||||||
|
|
||||||
if (roundCorners != ROUNDED_CORNERS_NONE) {
|
if (roundCorners != ROUNDED_CORNERS_NONE) {
|
||||||
val cornerSize =
|
val cornerSize =
|
||||||
|
|
19
app/src/main/kotlin/org/fossify/gallery/extensions/Glide.kt
Normal file
19
app/src/main/kotlin/org/fossify/gallery/extensions/Glide.kt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package org.fossify.gallery.extensions
|
||||||
|
|
||||||
|
import com.bumptech.glide.load.DataSource
|
||||||
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||||
|
import com.bumptech.glide.request.transition.DrawableCrossFadeFactory
|
||||||
|
import com.bumptech.glide.request.transition.TransitionFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cross fade transition option that disabled fading when loading from cache.
|
||||||
|
*/
|
||||||
|
fun getOptionalCrossFadeTransition(duration: Int): DrawableTransitionOptions {
|
||||||
|
return DrawableTransitionOptions.with(
|
||||||
|
TransitionFactory { dataSource, isFirstResource ->
|
||||||
|
if (dataSource == DataSource.RESOURCE_DISK_CACHE) return@TransitionFactory null
|
||||||
|
DrawableCrossFadeFactory.Builder(duration).build().build(dataSource, isFirstResource)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue