Improve the look of message bubbles.

This commit is contained in:
Greyson Parrelli
2018-06-26 10:27:44 -07:00
parent 7cfcb62c25
commit 24b062d8dd
202 changed files with 2671 additions and 1451 deletions

View File

@@ -37,8 +37,8 @@ public class AlertView extends LinearLayout {
private void initialize(AttributeSet attrs) {
inflate(getContext(), R.layout.alert_view, this);
approvalIndicator = (ImageView) findViewById(R.id.pending_approval_indicator);
failedIndicator = (ImageView) findViewById(R.id.sms_failed_indicator);
approvalIndicator = findViewById(R.id.pending_approval_indicator);
failedIndicator = findViewById(R.id.sms_failed_indicator);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.AlertView, 0, 0);
@@ -46,7 +46,10 @@ public class AlertView extends LinearLayout {
typedArray.recycle();
if (useSmallIcon) {
failedIndicator.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_error_red_18dp));
int size = getResources().getDimensionPixelOffset(R.dimen.alertview_small_icon_size);
failedIndicator.getLayoutParams().width = size;
failedIndicator.getLayoutParams().height = size;
requestLayout();
}
}
}

View File

@@ -0,0 +1,172 @@
package org.thoughtcrime.securesms.components;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat;
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Locale;
public class ConversationItemFooter extends LinearLayout {
private TextView dateView;
private TextView simView;
private ExpirationTimerView timerView;
private ImageView insecureIndicatorView;
private DeliveryStatusView deliveryStatusView;
public ConversationItemFooter(Context context) {
super(context);
init(null);
}
public ConversationItemFooter(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ConversationItemFooter(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(@Nullable AttributeSet attrs) {
inflate(getContext(), R.layout.conversation_item_footer, this);
dateView = findViewById(R.id.footer_date);
simView = findViewById(R.id.footer_sim_info);
timerView = findViewById(R.id.footer_expiration_timer);
insecureIndicatorView = findViewById(R.id.footer_insecure_indicator);
deliveryStatusView = findViewById(R.id.footer_delivery_status);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemFooter, 0, 0);
setColor(typedArray.getInt(R.styleable.ConversationItemFooter_footer_color, getResources().getColor(R.color.core_white)));
typedArray.recycle();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
timerView.stopAnimation();
}
public void setMessageRecord(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
presentDate(messageRecord, locale);
presentSimInfo(messageRecord);
presentTimer(messageRecord);
presentInsecureIndicator(messageRecord);
presentDeliveryStatus(messageRecord);
}
public void setColor(int color) {
dateView.setTextColor(color);
simView.setTextColor(color);
timerView.setColorFilter(color);
insecureIndicatorView.setColorFilter(color);
deliveryStatusView.setTint(color);
}
private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {
dateView.forceLayout();
if (messageRecord.isFailed()) {
dateView.setText(R.string.ConversationItem_error_not_delivered);
} else if (messageRecord.isPendingInsecureSmsFallback()) {
dateView.setText(R.string.ConversationItem_click_to_approve_unencrypted);
} else {
dateView.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, messageRecord.getTimestamp()));
}
}
private void presentSimInfo(@NonNull MessageRecord messageRecord) {
SubscriptionManagerCompat subscriptionManager = new SubscriptionManagerCompat(getContext());
if (messageRecord.getSubscriptionId() == -1 || !Permissions.hasAll(getContext(), Manifest.permission.READ_PHONE_STATE) || subscriptionManager.getActiveSubscriptionInfoList().size() < 2) {
simView.setVisibility(View.GONE);
} else {
Optional<SubscriptionInfoCompat> subscriptionInfo = subscriptionManager.getActiveSubscriptionInfo(messageRecord.getSubscriptionId());
if (subscriptionInfo.isPresent() && messageRecord.isOutgoing()) {
simView.setText(getContext().getString(R.string.ConversationItem_from_s, subscriptionInfo.get().getDisplayName()));
simView.setVisibility(View.VISIBLE);
} else if (subscriptionInfo.isPresent()) {
simView.setText(getContext().getString(R.string.ConversationItem_to_s, subscriptionInfo.get().getDisplayName()));
simView.setVisibility(View.VISIBLE);
} else {
simView.setVisibility(View.GONE);
}
}
}
@SuppressLint("StaticFieldLeak")
private void presentTimer(@NonNull final MessageRecord messageRecord) {
if (messageRecord.getExpiresIn() > 0 && !messageRecord.isPending()) {
this.timerView.setVisibility(View.VISIBLE);
this.timerView.setPercentComplete(0);
if (messageRecord.getExpireStarted() > 0) {
this.timerView.setExpirationTime(messageRecord.getExpireStarted(),
messageRecord.getExpiresIn());
this.timerView.startAnimation();
if (messageRecord.getExpireStarted() + messageRecord.getExpiresIn() <= System.currentTimeMillis()) {
ApplicationContext.getInstance(getContext()).getExpiringMessageManager().checkSchedule();
}
} else if (!messageRecord.isOutgoing() && !messageRecord.isMediaPending()) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
ExpiringMessageManager expirationManager = ApplicationContext.getInstance(getContext()).getExpiringMessageManager();
long id = messageRecord.getId();
boolean mms = messageRecord.isMms();
if (mms) DatabaseFactory.getMmsDatabase(getContext()).markExpireStarted(id);
else DatabaseFactory.getSmsDatabase(getContext()).markExpireStarted(id);
expirationManager.scheduleDeletion(id, mms, messageRecord.getExpiresIn());
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
} else {
this.timerView.setVisibility(View.GONE);
}
}
private void presentInsecureIndicator(@NonNull MessageRecord messageRecord) {
insecureIndicatorView.setVisibility(messageRecord.isSecure() ? View.GONE : View.VISIBLE);
}
private void presentDeliveryStatus(@NonNull MessageRecord messageRecord) {
if (!messageRecord.isFailed() && !messageRecord.isPendingInsecureSmsFallback()) {
if (!messageRecord.isOutgoing()) deliveryStatusView.setNone();
else if (messageRecord.isPending()) deliveryStatusView.setPending();
else if (messageRecord.isRemoteRead()) deliveryStatusView.setRead();
else if (messageRecord.isDelivered()) deliveryStatusView.setDelivered();
else deliveryStatusView.setSent();
} else {
deliveryStatusView.setNone();
}
}
}

View File

@@ -0,0 +1,149 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideClickListener;
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
public class ConversationItemThumbnail extends FrameLayout {
private static final String TAG = ConversationItemThumbnail.class.getSimpleName();
private ThumbnailView thumbnail;
private ImageView shade;
private CornerMaskingView cornerMask;
private ConversationItemFooter footer;
public ConversationItemThumbnail(Context context) {
super(context);
init(null);
}
public ConversationItemThumbnail(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ConversationItemThumbnail(final Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
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.cornerMask = findViewById(R.id.conversation_thumbnail_corner_mask);
this.footer = findViewById(R.id.conversation_thumbnail_footer);
setCornerRadius(getResources().getDimensionPixelSize(R.dimen.message_corner_radius));
setTouchDelegate(thumbnail.getTouchDelegate());
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.ConversationItemThumbnail, 0, 0);
thumbnail.setBounds(typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minWidth, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxWidth, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_minHeight, 0),
typedArray.getDimensionPixelSize(R.styleable.ConversationItemThumbnail_conversationThumbnail_maxHeight, 0));
typedArray.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getMeasuredWidth() != thumbnail.getMeasuredWidth()) {
getLayoutParams().width = shade.getLayoutParams().width = thumbnail.getMeasuredWidth();
measure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
public void setFocusable(boolean focusable) {
thumbnail.setFocusable(focusable);
}
@Override
public void setClickable(boolean clickable) {
thumbnail.setClickable(clickable);
}
@Override
public void setOnLongClickListener(@Nullable OnLongClickListener l) {
thumbnail.setOnLongClickListener(l);
}
public void showShade(boolean show) {
shade.setVisibility(show ? VISIBLE : GONE);
forceLayout();
}
public ConversationItemFooter getFooter() {
return footer;
}
public void setCornerRadius(int radius) {
setCornerRadii(radius, radius, radius, radius);
}
public void setCornerRadii(int topLeft, int topRight, int bottomRight, int bottomLeft) {
cornerMask.setRadii(topLeft, topRight, bottomRight, bottomLeft);
}
public void setImageBackground(@DrawableRes int resId) {
thumbnail.setImageBackground(resId);
}
@UiThread
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide,
boolean showControls, boolean isPreview)
{
thumbnail.setImageResource(glideRequests, slide, showControls, isPreview);
}
@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 setThumbnailClickListener(SlideClickListener listener) {
thumbnail.setThumbnailClickListener(listener);
}
public void setDownloadClickListener(SlideClickListener listener) {
thumbnail.setDownloadClickListener(listener);
}
public void clear(GlideRequests glideRequests) {
thumbnail.clear(glideRequests);
}
public void showProgressSpinner() {
thumbnail.showProgressSpinner();
}
}

View File

@@ -0,0 +1,93 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
public class CornerMaskingView extends FrameLayout {
private final float[] radii = new float[8];
private final Paint paint = new Paint();
private final Path corners = new Path();
private final RectF bounds = new RectF();
public CornerMaskingView(@NonNull Context context) {
super(context);
init();
}
public CornerMaskingView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CornerMaskingView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setLayerType(LAYER_TYPE_HARDWARE, null);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
bounds.left = 0;
bounds.top = 0;
bounds.right = canvas.getWidth();
bounds.bottom = canvas.getHeight();
corners.reset();
corners.addRoundRect(bounds, radii, Path.Direction.CW);
canvas.drawPath(corners, paint);
}
public void setRadius(int radius) {
setRadii(radius, radius, radius, radius);
}
public void setRadii(int topLeft, int topRight, int bottomRight, int bottomLeft) {
radii[0] = radii[1] = topLeft;
radii[2] = radii[3] = topRight;
radii[4] = radii[5] = bottomRight;
radii[6] = radii[7] = bottomLeft;
invalidate();
}
public void setTopLeftRadius(int radius) {
radii[0] = radii[1] = radius;
invalidate();
}
public void setTopRightRadius(int radius) {
radii[2] = radii[3] = radius;
invalidate();
}
public void setBottomRightRadius(int radius) {
radii[4] = radii[5] = radius;
invalidate();
}
public void setBottomLeftRadius(int radius) {
radii[6] = radii[7] = radius;
invalidate();
}
}

View File

@@ -19,7 +19,7 @@ public class DeliveryStatusView extends FrameLayout {
private static final String TAG = DeliveryStatusView.class.getSimpleName();
private final ViewGroup pendingIndicatorStub;
private final ImageView pendingIndicator;
private final ImageView sentIndicator;
private final ImageView deliveredIndicator;
private final ImageView readIndicator;
@@ -37,31 +37,16 @@ public class DeliveryStatusView extends FrameLayout {
inflate(context, R.layout.delivery_status_view, this);
this.deliveredIndicator = (ImageView) findViewById(R.id.delivered_indicator);
this.sentIndicator = (ImageView) findViewById(R.id.sent_indicator);
this.pendingIndicatorStub = (ViewGroup) findViewById(R.id.pending_indicator_stub);
this.readIndicator = (ImageView) findViewById(R.id.read_indicator);
int iconColor = Color.GRAY;
this.deliveredIndicator = findViewById(R.id.delivered_indicator);
this.sentIndicator = findViewById(R.id.sent_indicator);
this.pendingIndicator = findViewById(R.id.pending_indicator);
this.readIndicator = findViewById(R.id.read_indicator);
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DeliveryStatusView, 0, 0);
iconColor = typedArray.getColor(R.styleable.DeliveryStatusView_iconColor, iconColor);
setTint(typedArray.getColor(R.styleable.DeliveryStatusView_iconColor, getResources().getColor(R.color.core_white)));
typedArray.recycle();
}
deliveredIndicator.setColorFilter(iconColor, android.graphics.PorterDuff.Mode.MULTIPLY);
sentIndicator.setColorFilter(iconColor, android.graphics.PorterDuff.Mode.MULTIPLY);
if (Build.VERSION.SDK_INT >= 11) {
inflate(context, R.layout.conversation_item_pending_v11, pendingIndicatorStub);
DotsTextView pendingIndicator = (DotsTextView) findViewById(R.id.pending_indicator);
pendingIndicator.setDotsColor(iconColor);
} else {
inflate(context, R.layout.conversation_item_pending, pendingIndicatorStub);
TextView pendingIndicator = (TextView) findViewById(R.id.pending_indicator);
pendingIndicator.setTextColor(iconColor);
}
}
public void setNone() {
@@ -70,7 +55,7 @@ public class DeliveryStatusView extends FrameLayout {
public void setPending() {
this.setVisibility(View.VISIBLE);
pendingIndicatorStub.setVisibility(View.VISIBLE);
pendingIndicator.setVisibility(View.VISIBLE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
readIndicator.setVisibility(View.GONE);
@@ -78,7 +63,7 @@ public class DeliveryStatusView extends FrameLayout {
public void setSent() {
this.setVisibility(View.VISIBLE);
pendingIndicatorStub.setVisibility(View.GONE);
pendingIndicator.setVisibility(View.GONE);
sentIndicator.setVisibility(View.VISIBLE);
deliveredIndicator.setVisibility(View.GONE);
readIndicator.setVisibility(View.GONE);
@@ -86,7 +71,7 @@ public class DeliveryStatusView extends FrameLayout {
public void setDelivered() {
this.setVisibility(View.VISIBLE);
pendingIndicatorStub.setVisibility(View.GONE);
pendingIndicator.setVisibility(View.GONE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.VISIBLE);
readIndicator.setVisibility(View.GONE);
@@ -94,9 +79,16 @@ public class DeliveryStatusView extends FrameLayout {
public void setRead() {
this.setVisibility(View.VISIBLE);
pendingIndicatorStub.setVisibility(View.GONE);
pendingIndicator.setVisibility(View.GONE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
readIndicator.setVisibility(View.VISIBLE);
}
public void setTint(int color) {
pendingIndicator.setColorFilter(color);
deliveredIndicator.setColorFilter(color);
sentIndicator.setColorFilter(color);
readIndicator.setColorFilter(color);
}
}

View File

@@ -11,6 +11,7 @@ import android.support.annotation.Nullable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -34,8 +35,8 @@ public class DocumentView extends FrameLayout {
private final @NonNull AnimatingToggle controlToggle;
private final @NonNull ImageView downloadButton;
private final @NonNull ProgressWheel downloadProgress;
private final @NonNull View documentBackground;
private final @NonNull View container;
private final @NonNull ViewGroup iconContainer;
private final @NonNull TextView fileName;
private final @NonNull TextView fileSize;
private final @NonNull TextView document;
@@ -56,24 +57,23 @@ public class DocumentView extends FrameLayout {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.document_view, this);
this.container = findViewById(R.id.document_container);
this.controlToggle = (AnimatingToggle) findViewById(R.id.control_toggle);
this.downloadButton = (ImageView) findViewById(R.id.download);
this.downloadProgress = (ProgressWheel) findViewById(R.id.download_progress);
this.fileName = (TextView) findViewById(R.id.file_name);
this.fileSize = (TextView) findViewById(R.id.file_size);
this.documentBackground = findViewById(R.id.document_background);
this.document = (TextView) findViewById(R.id.document);
this.document.getBackground().mutate();
this.documentBackground.getBackground().mutate();
this.container = findViewById(R.id.document_container);
this.iconContainer = findViewById(R.id.icon_container);
this.controlToggle = findViewById(R.id.control_toggle);
this.downloadButton = findViewById(R.id.download);
this.downloadProgress = findViewById(R.id.download_progress);
this.fileName = findViewById(R.id.file_name);
this.fileSize = findViewById(R.id.file_size);
this.document = findViewById(R.id.document);
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DocumentView, 0, 0);
setTint(typedArray.getColor(R.styleable.DocumentView_documentForegroundTintColor, Color.WHITE),
typedArray.getColor(R.styleable.DocumentView_documentBackgroundTintColor, Color.WHITE));
container.setBackgroundColor(typedArray.getColor(R.styleable.DocumentView_documentWidgetBackground, Color.TRANSPARENT));
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.DocumentView, 0, 0);
int titleColor = typedArray.getInt(R.styleable.DocumentView_doc_titleColor, Color.BLACK);
int captionColor = typedArray.getInt(R.styleable.DocumentView_doc_captionColor, Color.BLACK);
typedArray.recycle();
fileName.setTextColor(titleColor);
fileSize.setTextColor(captionColor);
}
}
@@ -96,7 +96,7 @@ public class DocumentView extends FrameLayout {
controlToggle.displayQuick(downloadProgress);
downloadProgress.spin();
} else {
controlToggle.displayQuick(documentBackground);
controlToggle.displayQuick(iconContainer);
if (downloadProgress.isSpinning()) downloadProgress.stopSpinning();
}
@@ -108,18 +108,6 @@ public class DocumentView extends FrameLayout {
this.setOnClickListener(new OpenClickedListener(documentSlide));
}
public void setTint(int foregroundTint, int backgroundTint) {
DrawableCompat.setTint(this.document.getBackground(), backgroundTint);
DrawableCompat.setTint(this.documentBackground.getBackground(), foregroundTint);
this.document.setTextColor(foregroundTint);
this.fileName.setTextColor(foregroundTint);
this.fileSize.setTextColor(foregroundTint);
this.downloadButton.setColorFilter(foregroundTint);
this.downloadProgress.setBarColor(foregroundTint);
}
@Override
public void setFocusable(boolean focusable) {
super.setFocusable(focusable);

View File

@@ -5,12 +5,13 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.Util;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
public class ExpirationTimerView extends HourglassView {
public class ExpirationTimerView extends android.support.v7.widget.AppCompatImageView {
private long startedAt;
private long expiresIn;
@@ -18,6 +19,20 @@ public class ExpirationTimerView extends HourglassView {
private boolean visible = false;
private boolean stopped = true;
private final int[] frames = new int[]{ R.drawable.timer00,
R.drawable.timer05,
R.drawable.timer10,
R.drawable.timer15,
R.drawable.timer20,
R.drawable.timer25,
R.drawable.timer30,
R.drawable.timer35,
R.drawable.timer40,
R.drawable.timer45,
R.drawable.timer50,
R.drawable.timer55,
R.drawable.timer60 };
public ExpirationTimerView(Context context) {
super(context);
}
@@ -33,8 +48,13 @@ public class ExpirationTimerView extends HourglassView {
public void setExpirationTime(long startedAt, long expiresIn) {
this.startedAt = startedAt;
this.expiresIn = expiresIn;
setPercentComplete(calculateProgress(this.startedAt, this.expiresIn));
}
setPercentage(calculateProgress(this.startedAt, this.expiresIn));
public void setPercentComplete(float percentage) {
float percentFull = 1 - percentage;
int frame = (int) Math.ceil(percentFull * (frames.length - 1));
setImageResource(frames[frame]);
}
public void startAnimation() {
@@ -57,7 +77,7 @@ public class ExpirationTimerView extends HourglassView {
long progressed = System.currentTimeMillis() - startedAt;
float percentComplete = (float)progressed / (float)expiresIn;
return percentComplete * 100;
return percentComplete;
}
private long calculateAnimationDelay(long startedAt, long expiresIn) {

View File

@@ -24,7 +24,6 @@ public class HourglassView extends View {
private Bitmap empty;
private Bitmap full;
private int tint;
private float percentage;
private int offset;
@@ -40,13 +39,15 @@ public class HourglassView extends View {
public HourglassView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int tint = 0;
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.HourglassView, 0, 0);
this.empty = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(R.styleable.HourglassView_empty, 0));
this.full = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(R.styleable.HourglassView_full, 0));
this.tint = typedArray.getColor(R.styleable.HourglassView_tint, 0);
this.percentage = typedArray.getInt(R.styleable.HourglassView_percentage, 50);
this.offset = typedArray.getInt(R.styleable.HourglassView_offset, 0);
tint = typedArray.getColor(R.styleable.HourglassView_tint, 0);
typedArray.recycle();
}
@@ -80,6 +81,8 @@ public class HourglassView extends View {
invalidate();
}
public void setTint(int tint) {
this.backgroundPaint.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.MULTIPLY));
this.foregroundPaint.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.MULTIPLY));
}
}

View File

@@ -125,8 +125,8 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
ViewCompat.LAYOUT_DIRECTION_LTR ? -.25f : .25f;
AnimationSet animation = new AnimationSet(true);
animation.addAnimation(new TranslateAnimation(Animation.RELATIVE_TO_SELF, translation,
Animation.RELATIVE_TO_SELF, translation,
animation.addAnimation(new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, -.25f,
Animation.RELATIVE_TO_SELF, -.25f));
@@ -145,11 +145,10 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
public void moveTo(float x) {
this.lastPositionX = x;
float offset = getOffset(x);
int widthAdjustment = getWidthAdjustment();
float offset = getOffset(x);
Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, widthAdjustment + offset,
Animation.ABSOLUTE, widthAdjustment + offset,
Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, offset,
Animation.ABSOLUTE, offset,
Animation.RELATIVE_TO_SELF, -.25f,
Animation.RELATIVE_TO_SELF, -.25f);
@@ -163,16 +162,15 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
public void hide(float x) {
this.lastPositionX = x;
float offset = getOffset(x);
int widthAdjustment = getWidthAdjustment();
float offset = getOffset(x);
AnimationSet animation = new AnimationSet(false);
Animation scaleAnimation = new ScaleAnimation(1, 0.5f, 1, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, offset + widthAdjustment,
Animation.ABSOLUTE, widthAdjustment,
Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, offset,
Animation.ABSOLUTE, 0,
Animation.RELATIVE_TO_SELF, -.25f,
Animation.RELATIVE_TO_SELF, -.25f);

View File

@@ -3,12 +3,7 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.graphics.Color;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -26,14 +21,10 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.DocumentSlide;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.ThemeUtil;
@@ -49,16 +40,15 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
private static final int MESSAGE_TYPE_OUTGOING = 1;
private static final int MESSAGE_TYPE_INCOMING = 2;
private View rootView;
private TextView authorView;
private TextView bodyView;
private ImageView quoteBarView;
private ImageView attachmentView;
private ImageView attachmentVideoOverlayView;
private ViewGroup attachmentIconContainerView;
private ImageView attachmentIconView;
private ImageView attachmentIconBackgroundView;
private ImageView dismissView;
private CornerMaskingView rootView;
private TextView authorView;
private TextView bodyView;
private ImageView quoteBarView;
private ImageView thumbnailView;
private View attachmentVideoOverlayView;
private ViewGroup attachmentContainerView;
private TextView attachmentNameView;
private ImageView dismissView;
private long id;
private Recipient author;
@@ -66,10 +56,9 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
private TextView mediaDescriptionText;
private SlideDeck attachments;
private int messageType;
private int roundedCornerRadiusPx;
private int largeCornerRadius;
private int smallCornerRadius;
private final Path clipPath = new Path();
private final RectF drawRect = new RectF();
public QuoteView(Context context) {
super(context);
@@ -99,21 +88,36 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
this.authorView = findViewById(R.id.quote_author);
this.bodyView = findViewById(R.id.quote_text);
this.quoteBarView = findViewById(R.id.quote_bar);
this.attachmentView = findViewById(R.id.quote_attachment);
this.thumbnailView = findViewById(R.id.quote_thumbnail);
this.attachmentVideoOverlayView = findViewById(R.id.quote_video_overlay);
this.attachmentIconContainerView = findViewById(R.id.quote_attachment_icon_container);
this.attachmentIconView = findViewById(R.id.quote_attachment_icon);
this.attachmentIconBackgroundView = findViewById(R.id.quote_attachment_icon_background);
this.attachmentContainerView = findViewById(R.id.quote_attachment_container);
this.attachmentNameView = findViewById(R.id.quote_attachment_name);
this.dismissView = findViewById(R.id.quote_dismiss);
this.mediaDescriptionText = findViewById(R.id.media_name);
this.roundedCornerRadiusPx = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius);
this.mediaDescriptionText = findViewById(R.id.media_type);
this.largeCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_large);
this.smallCornerRadius = getResources().getDimensionPixelSize(R.dimen.quote_corner_radius_bottom);
rootView.setRadii(largeCornerRadius, largeCornerRadius, smallCornerRadius, smallCornerRadius);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.QuoteView, 0, 0);
messageType = typedArray.getInt(R.styleable.QuoteView_message_type, 0);
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.QuoteView, 0, 0);
int primaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorPrimary, Color.BLACK);
int secondaryColor = typedArray.getColor(R.styleable.QuoteView_quote_colorSecondary, Color.BLACK);
messageType = typedArray.getInt(R.styleable.QuoteView_message_type, 0);
typedArray.recycle();
dismissView.setVisibility(messageType == MESSAGE_TYPE_PREVIEW ? VISIBLE : GONE);
authorView.setTextColor(primaryColor);
bodyView.setTextColor(primaryColor);
attachmentNameView.setTextColor(primaryColor);
mediaDescriptionText.setTextColor(secondaryColor);
if (messageType == MESSAGE_TYPE_PREVIEW) {
int radius = getResources().getDimensionPixelOffset(R.dimen.quote_corner_radius_preview);
rootView.setTopLeftRadius(radius);
rootView.setTopRightRadius(radius);
}
}
dismissView.setOnClickListener(view -> setVisibility(GONE));
@@ -124,20 +128,6 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawRect.left = 0;
drawRect.top = 0;
drawRect.right = getWidth();
drawRect.bottom = getHeight();
clipPath.reset();
clipPath.addRoundRect(drawRect, roundedCornerRadiusPx, roundedCornerRadiusPx, Path.Direction.CW);
canvas.clipPath(clipPath);
}
public void setQuote(GlideRequests glideRequests, long id, @NonNull Recipient author, @Nullable String body, @NonNull SlideDeck attachments) {
if (this.author != null) this.author.removeListener(this);
@@ -149,7 +139,12 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
author.addListener(this);
setQuoteAuthor(author);
setQuoteText(body, attachments);
setQuoteAttachment(glideRequests, attachments, author);
setQuoteAttachment(glideRequests, attachments);
}
public void setTopCornerSizes(boolean topLeftLarge, boolean topRightLarge) {
rootView.setTopLeftRadius(topLeftLarge ? largeCornerRadius : smallCornerRadius);
rootView.setTopRightRadius(topRightLarge ? largeCornerRadius : smallCornerRadius);
}
public void dismiss() {
@@ -177,14 +172,11 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you)
: author.toShortString());
authorView.setTextColor(author.getColor().toQuoteTitleColor(getContext()));
authorView.setTextColor(author.getColor().toActionBarColor(getContext()));
// We use the raw color resource because Android 4.x was struggling with tints here
quoteBarView.setImageResource(author.getColor().toQuoteBarColorResource(getContext(), outgoing));
GradientDrawable background = (GradientDrawable) rootView.getBackground();
background.setColor(author.getColor().toQuoteBackgroundColor(getContext(), outgoing));
background.setStroke(getResources().getDimensionPixelSize(R.dimen.quote_outline_width),
author.getColor().toQuoteOutlineColor(getContext(), outgoing));
rootView.setBackgroundColor(author.getColor().toQuoteBackgroundColor(getContext(), outgoing));
}
private void setQuoteText(@Nullable String body, @NonNull SlideDeck attachments) {
@@ -197,7 +189,6 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
bodyView.setVisibility(GONE);
mediaDescriptionText.setVisibility(VISIBLE);
mediaDescriptionText.setTypeface(null, Typeface.ITALIC);
List<Slide> audioSlides = Stream.of(attachments.getSlides()).filter(Slide::hasAudio).limit(1).toList();
List<Slide> documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList();
@@ -208,13 +199,7 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
if (!audioSlides.isEmpty()) {
mediaDescriptionText.setText(R.string.QuoteView_audio);
} else if (!documentSlides.isEmpty()) {
String filename = documentSlides.get(0).getFileName().orNull();
if (!TextUtils.isEmpty(filename)) {
mediaDescriptionText.setTypeface(null, Typeface.NORMAL);
mediaDescriptionText.setText(filename);
} else {
mediaDescriptionText.setText(R.string.QuoteView_document);
}
mediaDescriptionText.setVisibility(GONE);
} else if (!videoSlides.isEmpty()) {
mediaDescriptionText.setText(R.string.QuoteView_video);
} else if (!imageSlides.isEmpty()) {
@@ -222,19 +207,15 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
}
}
private void setQuoteAttachment(@NonNull GlideRequests glideRequests,
@NonNull SlideDeck slideDeck,
@NonNull Recipient author)
{
private void setQuoteAttachment(@NonNull GlideRequests glideRequests, @NonNull SlideDeck slideDeck) {
List<Slide> imageVideoSlides = Stream.of(slideDeck.getSlides()).filter(s -> s.hasImage() || s.hasVideo()).limit(1).toList();
List<Slide> audioSlides = Stream.of(attachments.getSlides()).filter(Slide::hasAudio).limit(1).toList();
List<Slide> documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList();
List<Slide> documentSlides = Stream.of(attachments.getSlides()).filter(Slide::hasDocument).limit(1).toList();
attachmentVideoOverlayView.setVisibility(GONE);
if (!imageVideoSlides.isEmpty() && imageVideoSlides.get(0).getThumbnailUri() != null) {
attachmentView.setVisibility(VISIBLE);
attachmentIconContainerView.setVisibility(GONE);
thumbnailView.setVisibility(VISIBLE);
attachmentContainerView.setVisibility(GONE);
dismissView.setBackgroundResource(R.drawable.dismiss_background);
if (imageVideoSlides.get(0).hasVideo()) {
attachmentVideoOverlayView.setVisibility(VISIBLE);
@@ -242,26 +223,14 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
glideRequests.load(new DecryptableUri(imageVideoSlides.get(0).getThumbnailUri()))
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(attachmentView);
} else if (!audioSlides.isEmpty() || !documentSlides.isEmpty()){
boolean outgoing = messageType != MESSAGE_TYPE_INCOMING;
dismissView.setBackgroundResource(R.drawable.circle_alpha);
attachmentView.setVisibility(GONE);
attachmentIconContainerView.setVisibility(VISIBLE);
if (!audioSlides.isEmpty()) {
attachmentIconView.setImageResource(R.drawable.ic_mic_white_48dp);
} else {
attachmentIconView.setImageResource(R.drawable.ic_insert_drive_file_white_24dp);
}
attachmentIconView.setColorFilter(author.getColor().toQuoteIconForegroundColor(getContext(), outgoing), PorterDuff.Mode.SRC_IN);
attachmentIconBackgroundView.setColorFilter(author.getColor().toQuoteIconBackgroundColor(getContext(), outgoing), PorterDuff.Mode.SRC_IN);
.into(thumbnailView);
} else if (!documentSlides.isEmpty()){
thumbnailView.setVisibility(GONE);
attachmentContainerView.setVisibility(VISIBLE);
attachmentNameView.setText(documentSlides.get(0).getFileName().or(""));
} else {
attachmentView.setVisibility(GONE);
attachmentIconContainerView.setVisibility(GONE);
thumbnailView.setVisibility(GONE);
attachmentContainerView.setVisibility(GONE);
dismissView.setBackgroundDrawable(null);
}

View File

@@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
@@ -34,10 +36,11 @@ import java.util.Map;
public class SharedContactView extends LinearLayout implements RecipientModifiedListener {
private ImageView avatarView;
private TextView nameView;
private TextView numberView;
private TextView actionButtonView;
private ImageView avatarView;
private TextView nameView;
private TextView numberView;
private TextView actionButtonView;
private ConversationItemFooter footer;
private Contact contact;
private Locale locale;
@@ -48,32 +51,44 @@ public class SharedContactView extends LinearLayout implements RecipientModified
public SharedContactView(Context context) {
super(context);
initialize();
initialize(null);
}
public SharedContactView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialize();
initialize(attrs);
}
public SharedContactView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
initialize(attrs);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public SharedContactView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
initialize(attrs);
}
private void initialize() {
private void initialize(@Nullable AttributeSet attrs) {
inflate(getContext(), R.layout.shared_contact_view, this);
avatarView = findViewById(R.id.contact_avatar);
nameView = findViewById(R.id.contact_name);
numberView = findViewById(R.id.contact_number);
actionButtonView = findViewById(R.id.contact_action_button);
footer = findViewById(R.id.contact_footer);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.SharedContactView, 0, 0);
int titleColor = typedArray.getInt(R.styleable.SharedContactView_contact_titleColor, Color.BLACK);
int captionColor = typedArray.getInt(R.styleable.SharedContactView_contact_captionColor, Color.BLACK);
typedArray.recycle();
nameView.setTextColor(titleColor);
numberView.setTextColor(captionColor);
footer.setColor(captionColor);
}
}
public void setContact(@NonNull Contact contact, @NonNull GlideRequests glideRequests, @NonNull Locale locale) {
@@ -89,6 +104,18 @@ public class SharedContactView extends LinearLayout implements RecipientModified
presentActionButtons(ContactUtil.getRecipients(getContext(), contact));
}
public void setSingularStyle() {
actionButtonView.setBackgroundResource(R.drawable.shared_contact_button_background_alone);
}
public void setClusteredIncomingStyle() {
actionButtonView.setBackgroundResource(R.drawable.shared_contact_button_background_clustered_received);
}
public void setClusteredOutgoingStyle() {
actionButtonView.setBackgroundResource(R.drawable.shared_contact_button_background_clustered_sent);
}
public void setEventListener(@NonNull EventListener eventListener) {
this.eventListener = eventListener;
}
@@ -97,6 +124,10 @@ public class SharedContactView extends LinearLayout implements RecipientModified
return avatarView;
}
public ConversationItemFooter getFooter() {
return footer;
}
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(() -> presentActionButtons(Collections.singletonList(recipient)));

View File

@@ -0,0 +1,49 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
public class SpinningImageView extends AppCompatImageView {
private static final float DEGREES_PER_SECOND = 180;
private long lastDrawTime;
private float currentRotation;
public SpinningImageView(Context context) {
super(context);
init();
}
public SpinningImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public SpinningImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
lastDrawTime = System.currentTimeMillis();
setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - lastDrawTime;
float rotate = ((float) elapsedTime / 1000) * DEGREES_PER_SECOND;
currentRotation += rotate;
canvas.rotate(currentRotation, canvas.getWidth() / 2, canvas.getHeight() / 2);
lastDrawTime = currentTime;
super.onDraw(canvas);
invalidate();
}
}

View File

@@ -2,13 +2,12 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -39,7 +38,7 @@ import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOption
public class ThumbnailView extends FrameLayout {
private static final String TAG = ThumbnailView.class.getSimpleName();
private static final String TAG = ThumbnailView.class.getSimpleName();
private static final int WIDTH = 0;
private static final int HEIGHT = 1;
private static final int MIN_WIDTH = 0;
@@ -48,9 +47,7 @@ public class ThumbnailView extends FrameLayout {
private static final int MAX_HEIGHT = 3;
private ImageView image;
private ImageView playOverlay;
private int backgroundColorHint;
private int radius;
private View playOverlay;
private OnClickListener parentClickListener;
private final int[] dimens = new int[2];
@@ -62,6 +59,8 @@ public class ThumbnailView extends FrameLayout {
private SlideClickListener downloadClickListener = null;
private Slide slide = null;
private int radius;
public ThumbnailView(Context context) {
this(context, null);
}
@@ -75,20 +74,20 @@ public class ThumbnailView extends FrameLayout {
inflate(context, R.layout.thumbnail_view, this);
this.radius = getResources().getDimensionPixelSize(R.dimen.message_bubble_corner_radius);
this.image = findViewById(R.id.thumbnail_image);
this.playOverlay = findViewById(R.id.play_overlay);
super.setOnClickListener(new ThumbnailClickDispatcher());
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0);
backgroundColorHint = typedArray.getColor(R.styleable.ThumbnailView_backgroundColorHint, Color.BLACK);
bounds[MIN_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minWidth, 0);
bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0);
bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0);
bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0);
bounds[MIN_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minWidth, 0);
bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0);
bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0);
bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0);
typedArray.recycle();
}
radius = getResources().getDimensionPixelOffset(R.dimen.message_corner_collapse_radius);
}
@Override
@@ -210,8 +209,17 @@ public class ThumbnailView extends FrameLayout {
return transferControls.get();
}
public void setBackgroundColorHint(int color) {
this.backgroundColorHint = color;
public void setBounds(int minWidth, int maxWidth, int minHeight, int maxHeight) {
bounds[MIN_WIDTH] = minWidth;
bounds[MAX_WIDTH] = maxWidth;
bounds[MIN_HEIGHT] = minHeight;
bounds[MAX_HEIGHT] = maxHeight;
forceLayout();
}
public void setImageBackground(@DrawableRes int resId) {
image.setBackgroundResource(resId);
}
@UiThread
@@ -273,10 +281,9 @@ public class ThumbnailView extends FrameLayout {
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri) {
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE);
glideRequests.load(new DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.transform(new RoundedCorners(radius))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.transforms(new CenterCrop(), new RoundedCorners(radius))
.transition(withCrossFade())
.centerCrop()
.into(image);
}