mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-23 17:48:50 +00:00
Improve the look of message bubbles.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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) {
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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)));
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user