From 1d5d1473373fef766d62298a7ea7c4db15d685bf Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 1 Aug 2023 01:16:14 +0930 Subject: [PATCH 1/3] Add rounded corners to thumbnail in QuoteView --- .../securesms/conversation/v2/utilities/ThumbnailView.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt index 4a9986d6e..a8860389f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt @@ -140,7 +140,7 @@ open class ThumbnailView: FrameLayout { val dimens = dimensDelegate.resourceSize() - val request = glide.load(DecryptableUri(slide.thumbnailUri!!)) + var request = glide.load(DecryptableUri(slide.thumbnailUri!!)) .diskCacheStrategy(DiskCacheStrategy.NONE) .let { request -> if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) { @@ -150,7 +150,12 @@ open class ThumbnailView: FrameLayout { } } .transition(DrawableTransitionOptions.withCrossFade()) - .centerCrop() + + request = if (radius > 0) { + request.transforms(CenterCrop(), RoundedCorners(radius)) + } else { + request.transforms(CenterCrop()) + } return if (slide.isInProgress) request else request.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture)) } From 67fb05f534201c72ace125e27a1a9c9c6985224d Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 9 Aug 2023 18:59:43 +0930 Subject: [PATCH 2/3] Simplify ThumbnailView --- .../v2/utilities/ThumbnailView.kt | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt index a8860389f..aa41512b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt @@ -136,29 +136,26 @@ open class ThumbnailView: FrameLayout { return result } - fun buildThumbnailGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest { - val dimens = dimensDelegate.resourceSize() - var request = glide.load(DecryptableUri(slide.thumbnailUri!!)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .let { request -> - if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) { - request.override(getDefaultWidth(), getDefaultHeight()) - } else { - request.override(dimens[WIDTH], dimens[HEIGHT]) - } + private fun buildThumbnailGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest = + glide.load(DecryptableUri(slide.thumbnailUri!!)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .run { + val dimens = dimensDelegate.resourceSize() + if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) { + override(getDefaultWidth(), getDefaultHeight()) + } else { + override(dimens[WIDTH], dimens[HEIGHT]) } - .transition(DrawableTransitionOptions.withCrossFade()) - - request = if (radius > 0) { - request.transforms(CenterCrop(), RoundedCorners(radius)) - } else { - request.transforms(CenterCrop()) - } - - return if (slide.isInProgress) request else request.apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture)) - } + } + .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 { From 4a403d4c16e6b2058d9538d6a77fcd0aafcdb504 Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 10 Aug 2023 10:29:22 +0930 Subject: [PATCH 3/3] Cleanup ThumbnailView --- .../securesms/MediaGalleryAdapter.java | 2 +- .../v2/components/AlbumThumbnailView.kt | 2 +- .../v2/components/LinkPreviewDraftView.kt | 2 +- .../v2/messages/LinkPreviewView.kt | 2 +- .../conversation/v2/messages/QuoteView.kt | 2 +- .../v2/utilities/ThumbnailView.kt | 192 ++++++++---------- 6 files changed, 95 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java index 0fd813cf4..62766d1cd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaGalleryAdapter.java @@ -114,7 +114,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter { Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment()); if (slide != null) { - thumbnailView.setImageResource(glideRequests, slide, false, null); + thumbnailView.setImageResource(glideRequests, slide, false); } thumbnailView.setOnClickListener(view -> itemClickListener.onMediaClicked(mediaRecord)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt index 330534e23..78c465e9d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/AlbumThumbnailView.kt @@ -105,7 +105,7 @@ class AlbumThumbnailView : RelativeLayout { // iterate binding slides.take(MAX_ALBUM_DISPLAY_SIZE).forEachIndexed { position, slide -> val thumbnailView = getThumbnailView(position) - thumbnailView.setImageResource(glideRequests, slide, isPreview = false, mms = message) + thumbnailView.setImageResource(glideRequests, slide, isPreview = false) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt index 66164f100..33af3588d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/components/LinkPreviewDraftView.kt @@ -34,7 +34,7 @@ class LinkPreviewDraftView : LinearLayout { binding.thumbnailImageView.root.radius = toPx(4, resources) if (linkPreview.getThumbnail().isPresent) { // 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 } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt index 967722389..4e6066edb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt @@ -41,7 +41,7 @@ class LinkPreviewView : LinearLayout { // Thumbnail if (linkPreview.getThumbnail().isPresent) { // 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 } // Title diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt index 4e9140043..ddd8fb94d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/QuoteView.kt @@ -109,7 +109,7 @@ class QuoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? val slide = attachments.thumbnailSlide!! // This internally fetches the thumbnail 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.quoteViewBodyTextView.text = if (MediaUtil.isVideo(slide.asAttachment())) resources.getString(R.string.Slide_video) else resources.getString(R.string.Slide_image) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt index aa41512b7..ed9b1a30f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.kt @@ -21,18 +21,17 @@ import org.session.libsignal.utilities.ListenableFuture import org.session.libsignal.utilities.SettableFuture import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget 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.GlideRequest import org.thoughtcrime.securesms.mms.GlideRequests 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 { private const val WIDTH = 0 private const val HEIGHT = 1 @@ -41,9 +40,6 @@ open class ThumbnailView: FrameLayout { private val binding: ThumbnailViewBinding by lazy { ThumbnailViewBinding.bind(this) } // 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 } @@ -52,19 +48,20 @@ open class ThumbnailView: FrameLayout { private var slide: Slide? = null var radius: Int = 0 - private fun initialize(attrs: AttributeSet?) { - if (attrs != null) { - val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0) + init { + attrs?.let { context.theme.obtainStyledAttributes(it, 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), - typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0), - typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0), - typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0)) + radius = getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, 0) - radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, 0) - - typedArray.recycle() - } + recycle() + } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { @@ -87,113 +84,104 @@ open class ThumbnailView: FrameLayout { // endregion // region Interaction - fun setImageResource(glide: GlideRequests, slide: Slide, isPreview: Boolean, mms: MmsMessageRecord?): ListenableFuture { - return setImageResource(glide, slide, isPreview, 0, 0, mms) - } - - fun setImageResource(glide: GlideRequests, slide: Slide, - isPreview: Boolean, naturalWidth: Int, - naturalHeight: Int, mms: MmsMessageRecord?): ListenableFuture { - - val currentSlide = this.slide + fun setImageResource( + glide: GlideRequests, + slide: Slide, + isPreview: Boolean + ): ListenableFuture = setImageResource(glide, slide, isPreview, 0, 0) + fun setImageResource( + glide: GlideRequests, slide: Slide, + isPreview: Boolean, naturalWidth: Int, + naturalHeight: Int + ): ListenableFuture { 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 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 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) invalidate() - val result = SettableFuture() - - when { - slide.thumbnailUri != null -> { - buildThumbnailGlideRequest(glide, slide).into(GlideDrawableListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, result)) - } - slide.hasPlaceholder() -> { - buildPlaceholderGlideRequest(glide, slide).into(GlideBitmapListeningTarget(binding.thumbnailImage, null, result)) - } - else -> { - glide.clear(binding.thumbnailImage) - result.set(false) + return SettableFuture().also { + when { + slide.thumbnailUri != null -> { + buildThumbnailGlideRequest(glide, slide).into( + GlideDrawableListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, it) + ) + } + slide.hasPlaceholder() -> { + buildPlaceholderGlideRequest(glide, slide).into( + GlideBitmapListeningTarget(binding.thumbnailImage, null, it) + ) + } + else -> { + glide.clear(binding.thumbnailImage) + it.set(false) + } } } - return result } + private fun buildThumbnailGlideRequest( + glide: GlideRequests, + slide: Slide + ): GlideRequest = glide.load(DecryptableUri(slide.thumbnailUri!!)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .overrideDimensions() + .transition(DrawableTransitionOptions.withCrossFade()) + .crop(radius) + .missingThumbnailPicture(slide.isInProgress) - - private fun buildThumbnailGlideRequest(glide: GlideRequests, slide: Slide): GlideRequest = - glide.load(DecryptableUri(slide.thumbnailUri!!)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .run { - val dimens = dimensDelegate.resourceSize() - if (dimens[WIDTH] == 0 || dimens[HEIGHT] == 0) { - override(getDefaultWidth(), getDefaultHeight()) - } 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 { - - 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() - } + private fun buildPlaceholderGlideRequest( + glide: GlideRequests, + slide: Slide + ): GlideRequest = glide.asBitmap() + .load(slide.getPlaceholderRes(context.theme)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .overrideDimensions() + .fitCenter() open fun clear(glideRequests: GlideRequests) { glideRequests.clear(binding.thumbnailImage) slide = null } - fun setImageResource(glideRequests: GlideRequests, uri: Uri): ListenableFuture { - val future = SettableFuture() + fun setImageResource( + glideRequests: GlideRequests, + uri: Uri + ): ListenableFuture = glideRequests.load(DecryptableUri(uri)) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .transition(DrawableTransitionOptions.withCrossFade()) + .crop(radius) + .intoDrawableTargetAsFuture() - var request: GlideRequest = glideRequests.load(DecryptableUri(uri)) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .transition(DrawableTransitionOptions.withCrossFade()) - - request = if (radius > 0) { - request.transforms(CenterCrop(), RoundedCorners(radius)) - } else { - request.transforms(CenterCrop()) + private fun GlideRequest.intoDrawableTargetAsFuture() = + SettableFuture().also { + binding.run { + GlideDrawableListeningTarget(thumbnailImage, thumbnailLoadIndicator, it) + }.let { into(it) } } - request.into(GlideDrawableListeningTarget(binding.thumbnailImage, binding.thumbnailLoadIndicator, future)) + private fun GlideRequest.overrideDimensions() = + dimensDelegate.resourceSize().takeIf { 0 !in it } + ?.let { override(it[WIDTH], it[HEIGHT]) } + ?: override(getDefaultWidth(), getDefaultHeight()) - return future - } -} \ No newline at end of file + private fun GlideRequest.crop(radius: Int) = + if (radius > 0) transforms(CenterCrop(), RoundedCorners(radius)) + else transforms(CenterCrop()) +} + +private fun GlideRequest.missingThumbnailPicture( + inProgress: Boolean +) = takeIf { inProgress } ?: apply(RequestOptions.errorOf(R.drawable.ic_missing_thumbnail_picture))