mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-23 17:48:50 +00:00
Added support for multi-image receive.
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class AlbumThumbnailView extends FrameLayout {
|
||||
|
||||
private @Nullable SlideClickListener thumbnailClickListener;
|
||||
private @Nullable SlidesClickedListener downloadClickListener;
|
||||
|
||||
private int currentSizeClass;
|
||||
|
||||
private ViewGroup albumCellContainer;
|
||||
private Stub<TransferControlView> transferControls;
|
||||
|
||||
private final SlideClickListener defaultThumbnailClickListener = (v, slide) -> {
|
||||
if (thumbnailClickListener != null) {
|
||||
thumbnailClickListener.onClick(v, slide);
|
||||
}
|
||||
};
|
||||
|
||||
private final OnLongClickListener defaultLongClickListener = v -> this.performLongClick();
|
||||
|
||||
public AlbumThumbnailView(@NonNull @android.support.annotation.NonNull Context context) {
|
||||
super(context);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public AlbumThumbnailView(@NonNull @android.support.annotation.NonNull Context context, @Nullable @android.support.annotation.Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
inflate(getContext(), R.layout.album_thumbnail_view, this);
|
||||
|
||||
albumCellContainer = findViewById(R.id.album_cell_container);
|
||||
transferControls = new Stub<>(findViewById(R.id.album_transfer_controls_stub));
|
||||
}
|
||||
|
||||
public void setSlides(@NonNull GlideRequests glideRequests, @NonNull List<Slide> slides, boolean showControls) {
|
||||
if (slides.size() < 2) {
|
||||
throw new IllegalStateException("Provided less than two slides.");
|
||||
}
|
||||
|
||||
if (showControls) {
|
||||
transferControls.get().setShowDownloadText(true);
|
||||
transferControls.get().setSlides(slides);
|
||||
transferControls.get().setDownloadClickListener(v -> {
|
||||
if (downloadClickListener != null) {
|
||||
downloadClickListener.onClick(v, slides);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (transferControls.resolved()) {
|
||||
transferControls.get().setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
int sizeClass = sizeClass(slides.size());
|
||||
|
||||
if (sizeClass != currentSizeClass) {
|
||||
inflateLayout(sizeClass);
|
||||
currentSizeClass = sizeClass;
|
||||
}
|
||||
|
||||
showSlides(glideRequests, slides);
|
||||
}
|
||||
|
||||
public void setCellBackgroundColor(@ColorInt int color) {
|
||||
ViewGroup cellRoot = findViewById(R.id.album_thumbnail_root);
|
||||
|
||||
if (cellRoot != null) {
|
||||
for (int i = 0; i < cellRoot.getChildCount(); i++) {
|
||||
cellRoot.getChildAt(i).setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setThumbnailClickListener(@Nullable SlideClickListener listener) {
|
||||
thumbnailClickListener = listener;
|
||||
}
|
||||
|
||||
public void setDownloadClickListener(@Nullable SlidesClickedListener listener) {
|
||||
downloadClickListener = listener;
|
||||
}
|
||||
|
||||
private void inflateLayout(int sizeClass) {
|
||||
albumCellContainer.removeAllViews();
|
||||
|
||||
switch (sizeClass) {
|
||||
case 2:
|
||||
inflate(getContext(), R.layout.album_thumbnail_2, albumCellContainer);
|
||||
break;
|
||||
case 3:
|
||||
inflate(getContext(), R.layout.album_thumbnail_3, albumCellContainer);
|
||||
break;
|
||||
case 4:
|
||||
inflate(getContext(), R.layout.album_thumbnail_4, albumCellContainer);
|
||||
break;
|
||||
case 5:
|
||||
inflate(getContext(), R.layout.album_thumbnail_5, albumCellContainer);
|
||||
break;
|
||||
default:
|
||||
inflate(getContext(), R.layout.album_thumbnail_many, albumCellContainer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void showSlides(@NonNull GlideRequests glideRequests, @NonNull List<Slide> slides) {
|
||||
setSlide(glideRequests, slides.get(0), R.id.album_cell_1);
|
||||
setSlide(glideRequests, slides.get(1), R.id.album_cell_2);
|
||||
|
||||
if (slides.size() >= 3) {
|
||||
setSlide(glideRequests, slides.get(2), R.id.album_cell_3);
|
||||
}
|
||||
|
||||
if (slides.size() >= 4) {
|
||||
setSlide(glideRequests, slides.get(3), R.id.album_cell_4);
|
||||
}
|
||||
|
||||
if (slides.size() >= 5) {
|
||||
setSlide(glideRequests, slides.get(4), R.id.album_cell_5);
|
||||
}
|
||||
|
||||
if (slides.size() > 5) {
|
||||
TextView text = findViewById(R.id.album_cell_overflow_text);
|
||||
text.setText(getContext().getString(R.string.AlbumThumbnailView_plus, slides.size() - 5));
|
||||
}
|
||||
}
|
||||
|
||||
private void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide, @IdRes int id) {
|
||||
ThumbnailView cell = findViewById(id);
|
||||
cell.setImageResource(glideRequests, slide, false, false);
|
||||
cell.setThumbnailClickListener(defaultThumbnailClickListener);
|
||||
cell.setOnLongClickListener(defaultLongClickListener);
|
||||
}
|
||||
|
||||
private int sizeClass(int size) {
|
||||
return Math.min(size, 6);
|
||||
}
|
||||
}
|
@@ -3,11 +3,10 @@ package org.thoughtcrime.securesms.components;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
@@ -16,40 +15,36 @@ import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ConversationItemThumbnail extends FrameLayout {
|
||||
|
||||
private static final String TAG = ConversationItemThumbnail.class.getSimpleName();
|
||||
|
||||
private static final Paint LIGHT_THEME_OUTLINE_PAINT = new Paint();
|
||||
private static final Paint DARK_THEME_OUTLINE_PAINT = new Paint();
|
||||
|
||||
static {
|
||||
LIGHT_THEME_OUTLINE_PAINT.setColor(Color.argb((int) (255 * 0.2), 0, 0, 0));
|
||||
LIGHT_THEME_OUTLINE_PAINT.setStyle(Paint.Style.STROKE);
|
||||
LIGHT_THEME_OUTLINE_PAINT.setStrokeWidth(1f);
|
||||
LIGHT_THEME_OUTLINE_PAINT.setAntiAlias(true);
|
||||
|
||||
DARK_THEME_OUTLINE_PAINT.setColor(Color.argb((int) (255 * 0.2), 255, 255, 255));
|
||||
DARK_THEME_OUTLINE_PAINT.setStyle(Paint.Style.STROKE);
|
||||
DARK_THEME_OUTLINE_PAINT.setStrokeWidth(1f);
|
||||
DARK_THEME_OUTLINE_PAINT.setAntiAlias(true);
|
||||
}
|
||||
|
||||
private final float[] radii = new float[8];
|
||||
private final RectF bounds = new RectF();
|
||||
private final Path corners = new Path();
|
||||
private final float[] radii = new float[8];
|
||||
private final RectF bounds = new RectF();
|
||||
private final Path corners = new Path();
|
||||
|
||||
private ThumbnailView thumbnail;
|
||||
private AlbumThumbnailView album;
|
||||
private ImageView shade;
|
||||
private ConversationItemFooter footer;
|
||||
private Paint outlinePaint;
|
||||
private CornerMask cornerMask;
|
||||
|
||||
private final Paint outlinePaint = new Paint();
|
||||
{
|
||||
outlinePaint.setStyle(Paint.Style.STROKE);
|
||||
outlinePaint.setStrokeWidth(1f);
|
||||
outlinePaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
public ConversationItemThumbnail(Context context) {
|
||||
super(context);
|
||||
init(null);
|
||||
@@ -68,13 +63,13 @@ public class ConversationItemThumbnail extends FrameLayout {
|
||||
private void init(@Nullable AttributeSet attrs) {
|
||||
inflate(getContext(), R.layout.conversation_item_thumbnail, this);
|
||||
|
||||
this.thumbnail = findViewById(R.id.conversation_thumbnail_image);
|
||||
this.shade = findViewById(R.id.conversation_thumbnail_shade);
|
||||
this.footer = findViewById(R.id.conversation_thumbnail_footer);
|
||||
this.outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT;
|
||||
this.cornerMask = new CornerMask(this);
|
||||
outlinePaint.setColor(ThemeUtil.getThemedColor(getContext(), R.attr.conversation_item_image_outline_color));
|
||||
|
||||
setTouchDelegate(thumbnail.getTouchDelegate());
|
||||
this.thumbnail = findViewById(R.id.conversation_thumbnail_image);
|
||||
this.album = findViewById(R.id.conversation_thumbnail_album);
|
||||
this.shade = findViewById(R.id.conversation_thumbnail_shade);
|
||||
this.footer = findViewById(R.id.conversation_thumbnail_footer);
|
||||
this.cornerMask = new CornerMask(this);
|
||||
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemThumbnail, 0, 0);
|
||||
@@ -99,32 +94,37 @@ public class ConversationItemThumbnail extends FrameLayout {
|
||||
cornerMask.mask(canvas);
|
||||
}
|
||||
|
||||
final float halfStrokeWidth = outlinePaint.getStrokeWidth() / 2;
|
||||
if (album.getVisibility() != VISIBLE) {
|
||||
final float halfStrokeWidth = outlinePaint.getStrokeWidth() / 2;
|
||||
|
||||
bounds.left = halfStrokeWidth;
|
||||
bounds.top = halfStrokeWidth;
|
||||
bounds.right = canvas.getWidth() - halfStrokeWidth;
|
||||
bounds.bottom = canvas.getHeight() - halfStrokeWidth;
|
||||
bounds.left = halfStrokeWidth;
|
||||
bounds.top = halfStrokeWidth;
|
||||
bounds.right = canvas.getWidth() - halfStrokeWidth;
|
||||
bounds.bottom = canvas.getHeight() - halfStrokeWidth;
|
||||
|
||||
corners.reset();
|
||||
corners.addRoundRect(bounds, radii, Path.Direction.CW);
|
||||
corners.reset();
|
||||
corners.addRoundRect(bounds, radii, Path.Direction.CW);
|
||||
|
||||
canvas.drawPath(corners, outlinePaint);
|
||||
canvas.drawPath(corners, outlinePaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocusable(boolean focusable) {
|
||||
thumbnail.setFocusable(focusable);
|
||||
album.setFocusable(focusable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClickable(boolean clickable) {
|
||||
thumbnail.setClickable(clickable);
|
||||
album.setClickable(clickable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnLongClickListener(@Nullable OnLongClickListener l) {
|
||||
thumbnail.setOnLongClickListener(l);
|
||||
album.setOnLongClickListener(l);
|
||||
}
|
||||
|
||||
public void showShade(boolean show) {
|
||||
@@ -146,37 +146,38 @@ public class ConversationItemThumbnail extends FrameLayout {
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide,
|
||||
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull List<Slide> slides,
|
||||
boolean showControls, boolean isPreview)
|
||||
{
|
||||
thumbnail.setImageResource(glideRequests, slide, showControls, isPreview);
|
||||
if (slides.size() == 1) {
|
||||
thumbnail.setVisibility(VISIBLE);
|
||||
album.setVisibility(GONE);
|
||||
|
||||
Attachment attachment = slides.get(0).asAttachment();
|
||||
thumbnail.setImageResource(glideRequests, slides.get(0), showControls, isPreview, attachment.getWidth(), attachment.getHeight());
|
||||
setTouchDelegate(thumbnail.getTouchDelegate());
|
||||
} else {
|
||||
thumbnail.setVisibility(GONE);
|
||||
album.setVisibility(VISIBLE);
|
||||
|
||||
album.setSlides(glideRequests, slides, showControls);
|
||||
setTouchDelegate(album.getTouchDelegate());
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide,
|
||||
boolean showControls, boolean isPreview, int naturalWidth,
|
||||
int naturalHeight)
|
||||
{
|
||||
thumbnail.setImageResource(glideRequests, slide, showControls, isPreview, naturalWidth, naturalHeight);
|
||||
}
|
||||
|
||||
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri) {
|
||||
thumbnail.setImageResource(glideRequests, uri);
|
||||
public void setConversationColor(@ColorInt int color) {
|
||||
if (album.getVisibility() == VISIBLE) {
|
||||
album.setCellBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public void setThumbnailClickListener(SlideClickListener listener) {
|
||||
thumbnail.setThumbnailClickListener(listener);
|
||||
album.setThumbnailClickListener(listener);
|
||||
}
|
||||
|
||||
public void setDownloadClickListener(SlideClickListener listener) {
|
||||
public void setDownloadClickListener(SlidesClickedListener listener) {
|
||||
thumbnail.setDownloadClickListener(listener);
|
||||
}
|
||||
|
||||
public void clear(GlideRequests glideRequests) {
|
||||
thumbnail.clear(glideRequests);
|
||||
}
|
||||
|
||||
public void showProgressSpinner() {
|
||||
thumbnail.showProgressSpinner();
|
||||
album.setDownloadClickListener(listener);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
package org.thoughtcrime.securesms.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
public class MaxHeightScrollView extends ScrollView {
|
||||
|
||||
private int maxHeight = -1;
|
||||
|
||||
public MaxHeightScrollView(Context context) {
|
||||
super(context);
|
||||
initialize(null);
|
||||
}
|
||||
|
||||
public MaxHeightScrollView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize(attrs);
|
||||
}
|
||||
|
||||
private void initialize(@Nullable AttributeSet attrs) {
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView, 0, 0);
|
||||
|
||||
maxHeight = typedArray.getDimensionPixelOffset(R.styleable.MaxHeightScrollView_scrollView_maxHeight, -1);
|
||||
|
||||
typedArray.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (maxHeight >= 0) {
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
@@ -11,6 +11,7 @@ import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
@@ -81,6 +82,19 @@ public class MediaView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
public void hideControls() {
|
||||
if (this.videoView.resolved()){
|
||||
this.videoView.get().hideControls();
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable View getPlaybackControls() {
|
||||
if (this.videoView.resolved()){
|
||||
return this.videoView.get().getControlView();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
this.imageView.cleanup();
|
||||
if (this.videoView.resolved()) {
|
||||
|
@@ -27,12 +27,14 @@ import org.thoughtcrime.securesms.mms.GlideRequest;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideClickListener;
|
||||
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
||||
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.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
@@ -49,6 +51,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
private ImageView image;
|
||||
private View playOverlay;
|
||||
private View captionIcon;
|
||||
private OnClickListener parentClickListener;
|
||||
|
||||
private final int[] dimens = new int[2];
|
||||
@@ -57,7 +60,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
private Optional<TransferControlView> transferControls = Optional.absent();
|
||||
private SlideClickListener thumbnailClickListener = null;
|
||||
private SlideClickListener downloadClickListener = null;
|
||||
private SlidesClickedListener downloadClickListener = null;
|
||||
private Slide slide = null;
|
||||
|
||||
private int radius;
|
||||
@@ -77,6 +80,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
this.image = findViewById(R.id.thumbnail_image);
|
||||
this.playOverlay = findViewById(R.id.play_overlay);
|
||||
this.captionIcon = findViewById(R.id.thumbnail_caption_icon);
|
||||
super.setOnClickListener(new ThumbnailClickDispatcher());
|
||||
|
||||
if (attrs != null) {
|
||||
@@ -230,8 +234,8 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
@UiThread
|
||||
public ListenableFuture<Boolean> setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide,
|
||||
boolean showControls, boolean isPreview, int naturalWidth,
|
||||
int naturalHeight)
|
||||
boolean showControls, boolean isPreview,
|
||||
int naturalWidth, int naturalHeight)
|
||||
{
|
||||
if (showControls) {
|
||||
getTransferControls().setSlide(slide);
|
||||
@@ -267,6 +271,8 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
this.slide = slide;
|
||||
|
||||
this.captionIcon.setVisibility(slide.getCaption().isPresent() ? VISIBLE : GONE);
|
||||
|
||||
dimens[WIDTH] = naturalWidth;
|
||||
dimens[HEIGHT] = naturalHeight;
|
||||
invalidate();
|
||||
@@ -302,7 +308,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
this.thumbnailClickListener = listener;
|
||||
}
|
||||
|
||||
public void setDownloadClickListener(SlideClickListener listener) {
|
||||
public void setDownloadClickListener(SlidesClickedListener listener) {
|
||||
this.downloadClickListener = listener;
|
||||
}
|
||||
|
||||
@@ -342,8 +348,14 @@ public class ThumbnailView extends FrameLayout {
|
||||
size[WIDTH] = getDefaultWidth();
|
||||
size[HEIGHT] = getDefaultHeight();
|
||||
}
|
||||
return request.override(size[WIDTH], size[HEIGHT])
|
||||
.transforms(fitting, new RoundedCorners(radius));
|
||||
|
||||
request = request.override(size[WIDTH], size[HEIGHT]);
|
||||
|
||||
if (radius > 0) {
|
||||
return request.transforms(fitting, new RoundedCorners(radius));
|
||||
} else {
|
||||
return request.transforms(fitting);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDefaultWidth() {
|
||||
@@ -382,7 +394,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
public void onClick(View view) {
|
||||
Log.i(TAG, "onClick() for download button");
|
||||
if (downloadClickListener != null && slide != null) {
|
||||
downloadClickListener.onClick(view, slide);
|
||||
downloadClickListener.onClick(view, Collections.singletonList(slide));
|
||||
} else {
|
||||
Log.w(TAG, "Received a download button click, but unable to execute it. slide: " + String.valueOf(slide) + " downloadClickListener: " + String.valueOf(downloadClickListener));
|
||||
}
|
||||
|
@@ -15,32 +15,42 @@ import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.nineoldandroids.animation.Animator;
|
||||
import com.nineoldandroids.animation.ValueAnimator;
|
||||
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TransferControlView extends FrameLayout {
|
||||
private static final int TRANSITION_MS = 300;
|
||||
|
||||
@Nullable private Slide slide;
|
||||
@Nullable private View current;
|
||||
@Nullable private List<Slide> slides;
|
||||
@Nullable private View current;
|
||||
|
||||
private final ProgressWheel progressWheel;
|
||||
private final TextView downloadDetails;
|
||||
private final View downloadDetails;
|
||||
private final TextView downloadDetailsText;
|
||||
private final int contractedWidth;
|
||||
private final int expandedWidth;
|
||||
|
||||
private final Map<Attachment, Float> downloadProgress;
|
||||
|
||||
public TransferControlView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -61,10 +71,12 @@ public class TransferControlView extends FrameLayout {
|
||||
ViewUtil.setBackground(this, background);
|
||||
setVisibility(GONE);
|
||||
|
||||
this.progressWheel = ViewUtil.findById(this, R.id.progress_wheel);
|
||||
this.downloadDetails = ViewUtil.findById(this, R.id.download_details);
|
||||
this.contractedWidth = getResources().getDimensionPixelSize(R.dimen.transfer_controls_contracted_width);
|
||||
this.expandedWidth = getResources().getDimensionPixelSize(R.dimen.transfer_controls_expanded_width);
|
||||
this.downloadProgress = new HashMap<>();
|
||||
this.progressWheel = ViewUtil.findById(this, R.id.progress_wheel);
|
||||
this.downloadDetails = ViewUtil.findById(this, R.id.download_details);
|
||||
this.downloadDetailsText = ViewUtil.findById(this, R.id.download_details_text);
|
||||
this.contractedWidth = getResources().getDimensionPixelSize(R.dimen.transfer_controls_contracted_width);
|
||||
this.expandedWidth = getResources().getDimensionPixelSize(R.dimen.transfer_controls_expanded_width);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,20 +103,54 @@ public class TransferControlView extends FrameLayout {
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
public void setSlide(final @NonNull Slide slide) {
|
||||
this.slide = slide;
|
||||
if (slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_STARTED) {
|
||||
showProgressSpinner();
|
||||
} else if (slide.isPendingDownload()) {
|
||||
downloadDetails.setText(slide.getContentDescription());
|
||||
display(downloadDetails);
|
||||
public void setSlide(final @NonNull Slide slides) {
|
||||
setSlides(Collections.singletonList(slides));
|
||||
}
|
||||
|
||||
public void setSlides(final @NonNull List<Slide> slides) {
|
||||
if (slides.isEmpty()) {
|
||||
throw new IllegalArgumentException("Must provide at least one slide.");
|
||||
}
|
||||
|
||||
this.slides = slides;
|
||||
|
||||
if (!isUpdateToExistingSet(slides)) {
|
||||
downloadProgress.clear();
|
||||
Stream.of(slides).forEach(s -> downloadProgress.put(s.asAttachment(), 0f));
|
||||
} else {
|
||||
display(null);
|
||||
for (Slide slide : slides) {
|
||||
if (slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
|
||||
downloadProgress.put(slide.asAttachment(), 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (getTransferState(slides)) {
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_STARTED:
|
||||
showProgressSpinner(calculateProgress(downloadProgress));
|
||||
break;
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_PENDING:
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_FAILED:
|
||||
downloadDetailsText.setText(getDownloadText(this.slides));
|
||||
display(downloadDetails);
|
||||
break;
|
||||
default:
|
||||
display(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void showProgressSpinner() {
|
||||
progressWheel.spin();
|
||||
showProgressSpinner(calculateProgress(downloadProgress));
|
||||
}
|
||||
|
||||
public void showProgressSpinner(float progress) {
|
||||
if (progress == 0) {
|
||||
progressWheel.spin();
|
||||
} else {
|
||||
progressWheel.setInstantProgress(progress);
|
||||
}
|
||||
|
||||
display(progressWheel);
|
||||
}
|
||||
|
||||
@@ -120,12 +166,51 @@ public class TransferControlView extends FrameLayout {
|
||||
current.setVisibility(GONE);
|
||||
}
|
||||
current = null;
|
||||
slide = null;
|
||||
slides = null;
|
||||
}
|
||||
|
||||
public void setShowDownloadText(boolean showDownloadText) {
|
||||
downloadDetailsText.setVisibility(showDownloadText ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
private boolean isUpdateToExistingSet(@NonNull List<Slide> slides) {
|
||||
if (slides.size() != downloadProgress.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Slide slide : slides) {
|
||||
if (!downloadProgress.containsKey(slide.asAttachment())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getTransferState(@NonNull List<Slide> slides) {
|
||||
int transferState = AttachmentDatabase.TRANSFER_PROGRESS_DONE;
|
||||
for (Slide slide : slides) {
|
||||
if (slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_PENDING && transferState == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
|
||||
transferState = slide.getTransferState();
|
||||
} else {
|
||||
transferState = Math.max(transferState, slide.getTransferState());
|
||||
}
|
||||
}
|
||||
return transferState;
|
||||
}
|
||||
|
||||
private String getDownloadText(@NonNull List<Slide> slides) {
|
||||
if (slides.size() == 1) {
|
||||
return slides.get(0).getContentDescription();
|
||||
} else {
|
||||
int downloadCount = Stream.of(slides).reduce(0, (count, slide) -> slide.getTransferState() != AttachmentDatabase.TRANSFER_PROGRESS_DONE ? count + 1 : count);
|
||||
return getContext().getResources().getQuantityString(R.plurals.TransferControlView_n_items, downloadCount, downloadCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void display(@Nullable final View view) {
|
||||
final int sourceWidth = current == downloadDetails ? expandedWidth : contractedWidth;
|
||||
final int targetWidth = view == downloadDetails ? expandedWidth : contractedWidth;
|
||||
final int sourceWidth = (current == downloadDetails && downloadDetailsText.getVisibility() == VISIBLE) ? expandedWidth : contractedWidth;
|
||||
final int targetWidth = (view == downloadDetails && downloadDetailsText.getVisibility() == VISIBLE) ? expandedWidth : contractedWidth;
|
||||
|
||||
if (current == view || current == null) {
|
||||
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
@@ -149,28 +234,31 @@ public class TransferControlView extends FrameLayout {
|
||||
|
||||
private Animator getWidthAnimator(final int from, final int to) {
|
||||
final ValueAnimator anim = ValueAnimator.ofInt(from, to);
|
||||
anim.addUpdateListener(new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
final int val = (Integer)animation.getAnimatedValue();
|
||||
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
layoutParams.width = val;
|
||||
setLayoutParams(layoutParams);
|
||||
}
|
||||
anim.addUpdateListener(animation -> {
|
||||
final int val = (Integer)animation.getAnimatedValue();
|
||||
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
layoutParams.width = val;
|
||||
setLayoutParams(layoutParams);
|
||||
});
|
||||
anim.setInterpolator(new FastOutSlowInInterpolator());
|
||||
anim.setDuration(TRANSITION_MS);
|
||||
return anim;
|
||||
}
|
||||
|
||||
private float calculateProgress(@NonNull Map<Attachment, Float> downloadProgress) {
|
||||
float totalProgress = 0;
|
||||
for (float progress : downloadProgress.values()) {
|
||||
totalProgress += progress / downloadProgress.size();
|
||||
}
|
||||
return totalProgress;
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.ASYNC)
|
||||
public void onEventAsync(final PartProgressEvent event) {
|
||||
if (this.slide != null && event.attachment.equals(this.slide.asAttachment())) {
|
||||
Util.runOnMain(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressWheel.setInstantProgress(((float)event.progress) / event.total);
|
||||
}
|
||||
if (downloadProgress.containsKey(event.attachment)) {
|
||||
Util.runOnMain(() -> {
|
||||
downloadProgress.put(event.attachment, ((float)event.progress) / event.total);
|
||||
progressWheel.setInstantProgress(calculateProgress(downloadProgress));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user