From 5cf937215ad488b7c4d6208be65b08a35513b733 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 15 Feb 2023 12:39:48 -0400 Subject: [PATCH] Fix stub of TransferControlsView. --- .../securesms/components/LinkPreviewView.java | 85 ++++++++++++++----- .../LinkPreviewViewThumbnailState.kt | 28 ++++++ .../securesms/components/ThumbnailView.java | 68 ++++++++------- .../ThumbnailViewTransferControlsState.kt | 35 ++++++++ .../components/TransferControlView.java | 2 +- app/src/main/res/layout/link_preview.xml | 13 ++- .../layout/link_preview_thumbnail_stub.xml | 16 ++++ 7 files changed, 188 insertions(+), 59 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt create mode 100644 app/src/main/res/layout/link_preview_thumbnail_stub.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java index 242bdde2a2..e2990df284 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewView.java @@ -4,6 +4,8 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; +import android.os.Bundle; +import android.os.Parcelable; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -22,6 +24,7 @@ import org.thoughtcrime.securesms.mms.ImageSlide; import org.thoughtcrime.securesms.mms.SlidesClickedListener; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; +import org.thoughtcrime.securesms.util.views.Stub; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -34,23 +37,27 @@ import okhttp3.HttpUrl; */ public class LinkPreviewView extends FrameLayout { + private static final String STATE_ROOT = "linkPreviewView.state.root"; + private static final String STATE_STATE = "linkPreviewView.state.state"; + private static final int TYPE_CONVERSATION = 0; private static final int TYPE_COMPOSE = 1; - private ViewGroup container; - private OutlinedThumbnailView thumbnail; - private TextView title; - private TextView description; - private TextView site; - private View divider; - private View closeButton; - private View spinner; - private TextView noPreview; + private ViewGroup container; + private Stub thumbnail; + private TextView title; + private TextView description; + private TextView site; + private View divider; + private View closeButton; + private View spinner; + private TextView noPreview; - private int type; - private int defaultRadius; - private CornerMask cornerMask; - private CloseClickedListener closeClickedListener; + private int type; + private int defaultRadius; + private CornerMask cornerMask; + private CloseClickedListener closeClickedListener; + private LinkPreviewViewThumbnailState thumbnailState = new LinkPreviewViewThumbnailState(); public LinkPreviewView(Context context) { super(context); @@ -66,7 +73,7 @@ public class LinkPreviewView extends FrameLayout { inflate(getContext(), R.layout.link_preview, this); container = findViewById(R.id.linkpreview_container); - thumbnail = findViewById(R.id.linkpreview_thumbnail); + thumbnail = new Stub<>(findViewById(R.id.linkpreview_thumbnail)); title = findViewById(R.id.linkpreview_title); description = findViewById(R.id.linkpreview_description); site = findViewById(R.id.linkpreview_site); @@ -101,6 +108,30 @@ public class LinkPreviewView extends FrameLayout { setWillNotDraw(false); } + @Override + protected @NonNull Parcelable onSaveInstanceState() { + Parcelable root = super.onSaveInstanceState(); + Bundle bundle = new Bundle(); + + bundle.putParcelable(STATE_ROOT, root); + bundle.putParcelable(STATE_STATE, thumbnailState); + + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Parcelable root = ((Bundle) state).getParcelable(STATE_ROOT); + thumbnailState = ((Bundle) state).getParcelable(STATE_STATE); + + thumbnailState.applyState(thumbnail); + super.onRestoreInstanceState(root); + } else { + super.onRestoreInstanceState(state); + } + } + @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -173,8 +204,9 @@ public class LinkPreviewView extends FrameLayout { if (showThumbnail && linkPreview.getThumbnail().isPresent()) { thumbnail.setVisibility(VISIBLE); - thumbnail.setImageResource(glideRequests, new ImageSlide(getContext(), linkPreview.getThumbnail().get()), type == TYPE_CONVERSATION, false); - thumbnail.showDownloadText(false); + thumbnailState.applyState(thumbnail); + thumbnail.get().setImageResource(glideRequests, new ImageSlide(getContext(), linkPreview.getThumbnail().get()), type == TYPE_CONVERSATION, false); + thumbnail.get().showDownloadText(false); } else { thumbnail.setVisibility(GONE); } @@ -183,10 +215,24 @@ public class LinkPreviewView extends FrameLayout { public void setCorners(int topStart, int topEnd) { if (ViewUtil.isRtl(this)) { cornerMask.setRadii(topEnd, topStart, 0, 0); - thumbnail.setCorners(defaultRadius, topEnd, defaultRadius, defaultRadius); + thumbnailState = thumbnailState.copy( + defaultRadius, + topEnd, + defaultRadius, + defaultRadius, + thumbnailState.getDownloadListener() + ); + thumbnailState.applyState(thumbnail); } else { cornerMask.setRadii(topStart, topEnd, 0, 0); - thumbnail.setCorners(topStart, defaultRadius, defaultRadius, defaultRadius); + thumbnailState.copy( + topStart, + defaultRadius, + defaultRadius, + defaultRadius, + thumbnailState.getDownloadListener() + ); + thumbnailState.applyState(thumbnail); } postInvalidate(); } @@ -196,7 +242,8 @@ public class LinkPreviewView extends FrameLayout { } public void setDownloadClickedListener(SlidesClickedListener listener) { - thumbnail.setDownloadClickListener(listener); + thumbnailState = thumbnailState.withDownloadListener(listener); + thumbnailState.applyState(thumbnail); } private @StringRes static int getLinkPreviewErrorString(@Nullable LinkPreviewRepository.Error customError) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt new file mode 100644 index 0000000000..6f18e4e975 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/LinkPreviewViewThumbnailState.kt @@ -0,0 +1,28 @@ +package org.thoughtcrime.securesms.components + +import android.os.Parcelable +import kotlinx.parcelize.IgnoredOnParcel +import kotlinx.parcelize.Parcelize +import org.thoughtcrime.securesms.mms.SlidesClickedListener +import org.thoughtcrime.securesms.util.views.Stub + +@Parcelize +data class LinkPreviewViewThumbnailState( + val cornerTopLeft: Int = 0, + val cornerTopRight: Int = 0, + val cornerBottomRight: Int = 0, + val cornerBottomLeft: Int = 0, + @IgnoredOnParcel + val downloadListener: SlidesClickedListener? = null +) : Parcelable { + fun withDownloadListener(downloadListener: SlidesClickedListener?): LinkPreviewViewThumbnailState { + return copy(downloadListener = downloadListener) + } + + fun applyState(thumbnail: Stub) { + if (thumbnail.resolved()) { + thumbnail.get().setCorners(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft) + thumbnail.get().setDownloadClickListener(downloadListener) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java index a973daa9fa..20d85b3102 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailView.java @@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; import org.thoughtcrime.securesms.util.concurrent.SettableFuture; +import org.thoughtcrime.securesms.util.views.Stub; import java.util.Collections; import java.util.Locale; @@ -79,11 +80,11 @@ public class ThumbnailView extends FrameLayout { private final CornerMask cornerMask; - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - private Optional transferControls = Optional.empty(); - private SlideClickListener thumbnailClickListener = null; - private SlidesClickedListener downloadClickListener = null; - private Slide slide = null; + private ThumbnailViewTransferControlsState transferControlsState = new ThumbnailViewTransferControlsState(); + private Stub transferControlViewStub; + private SlideClickListener thumbnailClickListener = null; + private SlidesClickedListener downloadClickListener = null; + private Slide slide = null; public ThumbnailView(Context context) { @@ -99,12 +100,13 @@ public class ThumbnailView extends FrameLayout { inflate(context, R.layout.thumbnail_view, this); - this.image = findViewById(R.id.thumbnail_image); - this.blurHash = findViewById(R.id.thumbnail_blurhash); - this.playOverlay = findViewById(R.id.play_overlay); - this.captionIcon = findViewById(R.id.thumbnail_caption_icon); - this.errorImage = findViewById(R.id.thumbnail_error); - this.cornerMask = new CornerMask(this); + this.image = findViewById(R.id.thumbnail_image); + this.blurHash = findViewById(R.id.thumbnail_blurhash); + this.playOverlay = findViewById(R.id.play_overlay); + this.captionIcon = findViewById(R.id.thumbnail_caption_icon); + this.errorImage = findViewById(R.id.thumbnail_error); + this.cornerMask = new CornerMask(this); + this.transferControlViewStub = new Stub<>(findViewById(R.id.transfer_controls_stub)); super.setOnClickListener(new ThumbnailClickDispatcher()); @@ -275,26 +277,21 @@ public class ThumbnailView extends FrameLayout { @Override public void setFocusable(boolean focusable) { super.setFocusable(focusable); - transferControls.ifPresent(transferControlView -> transferControlView.setFocusable(focusable)); + transferControlsState = transferControlsState.withFocusable(focusable); + transferControlsState.applyState(transferControlViewStub); } @Override public void setClickable(boolean clickable) { super.setClickable(clickable); - transferControls.ifPresent(transferControlView -> transferControlView.setClickable(clickable)); + transferControlsState = transferControlsState.withClickable(clickable); + transferControlsState.applyState(transferControlViewStub); } public @Nullable Drawable getImageDrawable() { return image.getDrawable(); } - private TransferControlView getTransferControls() { - if (!transferControls.isPresent()) { - transferControls = Optional.of(ViewUtil.inflateStub(this, R.id.transfer_controls_stub)); - } - return transferControls.get(); - } - public void setBounds(int minWidth, int maxWidth, int minHeight, int maxHeight) { bounds[MIN_WIDTH] = minWidth; bounds[MAX_WIDTH] = maxWidth; @@ -327,7 +324,7 @@ public class ThumbnailView extends FrameLayout { if (slide.asAttachment().isPermanentlyFailed()) { this.slide = slide; - transferControls.ifPresent(c -> c.setVisibility(View.GONE)); + transferControlViewStub.setVisibility(View.GONE); playOverlay.setVisibility(View.GONE); glideRequests.clear(blurHash); @@ -353,10 +350,18 @@ public class ThumbnailView extends FrameLayout { } if (showControls) { - getTransferControls().setSlide(slide); - getTransferControls().setDownloadClickListener(new DownloadClickDispatcher()); - } else if (transferControls.isPresent()) { - getTransferControls().setVisibility(View.GONE); + int transferState = TransferControlView.getTransferState(Collections.singletonList(slide)); + if (transferState == AttachmentTable.TRANSFER_PROGRESS_DONE || transferState == AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE) { + transferControlViewStub.setVisibility(View.GONE); + } else { + transferControlViewStub.setVisibility(View.VISIBLE); + } + + transferControlsState = transferControlsState.withSlide(slide) + .withDownloadClickListener(new DownloadClickDispatcher()); + transferControlsState.applyState(transferControlViewStub); + } else { + transferControlViewStub.setVisibility(View.GONE); } if (slide.getUri() != null && slide.hasPlayOverlay() && @@ -440,7 +445,7 @@ public class ThumbnailView extends FrameLayout { public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri, int width, int height, boolean animate, @Nullable ThumbnailRequestListener listener) { SettableFuture future = new SettableFuture<>(); - if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); + transferControlViewStub.setVisibility(View.GONE); GlideRequest request = glideRequests.load(new DecryptableUri(uri)) .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -473,7 +478,7 @@ public class ThumbnailView extends FrameLayout { public ListenableFuture setImageResource(@NonNull GlideRequests glideRequests, @NonNull StoryTextPostModel model, int width, int height) { SettableFuture future = new SettableFuture<>(); - if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); + transferControlViewStub.setVisibility(View.GONE); GlideRequest request = glideRequests.load(model) .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -502,8 +507,8 @@ public class ThumbnailView extends FrameLayout { glideRequests.clear(image); image.setImageDrawable(null); - if (transferControls.isPresent()) { - getTransferControls().clear(); + if (transferControlViewStub.resolved()) { + transferControlViewStub.get().clear(); } glideRequests.clear(blurHash); @@ -513,11 +518,12 @@ public class ThumbnailView extends FrameLayout { } public void showDownloadText(boolean showDownloadText) { - getTransferControls().setShowDownloadText(showDownloadText); + transferControlsState = transferControlsState.withDownloadText(showDownloadText); + transferControlsState.applyState(transferControlViewStub); } public void showProgressSpinner() { - getTransferControls().showProgressSpinner(); + transferControlViewStub.get().showProgressSpinner(); } public void setScaleType(@NonNull ImageView.ScaleType scaleType) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt new file mode 100644 index 0000000000..9973fee2df --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThumbnailViewTransferControlsState.kt @@ -0,0 +1,35 @@ +package org.thoughtcrime.securesms.components + +import android.view.View.OnClickListener +import org.thoughtcrime.securesms.mms.Slide +import org.thoughtcrime.securesms.util.views.Stub + +/** + * State object for transfer controls. + */ +data class ThumbnailViewTransferControlsState( + val isFocusable: Boolean = true, + val isClickable: Boolean = true, + val slide: Slide? = null, + val downloadClickedListener: OnClickListener? = null, + val showDownloadText: Boolean = true +) { + + fun withFocusable(isFocusable: Boolean): ThumbnailViewTransferControlsState = copy(isFocusable = isFocusable) + fun withClickable(isClickable: Boolean): ThumbnailViewTransferControlsState = copy(isClickable = isClickable) + fun withSlide(slide: Slide?): ThumbnailViewTransferControlsState = copy(slide = slide) + fun withDownloadClickListener(downloadClickedListener: OnClickListener): ThumbnailViewTransferControlsState = copy(downloadClickedListener = downloadClickedListener) + fun withDownloadText(showDownloadText: Boolean): ThumbnailViewTransferControlsState = copy(showDownloadText = showDownloadText) + + fun applyState(transferControlView: Stub) { + if (transferControlView.resolved()) { + transferControlView.get().isFocusable = isFocusable + transferControlView.get().isClickable = isClickable + if (slide != null) { + transferControlView.get().setSlide(slide) + } + transferControlView.get().setDownloadClickListener(downloadClickedListener) + transferControlView.get().setShowDownloadText(showDownloadText) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java b/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java index 0deb3b2a85..9ba9d25484 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/TransferControlView.java @@ -182,7 +182,7 @@ public final class TransferControlView extends FrameLayout { return true; } - private int getTransferState(@NonNull List slides) { + static int getTransferState(@NonNull List slides) { int transferState = AttachmentTable.TRANSFER_PROGRESS_DONE; boolean allFailed = true; diff --git a/app/src/main/res/layout/link_preview.xml b/app/src/main/res/layout/link_preview.xml index b82d90d3e7..c4ff4b6cfe 100644 --- a/app/src/main/res/layout/link_preview.xml +++ b/app/src/main/res/layout/link_preview.xml @@ -1,8 +1,8 @@ + xmlns:tools="http://schemas.android.com/tools" + tools:viewBindingIgnore="true"> - + app:layout_constraintTop_toTopOf="@+id/linkpreview_title" /> + \ No newline at end of file