Fix stub of TransferControlsView.

main
Alex Hart 2023-02-15 12:39:48 -04:00 zatwierdzone przez Greyson Parrelli
rodzic 1b49b9bffb
commit 5cf937215a
7 zmienionych plików z 188 dodań i 59 usunięć

Wyświetl plik

@ -4,6 +4,8 @@ import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -22,6 +24,7 @@ import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.SlidesClickedListener; import org.thoughtcrime.securesms.mms.SlidesClickedListener;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -34,23 +37,27 @@ import okhttp3.HttpUrl;
*/ */
public class LinkPreviewView extends FrameLayout { 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_CONVERSATION = 0;
private static final int TYPE_COMPOSE = 1; private static final int TYPE_COMPOSE = 1;
private ViewGroup container; private ViewGroup container;
private OutlinedThumbnailView thumbnail; private Stub<OutlinedThumbnailView> thumbnail;
private TextView title; private TextView title;
private TextView description; private TextView description;
private TextView site; private TextView site;
private View divider; private View divider;
private View closeButton; private View closeButton;
private View spinner; private View spinner;
private TextView noPreview; private TextView noPreview;
private int type; private int type;
private int defaultRadius; private int defaultRadius;
private CornerMask cornerMask; private CornerMask cornerMask;
private CloseClickedListener closeClickedListener; private CloseClickedListener closeClickedListener;
private LinkPreviewViewThumbnailState thumbnailState = new LinkPreviewViewThumbnailState();
public LinkPreviewView(Context context) { public LinkPreviewView(Context context) {
super(context); super(context);
@ -66,7 +73,7 @@ public class LinkPreviewView extends FrameLayout {
inflate(getContext(), R.layout.link_preview, this); inflate(getContext(), R.layout.link_preview, this);
container = findViewById(R.id.linkpreview_container); 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); title = findViewById(R.id.linkpreview_title);
description = findViewById(R.id.linkpreview_description); description = findViewById(R.id.linkpreview_description);
site = findViewById(R.id.linkpreview_site); site = findViewById(R.id.linkpreview_site);
@ -101,6 +108,30 @@ public class LinkPreviewView extends FrameLayout {
setWillNotDraw(false); 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 @Override
protected void dispatchDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
@ -173,8 +204,9 @@ public class LinkPreviewView extends FrameLayout {
if (showThumbnail && linkPreview.getThumbnail().isPresent()) { if (showThumbnail && linkPreview.getThumbnail().isPresent()) {
thumbnail.setVisibility(VISIBLE); thumbnail.setVisibility(VISIBLE);
thumbnail.setImageResource(glideRequests, new ImageSlide(getContext(), linkPreview.getThumbnail().get()), type == TYPE_CONVERSATION, false); thumbnailState.applyState(thumbnail);
thumbnail.showDownloadText(false); thumbnail.get().setImageResource(glideRequests, new ImageSlide(getContext(), linkPreview.getThumbnail().get()), type == TYPE_CONVERSATION, false);
thumbnail.get().showDownloadText(false);
} else { } else {
thumbnail.setVisibility(GONE); thumbnail.setVisibility(GONE);
} }
@ -183,10 +215,24 @@ public class LinkPreviewView extends FrameLayout {
public void setCorners(int topStart, int topEnd) { public void setCorners(int topStart, int topEnd) {
if (ViewUtil.isRtl(this)) { if (ViewUtil.isRtl(this)) {
cornerMask.setRadii(topEnd, topStart, 0, 0); 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 { } else {
cornerMask.setRadii(topStart, topEnd, 0, 0); cornerMask.setRadii(topStart, topEnd, 0, 0);
thumbnail.setCorners(topStart, defaultRadius, defaultRadius, defaultRadius); thumbnailState.copy(
topStart,
defaultRadius,
defaultRadius,
defaultRadius,
thumbnailState.getDownloadListener()
);
thumbnailState.applyState(thumbnail);
} }
postInvalidate(); postInvalidate();
} }
@ -196,7 +242,8 @@ public class LinkPreviewView extends FrameLayout {
} }
public void setDownloadClickedListener(SlidesClickedListener listener) { public void setDownloadClickedListener(SlidesClickedListener listener) {
thumbnail.setDownloadClickListener(listener); thumbnailState = thumbnailState.withDownloadListener(listener);
thumbnailState.applyState(thumbnail);
} }
private @StringRes static int getLinkPreviewErrorString(@Nullable LinkPreviewRepository.Error customError) { private @StringRes static int getLinkPreviewErrorString(@Nullable LinkPreviewRepository.Error customError) {

Wyświetl plik

@ -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<OutlinedThumbnailView>) {
if (thumbnail.resolved()) {
thumbnail.get().setCorners(cornerTopLeft, cornerTopRight, cornerBottomRight, cornerBottomLeft)
thumbnail.get().setDownloadClickListener(downloadListener)
}
}
}

Wyświetl plik

@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture; import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture; import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.views.Stub;
import java.util.Collections; import java.util.Collections;
import java.util.Locale; import java.util.Locale;
@ -79,11 +80,11 @@ public class ThumbnailView extends FrameLayout {
private final CornerMask cornerMask; private final CornerMask cornerMask;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") private ThumbnailViewTransferControlsState transferControlsState = new ThumbnailViewTransferControlsState();
private Optional<TransferControlView> transferControls = Optional.empty(); private Stub<TransferControlView> transferControlViewStub;
private SlideClickListener thumbnailClickListener = null; private SlideClickListener thumbnailClickListener = null;
private SlidesClickedListener downloadClickListener = null; private SlidesClickedListener downloadClickListener = null;
private Slide slide = null; private Slide slide = null;
public ThumbnailView(Context context) { public ThumbnailView(Context context) {
@ -99,12 +100,13 @@ public class ThumbnailView extends FrameLayout {
inflate(context, R.layout.thumbnail_view, this); inflate(context, R.layout.thumbnail_view, this);
this.image = findViewById(R.id.thumbnail_image); this.image = findViewById(R.id.thumbnail_image);
this.blurHash = findViewById(R.id.thumbnail_blurhash); this.blurHash = findViewById(R.id.thumbnail_blurhash);
this.playOverlay = findViewById(R.id.play_overlay); this.playOverlay = findViewById(R.id.play_overlay);
this.captionIcon = findViewById(R.id.thumbnail_caption_icon); this.captionIcon = findViewById(R.id.thumbnail_caption_icon);
this.errorImage = findViewById(R.id.thumbnail_error); this.errorImage = findViewById(R.id.thumbnail_error);
this.cornerMask = new CornerMask(this); this.cornerMask = new CornerMask(this);
this.transferControlViewStub = new Stub<>(findViewById(R.id.transfer_controls_stub));
super.setOnClickListener(new ThumbnailClickDispatcher()); super.setOnClickListener(new ThumbnailClickDispatcher());
@ -275,26 +277,21 @@ public class ThumbnailView extends FrameLayout {
@Override @Override
public void setFocusable(boolean focusable) { public void setFocusable(boolean focusable) {
super.setFocusable(focusable); super.setFocusable(focusable);
transferControls.ifPresent(transferControlView -> transferControlView.setFocusable(focusable)); transferControlsState = transferControlsState.withFocusable(focusable);
transferControlsState.applyState(transferControlViewStub);
} }
@Override @Override
public void setClickable(boolean clickable) { public void setClickable(boolean clickable) {
super.setClickable(clickable); super.setClickable(clickable);
transferControls.ifPresent(transferControlView -> transferControlView.setClickable(clickable)); transferControlsState = transferControlsState.withClickable(clickable);
transferControlsState.applyState(transferControlViewStub);
} }
public @Nullable Drawable getImageDrawable() { public @Nullable Drawable getImageDrawable() {
return image.getDrawable(); 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) { public void setBounds(int minWidth, int maxWidth, int minHeight, int maxHeight) {
bounds[MIN_WIDTH] = minWidth; bounds[MIN_WIDTH] = minWidth;
bounds[MAX_WIDTH] = maxWidth; bounds[MAX_WIDTH] = maxWidth;
@ -327,7 +324,7 @@ public class ThumbnailView extends FrameLayout {
if (slide.asAttachment().isPermanentlyFailed()) { if (slide.asAttachment().isPermanentlyFailed()) {
this.slide = slide; this.slide = slide;
transferControls.ifPresent(c -> c.setVisibility(View.GONE)); transferControlViewStub.setVisibility(View.GONE);
playOverlay.setVisibility(View.GONE); playOverlay.setVisibility(View.GONE);
glideRequests.clear(blurHash); glideRequests.clear(blurHash);
@ -353,10 +350,18 @@ public class ThumbnailView extends FrameLayout {
} }
if (showControls) { if (showControls) {
getTransferControls().setSlide(slide); int transferState = TransferControlView.getTransferState(Collections.singletonList(slide));
getTransferControls().setDownloadClickListener(new DownloadClickDispatcher()); if (transferState == AttachmentTable.TRANSFER_PROGRESS_DONE || transferState == AttachmentTable.TRANSFER_PROGRESS_PERMANENT_FAILURE) {
} else if (transferControls.isPresent()) { transferControlViewStub.setVisibility(View.GONE);
getTransferControls().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() && if (slide.getUri() != null && slide.hasPlayOverlay() &&
@ -440,7 +445,7 @@ public class ThumbnailView extends FrameLayout {
public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri, int width, int height, boolean animate, @Nullable ThumbnailRequestListener listener) { public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri, int width, int height, boolean animate, @Nullable ThumbnailRequestListener listener) {
SettableFuture<Boolean> future = new SettableFuture<>(); SettableFuture<Boolean> future = new SettableFuture<>();
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); transferControlViewStub.setVisibility(View.GONE);
GlideRequest<Drawable> request = glideRequests.load(new DecryptableUri(uri)) GlideRequest<Drawable> request = glideRequests.load(new DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
@ -473,7 +478,7 @@ public class ThumbnailView extends FrameLayout {
public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull StoryTextPostModel model, int width, int height) { public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull StoryTextPostModel model, int width, int height) {
SettableFuture<Boolean> future = new SettableFuture<>(); SettableFuture<Boolean> future = new SettableFuture<>();
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); transferControlViewStub.setVisibility(View.GONE);
GlideRequest<Drawable> request = glideRequests.load(model) GlideRequest<Drawable> request = glideRequests.load(model)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
@ -502,8 +507,8 @@ public class ThumbnailView extends FrameLayout {
glideRequests.clear(image); glideRequests.clear(image);
image.setImageDrawable(null); image.setImageDrawable(null);
if (transferControls.isPresent()) { if (transferControlViewStub.resolved()) {
getTransferControls().clear(); transferControlViewStub.get().clear();
} }
glideRequests.clear(blurHash); glideRequests.clear(blurHash);
@ -513,11 +518,12 @@ public class ThumbnailView extends FrameLayout {
} }
public void showDownloadText(boolean showDownloadText) { public void showDownloadText(boolean showDownloadText) {
getTransferControls().setShowDownloadText(showDownloadText); transferControlsState = transferControlsState.withDownloadText(showDownloadText);
transferControlsState.applyState(transferControlViewStub);
} }
public void showProgressSpinner() { public void showProgressSpinner() {
getTransferControls().showProgressSpinner(); transferControlViewStub.get().showProgressSpinner();
} }
public void setScaleType(@NonNull ImageView.ScaleType scaleType) { public void setScaleType(@NonNull ImageView.ScaleType scaleType) {

Wyświetl plik

@ -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<TransferControlView>) {
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)
}
}
}

Wyświetl plik

@ -182,7 +182,7 @@ public final class TransferControlView extends FrameLayout {
return true; return true;
} }
private int getTransferState(@NonNull List<Slide> slides) { static int getTransferState(@NonNull List<Slide> slides) {
int transferState = AttachmentTable.TRANSFER_PROGRESS_DONE; int transferState = AttachmentTable.TRANSFER_PROGRESS_DONE;
boolean allFailed = true; boolean allFailed = true;

Wyświetl plik

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" <merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
tools:viewBindingIgnore="true" xmlns:tools="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools"> tools:viewBindingIgnore="true">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/linkpreview_container" android:id="@+id/linkpreview_container"
@ -11,19 +11,16 @@
android:background="@color/signal_neutralSurface" android:background="@color/signal_neutralSurface"
android:padding="6dp"> android:padding="6dp">
<org.thoughtcrime.securesms.components.OutlinedThumbnailView <ViewStub
android:id="@+id/linkpreview_thumbnail" android:id="@+id/linkpreview_thumbnail"
android:layout_width="72dp" android:layout_width="72dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout="@layout/link_preview_thumbnail_stub"
android:maxHeight="72dp" android:maxHeight="72dp"
android:scaleType="centerCrop"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/linkpreview_divider" app:layout_constraintBottom_toTopOf="@+id/linkpreview_divider"
app:layout_constraintHeight_min="72dp" app:layout_constraintHeight_min="72dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/linkpreview_title" app:layout_constraintTop_toTopOf="@+id/linkpreview_title" />
tools:src="@drawable/ic_contact_picture"
tools:visibility="visible" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView <org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/linkpreview_title" android:id="@+id/linkpreview_title"

Wyświetl plik

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.OutlinedThumbnailView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linkpreview_thumbnail"
android:layout_width="72dp"
android:layout_height="0dp"
android:maxHeight="72dp"
android:scaleType="centerCrop"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/linkpreview_divider"
app:layout_constraintHeight_min="72dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/linkpreview_title"
tools:src="@drawable/ic_contact_picture"
tools:visibility="visible" />