Cleanup ThumbnailView

This commit is contained in:
andrew 2023-08-10 10:29:22 +09:30
parent 67fb05f534
commit 4a403d4c16
6 changed files with 95 additions and 107 deletions

View File

@ -114,7 +114,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment()); Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment());
if (slide != null) { if (slide != null) {
thumbnailView.setImageResource(glideRequests, slide, false, null); thumbnailView.setImageResource(glideRequests, slide, false);
} }
thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord));

View File

@ -105,7 +105,7 @@ class AlbumThumbnailView : RelativeLayout {
// iterate binding // iterate binding
slides.take(MAX_ALBUM_DISPLAY_SIZE).forEachIndexed { position, slide -> slides.take(MAX_ALBUM_DISPLAY_SIZE).forEachIndexed { position, slide ->
val thumbnailView = getThumbnailView(position) val thumbnailView = getThumbnailView(position)
thumbnailView.setImageResource(glideRequests, slide, isPreview = false, mms = message) thumbnailView.setImageResource(glideRequests, slide, isPreview = false)
} }
} }

View File

@ -34,7 +34,7 @@ class LinkPreviewDraftView : LinearLayout {
binding.thumbnailImageView.root.radius = toPx(4, resources) binding.thumbnailImageView.root.radius = toPx(4, resources)
if (linkPreview.getThumbnail().isPresent) { if (linkPreview.getThumbnail().isPresent) {
// This internally fetches the thumbnail // This internally fetches the thumbnail
binding.thumbnailImageView.root.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), false, null) binding.thumbnailImageView.root.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), false)
} }
binding.linkPreviewDraftTitleTextView.text = linkPreview.title binding.linkPreviewDraftTitleTextView.text = linkPreview.title
} }

View File

@ -41,7 +41,7 @@ class LinkPreviewView : LinearLayout {
// Thumbnail // Thumbnail
if (linkPreview.getThumbnail().isPresent) { if (linkPreview.getThumbnail().isPresent) {
// This internally fetches the thumbnail // This internally fetches the thumbnail
binding.thumbnailImageView.root.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), isPreview = false, message) binding.thumbnailImageView.root.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), isPreview = false)
binding.thumbnailImageView.root.loadIndicator.isVisible = false binding.thumbnailImageView.root.loadIndicator.isVisible = false
} }
// Title // Title

View File

@ -109,7 +109,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
val slide = attachments.thumbnailSlide!! val slide = attachments.thumbnailSlide!!
// This internally fetches the thumbnail // This internally fetches the thumbnail
binding.quoteViewAttachmentThumbnailImageView.root.radius = toPx(4, resources) binding.quoteViewAttachmentThumbnailImageView.root.radius = toPx(4, resources)
binding.quoteViewAttachmentThumbnailImageView.root.setImageResource(glide, slide, false, null) binding.quoteViewAttachmentThumbnailImageView.root.setImageResource(glide, slide, false)
binding.quoteViewAttachmentThumbnailImageView.root.isVisible = true binding.quoteViewAttachmentThumbnailImageView.root.isVisible = true
binding.quoteViewBodyTextView.text = if (MediaUtil.isVideo(slide.asAttachment())) resources.getString(R.string.Slide_video) else resources.getString(R.string.Slide_image) binding.quoteViewBodyTextView.text = if (MediaUtil.isVideo(slide.asAttachment())) resources.getString(R.string.Slide_video) else resources.getString(R.string.Slide_image)
} }

View File

@ -21,18 +21,17 @@ import org.session.libsignal.utilities.ListenableFuture
import org.session.libsignal.utilities.SettableFuture import org.session.libsignal.utilities.SettableFuture
import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
import org.thoughtcrime.securesms.mms.GlideRequest import org.thoughtcrime.securesms.mms.GlideRequest
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.Slide
import kotlin.Boolean
import kotlin.Int
import kotlin.getValue
import kotlin.lazy
import kotlin.let
open class ThumbnailView: FrameLayout { open class ThumbnailView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
companion object { companion object {
private const val WIDTH = 0 private const val WIDTH = 0
private const val HEIGHT = 1 private const val HEIGHT = 1
@ -41,9 +40,6 @@ open class ThumbnailView: FrameLayout {
private val binding: ThumbnailViewBinding by lazy { ThumbnailViewBinding.bind(this) } private val binding: ThumbnailViewBinding by lazy { ThumbnailViewBinding.bind(this) }
// region Lifecycle // region Lifecycle
constructor(context: Context) : super(context) { initialize(null) }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize(attrs) }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize(attrs) }
val loadIndicator: View by lazy { binding.thumbnailLoadIndicator } val loadIndicator: View by lazy { binding.thumbnailLoadIndicator }
@ -52,19 +48,20 @@ open class ThumbnailView: FrameLayout {
private var slide: Slide? = null private var slide: Slide? = null
var radius: Int = 0 var radius: Int = 0
private fun initialize(attrs: AttributeSet?) { init {
if (attrs != null) { attrs?.let { context.theme.obtainStyledAttributes(it, R.styleable.ThumbnailView, 0, 0) }
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0) ?.apply {
dimensDelegate.setBounds(
getDimensionPixelSize(R.styleable.ThumbnailView_minWidth, 0),
getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0),
getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0),
getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0)
)
dimensDelegate.setBounds(typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minWidth, 0), radius = getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, 0)
typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0),
typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0),
typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0))
radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, 0) recycle()
}
typedArray.recycle()
}
} }
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@ -87,113 +84,104 @@ open class ThumbnailView: FrameLayout {
// endregion // endregion
// region Interaction // region Interaction
fun setImageResource(glide: GlideRequests, slide: Slide, isPreview: Boolean, mms: MmsMessageRecord?): ListenableFuture<Boolean> { fun setImageResource(
return setImageResource(glide, slide, isPreview, 0, 0, mms) glide: GlideRequests,
} slide: Slide,
isPreview: Boolean
fun setImageResource(glide: GlideRequests, slide: Slide, ): ListenableFuture<Boolean> = setImageResource(glide, slide, isPreview, 0, 0)
isPreview: Boolean, naturalWidth: Int,
naturalHeight: Int, mms: MmsMessageRecord?): ListenableFuture<Boolean> {
val currentSlide = this.slide
fun setImageResource(
glide: GlideRequests, slide: Slide,
isPreview: Boolean, naturalWidth: Int,
naturalHeight: Int
): ListenableFuture<Boolean> {
binding.playOverlay.isVisible = (slide.thumbnailUri != null && slide.hasPlayOverlay() && binding.playOverlay.isVisible = (slide.thumbnailUri != null && slide.hasPlayOverlay() &&
(slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_DONE || isPreview)) (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_DONE || isPreview))
if (equals(currentSlide, slide)) { if (equals(this.slide, slide)) {
// don't re-load slide // don't re-load slide
return SettableFuture(false) return SettableFuture(false)
} }
if (currentSlide != null && currentSlide.fastPreflightId != null && currentSlide.fastPreflightId == slide.fastPreflightId) {
// not reloading slide for fast preflight
this.slide = slide
}
this.slide = slide this.slide = slide
binding.thumbnailLoadIndicator.isVisible = slide.isInProgress binding.thumbnailLoadIndicator.isVisible = slide.isInProgress
binding.thumbnailDownloadIcon.isVisible = slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED binding.thumbnailDownloadIcon.isVisible =
slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED
dimensDelegate.setDimens(naturalWidth, naturalHeight) dimensDelegate.setDimens(naturalWidth, naturalHeight)
invalidate() invalidate()
val result = SettableFuture<Boolean>() return SettableFuture<Boolean>().also {
when {
when { slide.thumbnailUri != null -> {
slide.thumbnailUri != null -> { buildThumbnailGlideRequest(glide, slide).into(
buildThumbnailGlideRequest(glide, slide).into(GlideDrawableListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, result)) GlideDrawableListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, it)
} )
slide.hasPlaceholder() -> { }
buildPlaceholderGlideRequest(glide, slide).into(GlideBitmapListeningTarget(binding.thumbnailImage, null, result)) slide.hasPlaceholder() -> {
} buildPlaceholderGlideRequest(glide, slide).into(
else -> { GlideBitmapListeningTarget(binding.thumbnailImage, null, it)
glide.clear(binding.thumbnailImage) )
result.set(false) }
else -> {
glide.clear(binding.thumbnailImage)
it.set(false)
}
} }
} }
return result
} }
private fun buildThumbnailGlideRequest(
glide: GlideRequests,
slide: Slide
): GlideRequest<Drawable> = glide.load(DecryptableUri(slide.thumbnailUri!!))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.overrideDimensions()
.transition(DrawableTransitionOptions.withCrossFade())
.crop(radius)
.missingThumbnailPicture(slide.isInProgress)
private fun buildPlaceholderGlideRequest(
private fun buildThumbnailGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest<Drawable> = glide: GlideRequests,
glide.load(DecryptableUri(slide.thumbnailUri!!)) slide: Slide
.diskCacheStrategy(DiskCacheStrategy.NONE) ): GlideRequest<Bitmap> = glide.asBitmap()
.run { .load(slide.getPlaceholderRes(context.theme))
val dimens = dimensDelegate.resourceSize() .diskCacheStrategy(DiskCacheStrategy.NONE)
if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) { .overrideDimensions()
override(getDefaultWidth(), getDefaultHeight()) .fitCenter()
} else {
override(dimens[WIDTH], dimens[HEIGHT])
}
}
.transition(DrawableTransitionOptions.withCrossFade())
.run {
listOfNotNull(CenterCrop(), radius.takeIf { it > 0 }?.let(::RoundedCorners))
.toTypedArray().let(::transforms)
}.run {
takeIf { slide.isInProgress } ?: apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture))
}
fun buildPlaceholderGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest<Bitmap> {
val dimens = dimensDelegate.resourceSize()
return glide.asBitmap()
.load(slide.getPlaceholderRes(context.theme))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.let { request ->
if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) {
request.override(getDefaultWidth(), getDefaultHeight())
} else {
request.override(dimens[WIDTH], dimens[HEIGHT])
}
}
.fitCenter()
}
open fun clear(glideRequests: GlideRequests) { open fun clear(glideRequests: GlideRequests) {
glideRequests.clear(binding.thumbnailImage) glideRequests.clear(binding.thumbnailImage)
slide = null slide = null
} }
fun setImageResource(glideRequests: GlideRequests, uri: Uri): ListenableFuture<Boolean> { fun setImageResource(
val future = SettableFuture<Boolean>() glideRequests: GlideRequests,
uri: Uri
): ListenableFuture<Boolean> = glideRequests.load(DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transition(DrawableTransitionOptions.withCrossFade())
.crop(radius)
.intoDrawableTargetAsFuture()
var request: GlideRequest<Drawable> = glideRequests.load(DecryptableUri(uri)) private fun GlideRequest<Drawable>.intoDrawableTargetAsFuture() =
.diskCacheStrategy(DiskCacheStrategy.NONE) SettableFuture<Boolean>().also {
.transition(DrawableTransitionOptions.withCrossFade()) binding.run {
GlideDrawableListeningTarget(thumbnailImage, thumbnailLoadIndicator, it)
request = if (radius > 0) { }.let { into(it) }
request.transforms(CenterCrop(), RoundedCorners(radius))
} else {
request.transforms(CenterCrop())
} }
request.into(GlideDrawableListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, future)) private fun <T> GlideRequest<T>.overrideDimensions() =
dimensDelegate.resourceSize().takeIf { 0 !in it }
?.let { override(it[WIDTH], it[HEIGHT]) }
?: override(getDefaultWidth(), getDefaultHeight())
return future private fun <T> GlideRequest<T>.crop(radius: Int) =
} if (radius > 0) transforms(CenterCrop(), RoundedCorners(radius))
} else transforms(CenterCrop())
}
private fun <T> GlideRequest<T>.missingThumbnailPicture(
inProgress: Boolean
) = takeIf { inProgress } ?: apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture))