Styling improvements and code cleanup.

This commit is contained in:
Greyson Parrelli
2018-07-12 16:03:32 -07:00
parent 845fcf0864
commit 8579c30909
76 changed files with 519 additions and 767 deletions

View File

@@ -1240,18 +1240,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
View composeBubble = ViewUtil.findById(this, R.id.compose_bubble);
container.addOnKeyboardShownListener(this);
inputPanel.setListener(this);
inputPanel.setMediaListener(this);
int[] attributes = new int[]{R.attr.conversation_item_bubble_background};
TypedArray colors = obtainStyledAttributes(attributes);
int defaultColor = colors.getColor(0, Color.WHITE);
composeBubble.getBackground().setColorFilter(defaultColor, PorterDuff.Mode.MULTIPLY);
colors.recycle();
attachmentTypeSelector = null;
attachmentManager = new AttachmentManager(this, this);
audioRecorder = new AudioRecorder(this);

View File

@@ -49,6 +49,7 @@ import org.thoughtcrime.securesms.components.AudioView;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.ConversationItemFooter;
import org.thoughtcrime.securesms.components.ConversationItemThumbnail;
import org.thoughtcrime.securesms.components.CornerMaskingView;
import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.QuoteView;
import org.thoughtcrime.securesms.components.SharedContactView;
@@ -99,7 +100,9 @@ import java.util.Set;
public class ConversationItem extends LinearLayout
implements RecipientModifiedListener, BindableConversationItem
{
private final static String TAG = ConversationItem.class.getSimpleName();
private static final String TAG = ConversationItem.class.getSimpleName();
private static final int MAX_MEASURE_CALLS = 3;
private MessageRecord messageRecord;
private Locale locale;
@@ -107,7 +110,7 @@ public class ConversationItem extends LinearLayout
private Recipient recipient;
private GlideRequests glideRequests;
protected View bodyBubble;
protected CornerMaskingView bodyBubble;
private QuoteView quoteView;
private TextView bodyText;
private ConversationItemFooter footer;
@@ -127,6 +130,7 @@ public class ConversationItem extends LinearLayout
private @Nullable EventListener eventListener;
private int defaultBubbleColor;
private int measureCalls;
private final PassthroughClickListener passthroughClickListener = new PassthroughClickListener();
private final AttachmentDownloadClickListener downloadClickListener = new AttachmentDownloadClickListener();
@@ -199,18 +203,19 @@ public class ConversationItem extends LinearLayout
this.recipient.addListener(this);
this.conversationRecipient.addListener(this);
presentMessageBackground(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
presentMedia(messageRecord, previousMessageRecord, nextMessageRecord, conversationRecipient, groupThread);
presentInteractionState(messageRecord, pulseHighlight);
presentBodyText(messageRecord);
presentBubbleState(messageRecord);
presentStatusIcons(messageRecord);
presentContactPhoto(recipient);
presentGroupMessageStatus(messageRecord, recipient);
presentAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
presentQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
presentMessageSpacing(messageRecord, nextMessageRecord, groupThread);
presentFooter(messageRecord, nextMessageRecord, locale, groupThread);
setMediaAttributes(messageRecord, nextMessageRecord, previousMessageRecord, conversationRecipient, groupThread);
setInteractionState(messageRecord, pulseHighlight);
setBodyText(messageRecord);
setBubbleState(messageRecord);
setStatusIcons(messageRecord);
setContactPhoto(recipient);
setGroupMessageStatus(messageRecord, recipient);
setAuthor(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
setQuote(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
setMessageSpacing(context, messageRecord, nextMessageRecord);
setGutterSizes(messageRecord, groupThread);
setFooter(messageRecord, nextMessageRecord, locale, groupThread);
setMessageShape(messageRecord, previousMessageRecord, nextMessageRecord, groupThread);
}
@Override
@@ -247,7 +252,14 @@ public class ConversationItem extends LinearLayout
}
if (needsMeasure) {
measure(widthMeasureSpec, heightMeasureSpec);
if (measureCalls < MAX_MEASURE_CALLS) {
measure(widthMeasureSpec, heightMeasureSpec);
measureCalls++;
} else {
Log.w(TAG, "Hit measure() cap of " + MAX_MEASURE_CALLS);
}
} else {
measureCalls = 0;
}
}
@@ -287,7 +299,7 @@ public class ConversationItem extends LinearLayout
/// MessageRecord Attribute Parsers
private void presentBubbleState(MessageRecord messageRecord) {
private void setBubbleState(MessageRecord messageRecord) {
if (messageRecord.isOutgoing()) {
bodyBubble.getBackground().setColorFilter(defaultBubbleColor, PorterDuff.Mode.MULTIPLY);
} else {
@@ -312,7 +324,7 @@ public class ConversationItem extends LinearLayout
}
}
private void presentInteractionState(MessageRecord messageRecord, boolean pulseHighlight) {
private void setInteractionState(MessageRecord messageRecord, boolean pulseHighlight) {
if (batchSelected.contains(messageRecord)) {
setBackgroundResource(R.drawable.conversation_item_background);
setSelected(true);
@@ -370,7 +382,7 @@ public class ConversationItem extends LinearLayout
return messageRecord.isMms() && !((MmsMessageRecord)messageRecord).getSharedContacts().isEmpty();
}
private void presentBodyText(MessageRecord messageRecord) {
private void setBodyText(MessageRecord messageRecord) {
bodyText.setClickable(false);
bodyText.setFocusable(false);
bodyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, TextSecurePreferences.getMessageBodyTextSize(context));
@@ -383,67 +395,62 @@ public class ConversationItem extends LinearLayout
}
}
private void presentMedia(@NonNull MessageRecord currentMessage,
@NonNull Optional<MessageRecord> previousMessage,
@NonNull Optional<MessageRecord> nextMessage,
@NonNull Recipient conversationRecipient,
boolean isGroupThread)
private void setMediaAttributes(@NonNull MessageRecord messageRecord,
@NonNull Optional<MessageRecord> previousRecord,
@NonNull Optional<MessageRecord> nextRecord,
@NonNull Recipient conversationRecipient,
boolean isGroupThread)
{
boolean showControls = !currentMessage.isFailed() && !Util.isOwnNumber(context, conversationRecipient.getAddress());
boolean showControls = !messageRecord.isFailed() && !Util.isOwnNumber(context, conversationRecipient.getAddress());
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ViewUtil.setPaddingTop(bodyBubble, readDimen(R.dimen.message_bubble_top_padding));
ViewUtil.setPaddingBottom(bodyBubble, 0);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().showShade(false);
footer.setVisibility(VISIBLE);
if (hasSharedContact(currentMessage)) {
if (hasSharedContact(messageRecord)) {
sharedContactStub.get().setVisibility(VISIBLE);
if (audioViewStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
sharedContactStub.get().setContact(((MediaMmsMessageRecord) currentMessage).getSharedContacts().get(0), glideRequests, locale);
sharedContactStub.get().setContact(((MediaMmsMessageRecord) messageRecord).getSharedContacts().get(0), glideRequests, locale);
sharedContactStub.get().setEventListener(sharedContactEventListener);
sharedContactStub.get().setOnClickListener(sharedContactClickListener);
sharedContactStub.get().setOnLongClickListener(passthroughClickListener);
setSharedContactCorners(currentMessage, previousMessage, nextMessage, isGroupThread);
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(GONE);
} else if (hasAudio(currentMessage)) {
} else if (hasAudio(messageRecord)) {
audioViewStub.get().setVisibility(View.VISIBLE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
//noinspection ConstantConditions
audioViewStub.get().setAudio(((MediaMmsMessageRecord) currentMessage).getSlideDeck().getAudioSlide(), showControls);
audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls);
audioViewStub.get().setDownloadClickListener(downloadClickListener);
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
} else if (hasDocument(currentMessage)) {
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
} else if (hasDocument(messageRecord)) {
documentViewStub.get().setVisibility(View.VISIBLE);
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
//noinspection ConstantConditions
documentViewStub.get().setDocument(((MediaMmsMessageRecord)currentMessage).getSlideDeck().getDocumentSlide(), showControls);
documentViewStub.get().setDocument(((MediaMmsMessageRecord)messageRecord).getSlideDeck().getDocumentSlide(), showControls);
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
documentViewStub.get().setDownloadClickListener(downloadClickListener);
documentViewStub.get().setOnLongClickListener(passthroughClickListener);
} else if (hasThumbnail(currentMessage)) {
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
} else if (hasThumbnail(messageRecord)) {
mediaThumbnailStub.get().setVisibility(View.VISIBLE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
setThumbnailCorners(currentMessage, previousMessage, nextMessage, isGroupThread);
//noinspection ConstantConditions
Slide thumbnailSlide = ((MmsMessageRecord) currentMessage).getSlideDeck().getThumbnailSlide();
Slide thumbnailSlide = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlide();
Attachment attachment = thumbnailSlide.asAttachment();
mediaThumbnailStub.get().setImageResource(glideRequests,
thumbnailSlide,
@@ -455,31 +462,27 @@ public class ConversationItem extends LinearLayout
mediaThumbnailStub.get().setDownloadClickListener(downloadClickListener);
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getDisplayBody()));
setThumbnailOutlineCorners(messageRecord, nextRecord, previousRecord, isGroupThread);
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (!hasQuote(currentMessage)) {
ViewUtil.setPaddingTop(bodyBubble, 0);
} else {
ViewUtil.setPaddingTop(bodyBubble, readDimen(R.dimen.message_bubble_top_padding));
}
if (TextUtils.isEmpty(currentMessage.getDisplayBody())) {
mediaThumbnailStub.get().showShade(true);
mediaThumbnailStub.get().setBackgroundResource(getCornerBackgroundRes(currentMessage, previousMessage, nextMessage, isGroupThread));
}
footer.setVisibility(VISIBLE);
} else {
if (mediaThumbnailStub.resolved()) mediaThumbnailStub.get().setVisibility(View.GONE);
if (audioViewStub.resolved()) audioViewStub.get().setVisibility(View.GONE);
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().setVisibility(GONE);
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
}
}
private void setThumbnailCorners(@NonNull MessageRecord current,
@NonNull Optional<MessageRecord> previous,
@NonNull Optional<MessageRecord> next,
boolean isGroupThread)
private void setThumbnailOutlineCorners(@NonNull MessageRecord current,
@NonNull Optional<MessageRecord> previous,
@NonNull Optional<MessageRecord> next,
boolean isGroupThread)
{
int defaultRadius = readDimen(R.dimen.message_corner_radius);
int collapseRadius = readDimen(R.dimen.message_corner_collapse_radius);
@@ -531,22 +534,10 @@ public class ConversationItem extends LinearLayout
topRight = 0;
}
mediaThumbnailStub.get().setCornerRadii(topLeft, topRight, bottomRight, bottomLeft);
mediaThumbnailStub.get().setOutlineCorners(topLeft, topRight, bottomRight, bottomLeft);
}
private void setSharedContactCorners(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
if (isSingularMessage(current, previous, next, isGroupThread) || isEndOfMessageCluster(current, next, isGroupThread)) {
sharedContactStub.get().setSingularStyle();
} else {
if (current.isOutgoing()) {
sharedContactStub.get().setClusteredOutgoingStyle();
} else {
sharedContactStub.get().setClusteredIncomingStyle();
}
}
}
private void presentContactPhoto(@NonNull Recipient recipient) {
private void setContactPhoto(@NonNull Recipient recipient) {
if (contactPhoto == null) return;
if (messageRecord.isOutgoing() || !groupThread) {
@@ -571,19 +562,19 @@ public class ConversationItem extends LinearLayout
return messageBody;
}
private void presentStatusIcons(MessageRecord messageRecord) {
private void setStatusIcons(MessageRecord messageRecord) {
bodyText.setCompoundDrawablesWithIntrinsicBounds(0, 0, messageRecord.isKeyExchange() ? R.drawable.ic_menu_login : 0, 0);
if (messageRecord.isFailed()) {
setFailedStatusIcons();
alertView.setFailed();
} else if (messageRecord.isPendingInsecureSmsFallback()) {
setFallbackStatusIcons();
alertView.setPendingApproval();
} else {
alertView.setNone();
}
}
private void presentQuote(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
private void setQuote(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
if (current.isMms() && !current.isMmsNotification() && ((MediaMmsMessageRecord)current).getQuote() != null) {
Quote quote = ((MediaMmsMessageRecord)current).getQuote();
assert quote != null;
@@ -598,8 +589,8 @@ public class ConversationItem extends LinearLayout
passthroughClickListener.onClick(view);
}
});
quoteView.setOnLongClickListener(passthroughClickListener);
ViewUtil.setPaddingTop(bodyBubble, 0);
if (isStartOfMessageCluster(current, previous, isGroupThread)) {
if (current.isOutgoing()) {
@@ -616,14 +607,20 @@ public class ConversationItem extends LinearLayout
quoteView.setTopCornerSizes(false, true);
}
}
if (mediaThumbnailStub.resolved()) {
ViewUtil.setTopMargin(mediaThumbnailStub.get(), readDimen(R.dimen.message_bubble_top_padding));
}
} else {
quoteView.dismiss();
if (mediaThumbnailStub.resolved()) {
ViewUtil.setTopMargin(mediaThumbnailStub.get(), 0);
}
}
}
private void presentMessageSpacing(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
ViewUtil.setPaddingBottom(this, getMessageSpacing(context, current, next));
private void setGutterSizes(@NonNull MessageRecord current, boolean isGroupThread) {
if (isGroupThread) {
if (current.isOutgoing()) {
ViewUtil.setLeftMargin(container, readDimen(R.dimen.conversation_group_left_gutter));
@@ -639,17 +636,8 @@ public class ConversationItem extends LinearLayout
}
}
private void presentMessageBackground(@NonNull MessageRecord current,
@NonNull Optional<MessageRecord> previous,
@NonNull Optional<MessageRecord> next,
boolean isGroupThread)
{
bodyBubble.setBackgroundResource(getCornerBackgroundRes(current, previous, next, isGroupThread));
}
private void presentFooter(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> next, @NonNull Locale locale, boolean isGroupThread) {
private void setFooter(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> next, @NonNull Locale locale, boolean isGroupThread) {
ViewUtil.updateLayoutParams(footer, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ViewUtil.setPaddingBottom(bodyBubble, 0);
footer.setVisibility(GONE);
if (sharedContactStub.resolved()) sharedContactStub.get().getFooter().setVisibility(GONE);
@@ -660,8 +648,6 @@ public class ConversationItem extends LinearLayout
ConversationItemFooter activeFooter = getActiveFooter(current);
activeFooter.setVisibility(VISIBLE);
activeFooter.setMessageRecord(current, locale);
} else if (!TextUtils.isEmpty(messageRecord.getDisplayBody())) {
ViewUtil.setPaddingBottom(bodyBubble, readDimen(R.dimen.message_bubble_collapsed_footer_padding));
}
}
@@ -679,14 +665,6 @@ public class ConversationItem extends LinearLayout
return context.getResources().getDimensionPixelOffset(dimenId);
}
private void setFailedStatusIcons() {
alertView.setFailed();
}
private void setFallbackStatusIcons() {
alertView.setPendingApproval();
}
private boolean shouldInterceptClicks(MessageRecord messageRecord) {
return batchSelected.isEmpty() &&
((messageRecord.isFailed() && !messageRecord.isMmsNotification()) ||
@@ -695,23 +673,24 @@ public class ConversationItem extends LinearLayout
}
@SuppressLint("SetTextI18n")
private void presentGroupMessageStatus(@NonNull MessageRecord current, @NonNull Recipient recipient) {
this.groupSender.setText(recipient.toShortString());
private void setGroupMessageStatus(MessageRecord messageRecord, Recipient recipient) {
if (groupThread && !messageRecord.isOutgoing()) {
this.groupSender.setText(recipient.toShortString());
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName()) && !current.isOutgoing()) {
this.groupSenderProfileName.setText("~" + recipient.getProfileName());
this.groupSenderProfileName.setVisibility(View.VISIBLE);
} else {
this.groupSenderProfileName.setText(null);
this.groupSenderProfileName.setVisibility(View.GONE);
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
this.groupSenderProfileName.setText("~" + recipient.getProfileName());
this.groupSenderProfileName.setVisibility(View.VISIBLE);
} else {
this.groupSenderProfileName.setText(null);
this.groupSenderProfileName.setVisibility(View.GONE);
}
}
}
private void presentAuthor(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
private void setAuthor(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
if (isGroupThread && !current.isOutgoing()) {
if (!previous.isPresent() || previous.get().isUpdate() || !current.getRecipient().getAddress().equals(previous.get().getRecipient().getAddress())) {
groupSenderHolder.setVisibility(VISIBLE);
ViewUtil.setPaddingTop(bodyBubble, readDimen(R.dimen.message_bubble_top_padding));
} else {
groupSenderHolder.setVisibility(GONE);
}
@@ -730,19 +709,45 @@ public class ConversationItem extends LinearLayout
}
}
private int getCornerBackgroundRes(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
private void setMessageShape(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> previous, @NonNull Optional<MessageRecord> next, boolean isGroupThread) {
if (isSingularMessage(current, previous, next, isGroupThread)) {
return current.isOutgoing() ? R.drawable.message_bubble_background_sent_alone
: R.drawable.message_bubble_background_received_alone;
bodyBubble.setRadius(readDimen(R.dimen.message_corner_radius));
} else if (isStartOfMessageCluster(current, previous, isGroupThread)) {
return current.isOutgoing() ? R.drawable.message_bubble_background_sent_start
: R.drawable.message_bubble_background_received_start;
if (current.isOutgoing()) {
bodyBubble.setRadii(readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_collapse_radius),
readDimen(R.dimen.message_corner_radius));
} else {
bodyBubble.setRadii(readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_collapse_radius));
}
} else if (isEndOfMessageCluster(current, next, isGroupThread)) {
return current.isOutgoing() ? R.drawable.message_bubble_background_sent_end
: R.drawable.message_bubble_background_received_end;
if (current.isOutgoing()) {
bodyBubble.setRadii(readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_collapse_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_radius));
} else {
bodyBubble.setRadii(readDimen(R.dimen.message_corner_collapse_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_radius));
}
} else {
return current.isOutgoing() ? R.drawable.message_bubble_background_sent_middle
: R.drawable.message_bubble_background_received_middle;
if (current.isOutgoing()) {
bodyBubble.setRadii(readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_collapse_radius),
readDimen(R.dimen.message_corner_collapse_radius),
readDimen(R.dimen.message_corner_radius));
} else {
bodyBubble.setRadii(readDimen(R.dimen.message_corner_collapse_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_radius),
readDimen(R.dimen.message_corner_collapse_radius));
}
}
}
@@ -770,16 +775,19 @@ public class ConversationItem extends LinearLayout
return isStartOfMessageCluster(current, previous, isGroupThread) && isEndOfMessageCluster(current, next, isGroupThread);
}
private int getMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional<MessageRecord> next) {
private void setMessageSpacing(@NonNull Context context, @NonNull MessageRecord current, @NonNull Optional<MessageRecord> next) {
int spacing = readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse);
if (next.isPresent()) {
boolean recipientsMatch = current.getRecipient().getAddress().equals(next.get().getRecipient().getAddress());
boolean outgoingMatch = current.isOutgoing() == next.get().isOutgoing();
if (!recipientsMatch || !outgoingMatch) {
return readDimen(context, R.dimen.conversation_vertical_message_spacing_default);
spacing = readDimen(context, R.dimen.conversation_vertical_message_spacing_default);
}
}
return readDimen(context, R.dimen.conversation_vertical_message_spacing_collapse);
ViewUtil.setPaddingBottom(this, spacing);
}
private int readDimen(@NonNull Context context, @DimenRes int dimenId) {
@@ -801,9 +809,9 @@ public class ConversationItem extends LinearLayout
@Override
public void onModified(final Recipient modified) {
Util.runOnMain(() -> {
presentBubbleState(messageRecord);
presentContactPhoto(recipient);
presentGroupMessageStatus(messageRecord, recipient);
setBubbleState(messageRecord);
setContactPhoto(recipient);
setGroupMessageStatus(messageRecord, recipient);
setAudioViewTint(messageRecord, conversationRecipient);
});
}

View File

@@ -84,7 +84,6 @@ public class ConversationItemFooter extends LinearLayout {
simView.setTextColor(color);
timerView.setColorFilter(color);
insecureIndicatorView.setColorFilter(color);
deliveryStatusView.setTint(color);
}
private void presentDate(@NonNull MessageRecord messageRecord, @NonNull Locale locale) {

View File

@@ -2,15 +2,16 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.net.Uri;
import android.support.annotation.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;
@@ -18,17 +19,35 @@ 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;
import org.thoughtcrime.securesms.util.ThemeUtil;
public class ConversationItemThumbnail extends FrameLayout {
private static final String TAG = ConversationItemThumbnail.class.getSimpleName();
private static final Paint LIGHT_THEME_OUTLINE_PAINT = new Paint();
private static final Paint DARK_THEME_OUTLINE_PAINT = new Paint();
static {
LIGHT_THEME_OUTLINE_PAINT.setColor(Color.argb((int) (255 * 0.2), 0, 0, 0));
LIGHT_THEME_OUTLINE_PAINT.setStyle(Paint.Style.STROKE);
LIGHT_THEME_OUTLINE_PAINT.setStrokeWidth(1f);
LIGHT_THEME_OUTLINE_PAINT.setAntiAlias(true);
DARK_THEME_OUTLINE_PAINT.setColor(Color.argb((int) (255 * 0.2), 255, 255, 255));
DARK_THEME_OUTLINE_PAINT.setStyle(Paint.Style.STROKE);
DARK_THEME_OUTLINE_PAINT.setStrokeWidth(1f);
DARK_THEME_OUTLINE_PAINT.setAntiAlias(true);
}
private final float[] radii = new float[8];
private final RectF bounds = new RectF();
private final Path corners = new Path();
private ThumbnailView thumbnail;
private ImageView shade;
private CornerMaskingView cornerMask;
private ConversationItemFooter footer;
private Paint outlinePaint;
public ConversationItemThumbnail(Context context) {
super(context);
@@ -48,12 +67,11 @@ public class ConversationItemThumbnail extends FrameLayout {
private void init(@Nullable AttributeSet attrs) {
inflate(getContext(), R.layout.conversation_item_thumbnail, this);
this.thumbnail = findViewById(R.id.conversation_thumbnail_image);
this.shade = findViewById(R.id.conversation_thumbnail_shade);
this.cornerMask = findViewById(R.id.conversation_thumbnail_corner_mask);
this.footer = findViewById(R.id.conversation_thumbnail_footer);
this.thumbnail = findViewById(R.id.conversation_thumbnail_image);
this.shade = findViewById(R.id.conversation_thumbnail_shade);
this.footer = findViewById(R.id.conversation_thumbnail_footer);
this.outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT;
setCornerRadius(getResources().getDimensionPixelSize(R.dimen.message_corner_radius));
setTouchDelegate(thumbnail.getTouchDelegate());
if (attrs != null) {
@@ -66,6 +84,24 @@ public class ConversationItemThumbnail extends FrameLayout {
}
}
@SuppressWarnings("SuspiciousNameCombination")
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
final float halfStrokeWidth = outlinePaint.getStrokeWidth() / 2;
bounds.left = halfStrokeWidth;
bounds.top = halfStrokeWidth;
bounds.right = canvas.getWidth() - halfStrokeWidth;
bounds.bottom = canvas.getHeight() - halfStrokeWidth;
corners.reset();
corners.addRoundRect(bounds, radii, Path.Direction.CW);
canvas.drawPath(corners, outlinePaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -96,22 +132,17 @@ public class ConversationItemThumbnail extends FrameLayout {
forceLayout();
}
public void setOutlineCorners(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;
}
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)

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -8,40 +9,56 @@ import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import org.thoughtcrime.securesms.R;
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();
private final float[] radii = new float[8];
private final Paint dstPaint = new Paint();
private final Paint clearPaint = new Paint();
private final Path outline = new Path();
private final Path corners = new Path();
private final RectF bounds = new RectF();
public CornerMaskingView(@NonNull Context context) {
super(context);
init();
init(null);
}
public CornerMaskingView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
init(attrs);
}
public CornerMaskingView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
init(attrs);
}
private void init() {
private void init(@Nullable AttributeSet attrs) {
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));
dstPaint.setColor(Color.BLACK);
dstPaint.setStyle(Paint.Style.FILL);
dstPaint.setAntiAlias(true);
dstPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
clearPaint.setColor(Color.BLACK);
clearPaint.setStyle(Paint.Style.FILL);
clearPaint.setAntiAlias(true);
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.CornerMaskingView, 0, 0);
setRadius(typedArray.getDimensionPixelOffset(R.styleable.CornerMaskingView_cmv_radius, 0));
typedArray.recycle();
}
}
@Override
@@ -56,7 +73,18 @@ public class CornerMaskingView extends FrameLayout {
corners.reset();
corners.addRoundRect(bounds, radii, Path.Direction.CW);
canvas.drawPath(corners, paint);
// Note: There's a bug in the P beta where most PorterDuff modes aren't working. But CLEAR does.
// So we find and inverse path and use Mode.CLEAR for versions that support Path.op().
// See issue https://issuetracker.google.com/issues/111394085.
if (Build.VERSION.SDK_INT >= 19) {
outline.reset();
outline.addRect(bounds, Path.Direction.CW);
outline.op(corners, Path.Op.DIFFERENCE);
canvas.drawPath(outline, clearPaint);
} else {
corners.addRoundRect(bounds, radii, Path.Direction.CW);
canvas.drawPath(corners, dstPaint);
}
}
public void setRadius(int radius) {

View File

@@ -7,6 +7,9 @@ import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -19,6 +22,15 @@ public class DeliveryStatusView extends FrameLayout {
private static final String TAG = DeliveryStatusView.class.getSimpleName();
private static final RotateAnimation ROTATION_ANIMATION = new RotateAnimation(0, 360f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
static {
ROTATION_ANIMATION.setInterpolator(new LinearInterpolator());
ROTATION_ANIMATION.setDuration(1500);
ROTATION_ANIMATION.setRepeatCount(Animation.INFINITE);
}
private final ImageView pendingIndicator;
private final ImageView sentIndicator;
private final ImageView deliveredIndicator;
@@ -56,6 +68,7 @@ public class DeliveryStatusView extends FrameLayout {
public void setPending() {
this.setVisibility(View.VISIBLE);
pendingIndicator.setVisibility(View.VISIBLE);
pendingIndicator.startAnimation(ROTATION_ANIMATION);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
readIndicator.setVisibility(View.GONE);
@@ -64,6 +77,7 @@ public class DeliveryStatusView extends FrameLayout {
public void setSent() {
this.setVisibility(View.VISIBLE);
pendingIndicator.setVisibility(View.GONE);
pendingIndicator.clearAnimation();
sentIndicator.setVisibility(View.VISIBLE);
deliveredIndicator.setVisibility(View.GONE);
readIndicator.setVisibility(View.GONE);
@@ -72,6 +86,7 @@ public class DeliveryStatusView extends FrameLayout {
public void setDelivered() {
this.setVisibility(View.VISIBLE);
pendingIndicator.setVisibility(View.GONE);
pendingIndicator.clearAnimation();
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.VISIBLE);
readIndicator.setVisibility(View.GONE);
@@ -80,6 +95,7 @@ public class DeliveryStatusView extends FrameLayout {
public void setRead() {
this.setVisibility(View.VISIBLE);
pendingIndicator.setVisibility(View.GONE);
pendingIndicator.clearAnimation();
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
readIndicator.setVisibility(View.VISIBLE);

View File

@@ -77,7 +77,7 @@ public class ExpirationTimerView extends android.support.v7.widget.AppCompatImag
long progressed = System.currentTimeMillis() - startedAt;
float percentComplete = (float)progressed / (float)expiresIn;
return percentComplete;
return Math.min(percentComplete, 1);
}
private long calculateAnimationDelay(long startedAt, long expiresIn) {

View File

@@ -41,6 +41,7 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
private static final int MESSAGE_TYPE_INCOMING = 2;
private CornerMaskingView rootView;
private View backgroundView;
private TextView authorView;
private TextView bodyView;
private ImageView quoteBarView;
@@ -85,6 +86,7 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
inflate(getContext(), R.layout.quote_view, this);
this.rootView = findViewById(R.id.quote_root);
this.backgroundView = findViewById(R.id.quote_background);
this.authorView = findViewById(R.id.quote_author);
this.bodyView = findViewById(R.id.quote_text);
this.quoteBarView = findViewById(R.id.quote_bar);
@@ -172,11 +174,10 @@ public class QuoteView extends LinearLayout implements RecipientModifiedListener
authorView.setText(isOwnNumber ? getContext().getString(R.string.QuoteView_you)
: author.toShortString());
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));
rootView.setBackgroundColor(author.getColor().toQuoteBackgroundColor(getContext(), outgoing));
backgroundView.setBackgroundColor(author.getColor().toQuoteBackgroundColor(getContext(), outgoing));
}
private void setQuoteText(@Nullable String body, @NonNull SlideDeck attachments) {

View File

@@ -56,7 +56,7 @@ public class RemovableEditableMediaView extends FrameLayout {
if (current != null) current.setVisibility(View.GONE);
if (view != null) {
view.setPadding(0, removeSize / 2, removeSize / 2, 0);
view.setPadding(view.getPaddingLeft(), removeSize / 2, removeSize / 2, view.getPaddingRight());
edit.setPadding(0, 0, removeSize / 2, 0);
view.setVisibility(View.VISIBLE);

View File

@@ -104,18 +104,6 @@ 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;
}

View File

@@ -1,49 +0,0 @@
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

@@ -84,10 +84,12 @@ public class ThumbnailView extends FrameLayout {
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);
radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, getResources().getDimensionPixelSize(R.dimen.message_corner_collapse_radius));
typedArray.recycle();
} else {
radius = getResources().getDimensionPixelSize(R.dimen.message_corner_collapse_radius);
}
radius = getResources().getDimensionPixelOffset(R.dimen.message_corner_collapse_radius);
}
@Override
@@ -218,10 +220,6 @@ public class ThumbnailView extends FrameLayout {
forceLayout();
}
public void setImageBackground(@DrawableRes int resId) {
image.setBackgroundResource(resId);
}
@UiThread
public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide,
boolean showControls, boolean isPreview)

View File

@@ -34,7 +34,7 @@ import java.util.concurrent.TimeUnit;
public class DateUtils extends android.text.format.DateUtils {
@SuppressWarnings("unused")
private static final String TAG = DateUtils.class.getSimpleName();
private static final String TAG = DateUtils.class.getSimpleName();
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
private static boolean isWithin(final long millis, final long span, final TimeUnit unit) {

View File

@@ -232,15 +232,6 @@ public class ViewUtil {
view.requestLayout();
}
public static void setBottomMargin(@NonNull View view, int margin) {
((ViewGroup.MarginLayoutParams) view.getLayoutParams()).bottomMargin = margin;
view.requestLayout();
}
public static void setPaddingTop(@NonNull View view, int padding) {
view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), view.getPaddingBottom());
}
public static void setPaddingBottom(@NonNull View view, int padding) {
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), padding);
}