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.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<OutlinedThumbnailView> 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) {

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.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<TransferControlView> transferControls = Optional.empty();
private SlideClickListener thumbnailClickListener = null;
private SlidesClickedListener downloadClickListener = null;
private Slide slide = null;
private ThumbnailViewTransferControlsState transferControlsState = new ThumbnailViewTransferControlsState();
private Stub<TransferControlView> 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<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri, int width, int height, boolean animate, @Nullable ThumbnailRequestListener listener) {
SettableFuture<Boolean> future = new SettableFuture<>();
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE);
transferControlViewStub.setVisibility(View.GONE);
GlideRequest<Drawable> request = glideRequests.load(new DecryptableUri(uri))
.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) {
SettableFuture<Boolean> future = new SettableFuture<>();
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE);
transferControlViewStub.setVisibility(View.GONE);
GlideRequest<Drawable> 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) {

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;
}
private int getTransferState(@NonNull List<Slide> slides) {
static int getTransferState(@NonNull List<Slide> slides) {
int transferState = AttachmentTable.TRANSFER_PROGRESS_DONE;
boolean allFailed = true;

Wyświetl plik

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
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
android:id="@+id/linkpreview_container"
@ -11,19 +11,16 @@
android:background="@color/signal_neutralSurface"
android:padding="6dp">
<org.thoughtcrime.securesms.components.OutlinedThumbnailView
<ViewStub
android:id="@+id/linkpreview_thumbnail"
android:layout_width="72dp"
android:layout_height="0dp"
android:layout="@layout/link_preview_thumbnail_stub"
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" />
app:layout_constraintTop_toTopOf="@+id/linkpreview_title" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
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" />