Add delivery status icons to the conversation list

Closes #4710
pull/1/head
haffenloher 10 years ago committed by Moxie Marlinspike
parent df5c497b5b
commit 7c95adc7e6

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/sms_failed_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_error_red_24dp"
android:visibility="gone"
tools:visibility="visible"
android:contentDescription="@string/conversation_item_sent__send_failed_indicator_description" />
<ImageView
android:id="@+id/pending_approval_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_info_outline_grey600_24dp"
android:visibility="gone"
tools:visibility="visible"
android:layout_gravity="center_vertical"
android:contentDescription="@string/conversation_item_sent__pending_approval_description" />
</merge>

@ -2,9 +2,8 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:id="@+id/pending_indicator" <TextView android:id="@+id/pending_indicator"
android:layout_width="20dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
android:textColor="?conversation_item_sent_text_secondary_color" android:textColor="?conversation_item_sent_text_secondary_color"

@ -4,9 +4,8 @@
<pl.tajchert.sample.DotsTextView <pl.tajchert.sample.DotsTextView
android:id="@+id/pending_indicator" android:id="@+id/pending_indicator"
android:layout_width="20dp" android:layout_width="match_parent"
android:layout_height="20dp" android:layout_height="20dp"
android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
android:textColor="?conversation_item_sent_text_secondary_color" android:textColor="?conversation_item_sent_text_secondary_color"

@ -109,16 +109,6 @@
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="left"> android:gravity="left">
<ImageView android:id="@+id/sent_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
<ImageView android:id="@+id/delivered_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
<ImageView android:id="@+id/secure_indicator" <ImageView android:id="@+id/secure_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -131,9 +121,11 @@
android:tint="?conversation_item_received_text_secondary_color" android:tint="?conversation_item_received_text_secondary_color"
android:tintMode="multiply"/> android:tintMode="multiply"/>
<FrameLayout android:id="@+id/pending_indicator_stub" <org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:visibility="gone"/>
<TextView android:id="@+id/conversation_item_date" <TextView android:id="@+id/conversation_item_date"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -149,31 +141,13 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout android:id="@+id/indicators_parent" <org.thoughtcrime.securesms.components.AlertView
android:id="@+id/indicators_parent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center_vertical"> android:gravity="center_vertical"/>
<ImageView android:id="@+id/sms_failed_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_warning_red"
android:contentDescription="@string/conversation_item_sent__send_failed_indicator_description"
android:visibility="gone" />
<ImageView
android:id="@+id/pending_approval_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_info_outline_grey600_24dp"
android:visibility="gone"
android:layout_gravity="center_vertical"
android:contentDescription="@string/conversation_item_sent__pending_approval_description"
tools:visibility="gone" />
</LinearLayout>
<TextView android:id="@+id/indicator_text" <TextView android:id="@+id/indicator_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"

@ -16,35 +16,15 @@
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:layout_marginRight="0dp"> android:layout_marginRight="0dp">
<LinearLayout android:id="@+id/indicators_parent" <org.thoughtcrime.securesms.components.AlertView
android:id="@+id/indicators_parent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:gravity="left|center_vertical" android:gravity="left|center_vertical"
android:layout_marginLeft="6dp" android:layout_marginLeft="6dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_centerVertical="true"> android:layout_centerVertical="true"/>
<ImageView
android:id="@+id/sms_failed_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_error_red_24dp"
tools:visibility="visible"
android:visibility="gone"
android:contentDescription="@string/conversation_item_sent__send_failed_indicator_description" />
<ImageView
android:id="@+id/pending_approval_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_info_outline_grey600_24dp"
android:visibility="gone"
android:layout_gravity="center_vertical"
android:contentDescription="@string/conversation_item_sent__pending_approval_description"
tools:visibility="visible" />
</LinearLayout>
<LinearLayout android:id="@+id/body_bubble" <LinearLayout android:id="@+id/body_bubble"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -145,33 +125,11 @@
android:paddingBottom="2dp" android:paddingBottom="2dp"
tools:text="30 mins" /> tools:text="30 mins" />
<FrameLayout android:id="@+id/pending_indicator_stub" <org.thoughtcrime.securesms.components.DeliveryStatusView
android:layout_width="wrap_content" android:id="@+id/delivery_status"
android:layout_height="wrap_content"/> android:layout_width="20dp"
<ImageView android:id="@+id/sent_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_done_white_18dp"
android:paddingLeft="2dp"
android:paddingBottom="2dp"
android:visibility="gone"
android:tint="?conversation_item_sent_text_secondary_color"
android:tintMode="multiply"
android:contentDescription="@string/conversation_item_sent__delivered_description" />
<ImageView android:id="@+id/delivered_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end" app:iconColor="?conversation_item_sent_text_secondary_color"/>
android:src="@drawable/ic_done_all_white_18dp"
android:paddingLeft="2dp"
android:paddingBottom="2dp"
android:visibility="gone"
android:tint="?conversation_item_sent_text_secondary_color"
android:tintMode="multiply"
android:contentDescription="@string/conversation_item_sent__delivered_description" />
<ImageView android:id="@+id/secure_indicator" <ImageView android:id="@+id/secure_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"

@ -44,24 +44,25 @@
android:ellipsize="end" android:ellipsize="end"
android:drawablePadding="5dp"/> android:drawablePadding="5dp"/>
<ImageView android:id="@+id/error" <org.thoughtcrime.securesms.components.AlertView
android:layout_height="20dp" android:id="@+id/indicators_parent"
android:layout_width="20dp" android:layout_width="18dp"
android:layout_height="18dp"
android:paddingTop="2dp"
android:layout_marginRight="2dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_below="@id/from" android:layout_below="@id/from"
android:paddingBottom="3dp" app:useSmallIcon="true"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" tools:visibility="visible"/>
android:src="@drawable/ic_action_warning_red"
android:contentDescription="@string/conversation_list_item_view__error_alert" />
<org.thoughtcrime.securesms.components.emoji.EmojiTextView <org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/subject" android:id="@+id/subject"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/from" android:layout_below="@id/from"
android:layout_toRightOf="@id/error" android:layout_toRightOf="@id/indicators_parent"
android:layout_toLeftOf="@+id/archived" android:layout_toLeftOf="@+id/delivery_status"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?attr/conversation_list_item_subject_color" android:textColor="?attr/conversation_list_item_subject_color"
android:fontFamily="sans-serif-light" android:fontFamily="sans-serif-light"
@ -111,6 +112,14 @@
android:textSize="12sp" android:textSize="12sp"
/> />
<org.thoughtcrime.securesms.components.DeliveryStatusView
android:id="@+id/delivery_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/date"
android:layout_toLeftOf="@+id/archived"
android:layout_alignWithParentIfMissing="true"
app:iconColor="?attr/conversation_list_item_subject_color"/>
</RelativeLayout> </RelativeLayout>
</org.thoughtcrime.securesms.ConversationListItem> </org.thoughtcrime.securesms.ConversationListItem>

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<FrameLayout android:id="@+id/pending_indicator_stub"
android:layout_width="wrap_content"
android:paddingRight="2dp"
android:layout_height="wrap_content" />
<ImageView android:id="@+id/sent_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_done_white_18dp"
android:paddingLeft="2dp"
android:paddingBottom="2dp"
android:visibility="gone"
android:contentDescription="@string/conversation_item_sent__delivered_description" />
<ImageView android:id="@+id/delivered_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_done_all_white_18dp"
android:paddingLeft="2dp"
android:paddingBottom="2dp"
android:visibility="gone"
android:contentDescription="@string/conversation_item_sent__delivered_description" />
</merge>

@ -139,6 +139,14 @@
<attr name="backgroundColorHint" format="color" /> <attr name="backgroundColorHint" format="color" />
</declare-styleable> </declare-styleable>
<declare-styleable name="DeliveryStatusView">
<attr name="iconColor" format="color" />
</declare-styleable>
<declare-styleable name="AlertView">
<attr name="useSmallIcon" format="boolean" />
</declare-styleable>
<declare-styleable name="AudioView"> <declare-styleable name="AudioView">
<attr name="tintColor" format="color" /> <attr name="tintColor" format="color" />
</declare-styleable> </declare-styleable>

@ -23,7 +23,6 @@ import android.content.Intent;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.os.Build;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
@ -31,7 +30,6 @@ import android.text.TextUtils;
import android.text.util.Linkify; import android.text.util.Linkify;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
@ -42,6 +40,8 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.AudioView; import org.thoughtcrime.securesms.components.AudioView;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.AlertView;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
@ -97,15 +97,11 @@ public class ConversationItem extends LinearLayout
private TextView groupStatusText; private TextView groupStatusText;
private ImageView secureImage; private ImageView secureImage;
private AvatarImageView contactPhoto; private AvatarImageView contactPhoto;
private ImageView failedIndicator; private DeliveryStatusView deliveryStatusIndicator;
private ImageView deliveredIndicator; private AlertView alertView;
private ImageView sentIndicator;
private View pendingIndicator;
private ImageView pendingApprovalIndicator;
private @NonNull Set<MessageRecord> batchSelected = new HashSet<>(); private @NonNull Set<MessageRecord> batchSelected = new HashSet<>();
private @Nullable Recipients conversationRecipients; private @Nullable Recipients conversationRecipients;
private @NonNull StatusManager statusManager;
private @NonNull ThumbnailView mediaThumbnail; private @NonNull ThumbnailView mediaThumbnail;
private @NonNull AudioView audioView; private @NonNull AudioView audioView;
private @NonNull Button mmsDownloadButton; private @NonNull Button mmsDownloadButton;
@ -136,31 +132,20 @@ public class ConversationItem extends LinearLayout
super.onFinishInflate(); super.onFinishInflate();
initializeAttributes(); initializeAttributes();
ViewGroup pendingIndicatorStub = (ViewGroup) findViewById(R.id.pending_indicator_stub);
if (pendingIndicatorStub != null) {
LayoutInflater inflater = LayoutInflater.from(context);
if (Build.VERSION.SDK_INT >= 11) inflater.inflate(R.layout.conversation_item_pending_v11, pendingIndicatorStub, true);
else inflater.inflate(R.layout.conversation_item_pending, pendingIndicatorStub, true);
}
this.bodyText = (TextView) findViewById(R.id.conversation_item_body); this.bodyText = (TextView) findViewById(R.id.conversation_item_body);
this.dateText = (TextView) findViewById(R.id.conversation_item_date); this.dateText = (TextView) findViewById(R.id.conversation_item_date);
this.indicatorText = (TextView) findViewById(R.id.indicator_text); this.indicatorText = (TextView) findViewById(R.id.indicator_text);
this.groupStatusText = (TextView) findViewById(R.id.group_message_status); this.groupStatusText = (TextView) findViewById(R.id.group_message_status);
this.secureImage = (ImageView) findViewById(R.id.secure_indicator); this.secureImage = (ImageView) findViewById(R.id.secure_indicator);
this.failedIndicator = (ImageView) findViewById(R.id.sms_failed_indicator); this.deliveryStatusIndicator = (DeliveryStatusView) findViewById(R.id.delivery_status);
this.alertView = (AlertView) findViewById(R.id.indicators_parent);
this.mmsDownloadButton = (Button) findViewById(R.id.mms_download_button); this.mmsDownloadButton = (Button) findViewById(R.id.mms_download_button);
this.mmsDownloadingLabel = (TextView) findViewById(R.id.mms_label_downloading); this.mmsDownloadingLabel = (TextView) findViewById(R.id.mms_label_downloading);
this.contactPhoto = (AvatarImageView) findViewById(R.id.contact_photo); this.contactPhoto = (AvatarImageView) findViewById(R.id.contact_photo);
this.deliveredIndicator = (ImageView) findViewById(R.id.delivered_indicator);
this.sentIndicator = (ImageView) findViewById(R.id.sent_indicator);
this.bodyBubble = findViewById(R.id.body_bubble); this.bodyBubble = findViewById(R.id.body_bubble);
this.pendingApprovalIndicator = (ImageView) findViewById(R.id.pending_approval_indicator);
this.pendingIndicator = findViewById(R.id.pending_indicator);
this.mediaThumbnail = (ThumbnailView) findViewById(R.id.image_view); this.mediaThumbnail = (ThumbnailView) findViewById(R.id.image_view);
this.audioView = (AudioView) findViewById(R.id.audio_view); this.audioView = (AudioView) findViewById(R.id.audio_view);
this.statusManager = new StatusManager(pendingIndicator, sentIndicator, deliveredIndicator, failedIndicator, pendingApprovalIndicator);
setOnClickListener(new ClickListener(null)); setOnClickListener(new ClickListener(null));
PassthroughClickListener passthroughClickListener = new PassthroughClickListener(); PassthroughClickListener passthroughClickListener = new PassthroughClickListener();
@ -334,16 +319,25 @@ public class ConversationItem extends LinearLayout
dateText.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, timestamp)); dateText.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, timestamp));
if (messageRecord.isFailed()) setFailedStatusIcons(); if (messageRecord.isFailed()) {
else if (messageRecord.isPendingInsecureSmsFallback()) setFallbackStatusIcons(); setFailedStatusIcons();
else if (messageRecord.isPending()) statusManager.displayPending(); } else if (messageRecord.isPendingInsecureSmsFallback()) {
else if (messageRecord.isDelivered()) statusManager.displayDelivered(); setFallbackStatusIcons();
else statusManager.displaySent(); } else {
alertView.setNone();
if (!messageRecord.isOutgoing()) deliveryStatusIndicator.setNone();
else if (messageRecord.isPending()) deliveryStatusIndicator.setPending();
else if (messageRecord.isDelivered()) deliveryStatusIndicator.setDelivered();
else deliveryStatusIndicator.setSent();
}
} }
private void setFailedStatusIcons() { private void setFailedStatusIcons() {
statusManager.displayFailed(); alertView.setFailed();
deliveryStatusIndicator.setNone();
dateText.setText(R.string.ConversationItem_error_not_delivered); dateText.setText(R.string.ConversationItem_error_not_delivered);
if (messageRecord.isOutgoing()) { if (messageRecord.isOutgoing()) {
indicatorText.setText(R.string.ConversationItem_click_for_details); indicatorText.setText(R.string.ConversationItem_click_for_details);
indicatorText.setVisibility(View.VISIBLE); indicatorText.setVisibility(View.VISIBLE);
@ -351,7 +345,8 @@ public class ConversationItem extends LinearLayout
} }
private void setFallbackStatusIcons() { private void setFallbackStatusIcons() {
statusManager.displayPendingApproval(); alertView.setPendingApproval();
deliveryStatusIndicator.setNone();
indicatorText.setVisibility(View.VISIBLE); indicatorText.setVisibility(View.VISIBLE);
indicatorText.setText(R.string.ConversationItem_click_to_approve_unencrypted); indicatorText.setText(R.string.ConversationItem_click_to_approve_unencrypted);
} }
@ -612,72 +607,4 @@ public class ConversationItem extends LinearLayout
builder.show(); builder.show();
} }
private static class StatusManager {
private final View pendingIndicator;
private final View sentIndicator;
private final View deliveredIndicator;
private final View failedIndicator;
private final View approvalIndicator;
public StatusManager(View pendingIndicator, View sentIndicator,
View deliveredIndicator, View failedIndicator,
View approvalIndicator)
{
this.pendingIndicator = pendingIndicator;
this.sentIndicator = sentIndicator;
this.deliveredIndicator = deliveredIndicator;
this.failedIndicator = failedIndicator;
this.approvalIndicator = approvalIndicator;
}
public void displayFailed() {
pendingIndicator.setVisibility(View.GONE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
approvalIndicator.setVisibility(View.GONE);
failedIndicator.setVisibility(View.VISIBLE);
}
public void displayPendingApproval() {
pendingIndicator.setVisibility(View.GONE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
failedIndicator.setVisibility(View.GONE);
approvalIndicator.setVisibility(View.VISIBLE);
}
public void displayPending() {
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
failedIndicator.setVisibility(View.GONE);
approvalIndicator.setVisibility(View.GONE);
pendingIndicator.setVisibility(View.VISIBLE);
}
public void displaySent() {
pendingIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
failedIndicator.setVisibility(View.GONE);
approvalIndicator.setVisibility(View.GONE);
sentIndicator.setVisibility(View.VISIBLE);
}
public void displayDelivered() {
pendingIndicator.setVisibility(View.GONE);
failedIndicator.setVisibility(View.GONE);
approvalIndicator.setVisibility(View.GONE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.VISIBLE);
}
}
} }

@ -27,12 +27,13 @@ import android.os.Handler;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.AlertView;
import org.thoughtcrime.securesms.components.FromTextView; import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
@ -70,6 +71,9 @@ public class ConversationListItem extends RelativeLayout
private FromTextView fromView; private FromTextView fromView;
private TextView dateView; private TextView dateView;
private TextView archivedView; private TextView archivedView;
private DeliveryStatusView deliveryStatusIndicator;
private AlertView alertView;
private boolean read; private boolean read;
private AvatarImageView contactPhotoImage; private AvatarImageView contactPhotoImage;
private ThumbnailView thumbnailView; private ThumbnailView thumbnailView;
@ -96,6 +100,8 @@ public class ConversationListItem extends RelativeLayout
this.subjectView = (TextView) findViewById(R.id.subject); this.subjectView = (TextView) findViewById(R.id.subject);
this.fromView = (FromTextView) findViewById(R.id.from); this.fromView = (FromTextView) findViewById(R.id.from);
this.dateView = (TextView) findViewById(R.id.date); this.dateView = (TextView) findViewById(R.id.date);
this.deliveryStatusIndicator = (DeliveryStatusView) findViewById(R.id.delivery_status);
this.alertView = (AlertView) findViewById(R.id.indicators_parent);
this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image); this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image);
this.thumbnailView = (ThumbnailView) findViewById(R.id.thumbnail); this.thumbnailView = (ThumbnailView) findViewById(R.id.thumbnail);
this.archivedView = ViewUtil.findById(this, R.id.archived); this.archivedView = ViewUtil.findById(this, R.id.archived);
@ -129,6 +135,7 @@ public class ConversationListItem extends RelativeLayout
this.archivedView.setVisibility(View.GONE); this.archivedView.setVisibility(View.GONE);
} }
setStatusIcons(thread);
setThumbnailSnippet(masterSecret, thread); setThumbnailSnippet(masterSecret, thread);
setBatchState(batchMode); setBatchState(batchMode);
setBackground(thread); setBackground(thread);
@ -165,16 +172,35 @@ public class ConversationListItem extends RelativeLayout
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams(); LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail); subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail);
this.subjectView.setLayoutParams(subjectParams); this.subjectView.setLayoutParams(subjectParams);
this.post(new ThumbnailPositioner(thumbnailView, archivedView, dateView)); this.post(new ThumbnailPositioner(thumbnailView, archivedView, deliveryStatusIndicator, dateView));
} else { } else {
this.thumbnailView.setVisibility(View.GONE); this.thumbnailView.setVisibility(View.GONE);
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams(); LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.archived); subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.delivery_status);
this.subjectView.setLayoutParams(subjectParams); this.subjectView.setLayoutParams(subjectParams);
} }
} }
private void setStatusIcons(ThreadRecord thread) {
if (!thread.isOutgoing()) {
deliveryStatusIndicator.setNone();
alertView.setNone();
} else if (thread.isFailed()) {
deliveryStatusIndicator.setNone();
alertView.setFailed();
} else if (thread.isPendingInsecureSmsFallback()) {
deliveryStatusIndicator.setNone();
alertView.setPendingApproval();
} else {
alertView.setNone();
if (thread.isPending()) deliveryStatusIndicator.setPending();
else if (thread.isDelivered()) deliveryStatusIndicator.setDelivered();
else deliveryStatusIndicator.setSent();
}
}
private void setBackground(ThreadRecord thread) { private void setBackground(ThreadRecord thread) {
if (thread.isRead()) setBackgroundResource(readBackground); if (thread.isRead()) setBackgroundResource(readBackground);
else setBackgroundResource(unreadBackround); else setBackgroundResource(unreadBackround);
@ -204,11 +230,13 @@ public class ConversationListItem extends RelativeLayout
private final View thumbnailView; private final View thumbnailView;
private final View archivedView; private final View archivedView;
private final View deliveryStatusView;
private final View dateView; private final View dateView;
public ThumbnailPositioner(View thumbnailView, View archivedView, View dateView) { public ThumbnailPositioner(View thumbnailView, View archivedView, View deliveryStatusView, View dateView) {
this.thumbnailView = thumbnailView; this.thumbnailView = thumbnailView;
this.archivedView = archivedView; this.archivedView = archivedView;
this.deliveryStatusView = deliveryStatusView;
this.dateView = dateView; this.dateView = dateView;
} }
@ -216,8 +244,10 @@ public class ConversationListItem extends RelativeLayout
public void run() { public void run() {
LayoutParams thumbnailParams = (RelativeLayout.LayoutParams)thumbnailView.getLayoutParams(); LayoutParams thumbnailParams = (RelativeLayout.LayoutParams)thumbnailView.getLayoutParams();
if (archivedView.getVisibility() == View.VISIBLE && archivedView.getWidth() > dateView.getWidth()) { if (archivedView.getVisibility() == View.VISIBLE &&
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.archived); (archivedView.getWidth() + deliveryStatusView.getWidth()) > dateView.getWidth())
{
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.delivery_status);
} else { } else {
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.date); thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.date);
} }

@ -0,0 +1,69 @@
package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build.VERSION_CODES;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import org.thoughtcrime.securesms.R;
public class AlertView extends LinearLayout {
private static final String TAG = AlertView.class.getSimpleName();
private ImageView approvalIndicator;
private ImageView failedIndicator;
public AlertView(Context context) {
this(context, null);
}
public AlertView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}
@TargetApi(VERSION_CODES.HONEYCOMB)
public AlertView(final Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize(attrs);
}
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);
if (attrs != null) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.AlertView, 0, 0);
boolean useSmallIcon = typedArray.getBoolean(R.styleable.AlertView_useSmallIcon, false);
typedArray.recycle();
if (useSmallIcon) {
failedIndicator.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_error_red_18dp));
}
}
}
public void setNone() {
this.setVisibility(View.GONE);
}
public void setPendingApproval() {
this.setVisibility(View.VISIBLE);
approvalIndicator.setVisibility(View.VISIBLE);
failedIndicator.setVisibility(View.GONE);
}
public void setFailed() {
this.setVisibility(View.VISIBLE);
approvalIndicator.setVisibility(View.GONE);
failedIndicator.setVisibility(View.VISIBLE);
}
}

@ -0,0 +1,89 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
import pl.tajchert.sample.DotsTextView;
public class DeliveryStatusView extends FrameLayout {
private static final String TAG = DeliveryStatusView.class.getSimpleName();
private final ViewGroup pendingIndicatorStub;
private final ImageView sentIndicator;
private final ImageView deliveredIndicator;
public DeliveryStatusView(Context context) {
this(context, null);
}
public DeliveryStatusView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DeliveryStatusView(final Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
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);
int iconColor = Color.GRAY;
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DeliveryStatusView, 0, 0);
iconColor = typedArray.getColor(R.styleable.DeliveryStatusView_iconColor, iconColor);
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() {
this.setVisibility(View.GONE);
}
public void setPending() {
this.setVisibility(View.VISIBLE);
pendingIndicatorStub.setVisibility(View.VISIBLE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
}
public void setSent() {
this.setVisibility(View.VISIBLE);
pendingIndicatorStub.setVisibility(View.GONE);
sentIndicator.setVisibility(View.VISIBLE);
deliveredIndicator.setVisibility(View.GONE);
}
public void setDelivered() {
this.setVisibility(View.VISIBLE);
pendingIndicatorStub.setVisibility(View.GONE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.VISIBLE);
}
}

@ -69,7 +69,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22; private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22;
private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23; private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23;
private static final int INTRODUCED_ARCHIVE_VERSION = 24; private static final int INTRODUCED_ARCHIVE_VERSION = 24;
private static final int DATABASE_VERSION = 24; private static final int INTRODUCED_CONVERSATION_LIST_STATUS_VERSION = 25;
private static final int DATABASE_VERSION = 25;
private static final String DATABASE_NAME = "messages.db"; private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object(); private static final Object lock = new Object();
@ -784,6 +785,11 @@ public class DatabaseFactory {
db.execSQL("CREATE INDEX IF NOT EXISTS archived_index ON thread (archived)"); db.execSQL("CREATE INDEX IF NOT EXISTS archived_index ON thread (archived)");
} }
if (oldVersion < INTRODUCED_CONVERSATION_LIST_STATUS_VERSION) {
db.execSQL("ALTER TABLE thread ADD COLUMN status INTEGER DEFAULT -1");
db.execSQL("ALTER TABLE thread ADD COLUMN delivery_receipt_count INTEGER DEFAULT 0");
}
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
} }

@ -214,6 +214,7 @@ public class MmsDatabase extends MessagingDatabase {
RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?", RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)}); new String[] {String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
} }
} catch (InvalidNumberException e) { } catch (InvalidNumberException e) {
@ -337,6 +338,8 @@ public class MmsDatabase extends MessagingDatabase {
db.execSQL("UPDATE " + TABLE_NAME + db.execSQL("UPDATE " + TABLE_NAME +
" SET " + MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" + " SET " + MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" +
" WHERE " + ID + " = ?", new String[] {id + ""}); " WHERE " + ID + " = ?", new String[] {id + ""});
DatabaseFactory.getThreadDatabase(context).update(getThreadIdForMessage(id), false);
} }
public void markAsOutbox(long messageId) { public void markAsOutbox(long messageId) {

@ -120,7 +120,6 @@ public class SmsDatabase extends MessagingDatabase {
DatabaseFactory.getThreadDatabase(context).update(threadId, false); DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId); notifyConversationListeners(threadId);
notifyConversationListListeners();
} }
public long getThreadIdForMessage(long id) { public long getThreadIdForMessage(long id) {
@ -265,12 +264,15 @@ public class SmsDatabase extends MessagingDatabase {
String ourAddress = canonicalizeNumber(context, cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); String ourAddress = canonicalizeNumber(context, cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (ourAddress.equals(theirAddress)) { if (ourAddress.equals(theirAddress)) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
database.execSQL("UPDATE " + TABLE_NAME + database.execSQL("UPDATE " + TABLE_NAME +
" SET " + RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + " SET " + RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " +
ID + " = ?", ID + " = ?",
new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))}); new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))});
notifyConversationListeners(cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID))); DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
} }
} catch (InvalidNumberException e) { } catch (InvalidNumberException e) {
Log.w("SmsDatabase", e); Log.w("SmsDatabase", e);

@ -64,12 +64,17 @@ public class ThreadDatabase extends Database {
public static final String SNIPPET_TYPE = "snippet_type"; public static final String SNIPPET_TYPE = "snippet_type";
public static final String SNIPPET_URI = "snippet_uri"; public static final String SNIPPET_URI = "snippet_uri";
public static final String ARCHIVED = "archived"; public static final String ARCHIVED = "archived";
public static final String STATUS = "status";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + public static final String RECEIPT_COUNT = "delivery_receipt_count";
DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " +
RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
READ + " INTEGER DEFAULT 1, " + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + ARCHIVED + " INTEGER DEFAULT 0);"; MESSAGE_COUNT + " INTEGER DEFAULT 0, " + RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " +
SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " +
TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " +
ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " +
RECEIPT_COUNT + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");", "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");",
@ -126,14 +131,17 @@ public class ThreadDatabase extends Database {
return db.insert(TABLE_NAME, null, contentValues); return db.insert(TABLE_NAME, null, contentValues);
} }
private void updateThread(long threadId, long count, String body, @Nullable Uri attachment, long date, long type, boolean unarchive) private void updateThread(long threadId, long count, String body, @Nullable Uri attachment,
long date, int status, int receiptCount, long type, boolean unarchive)
{ {
ContentValues contentValues = new ContentValues(5); ContentValues contentValues = new ContentValues(7);
contentValues.put(DATE, date - date % 1000); contentValues.put(DATE, date - date % 1000);
contentValues.put(MESSAGE_COUNT, count); contentValues.put(MESSAGE_COUNT, count);
contentValues.put(SNIPPET, body); contentValues.put(SNIPPET, body);
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString()); contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
contentValues.put(SNIPPET_TYPE, type); contentValues.put(SNIPPET_TYPE, type);
contentValues.put(STATUS, status);
contentValues.put(RECEIPT_COUNT, receiptCount);
if (unarchive) { if (unarchive) {
contentValues.put(ARCHIVED, 0); contentValues.put(ARCHIVED, 0);
@ -145,7 +153,7 @@ public class ThreadDatabase extends Database {
} }
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) { public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) {
ContentValues contentValues = new ContentValues(3); ContentValues contentValues = new ContentValues(4);
contentValues.put(DATE, date - date % 1000); contentValues.put(DATE, date - date % 1000);
contentValues.put(SNIPPET, snippet); contentValues.put(SNIPPET, snippet);
@ -479,10 +487,9 @@ public class ThreadDatabase extends Database {
if (record.isPush()) timestamp = record.getDateSent(); if (record.isPush()) timestamp = record.getDateSent();
else timestamp = record.getDateReceived(); else timestamp = record.getDateReceived();
updateThread(threadId, count, record.getBody().getBody(), updateThread(threadId, count, record.getBody().getBody(), getAttachmentUriFor(record),
getAttachmentUriFor(record), timestamp, timestamp, record.getDeliveryStatus(), record.getReceiptCount(),
record.getType(), unarchive); record.getType(), unarchive);
notifyConversationListListeners(); notifyConversationListListeners();
return false; return false;
} else { } else {
@ -549,10 +556,12 @@ public class ThreadDatabase extends Database {
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE)); long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE)); int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
boolean archived = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.ARCHIVED)) != 0; boolean archived = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.ARCHIVED)) != 0;
int status = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.STATUS));
int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.RECEIPT_COUNT));
Uri snippetUri = getSnippetUri(cursor); Uri snippetUri = getSnippetUri(cursor);
return new ThreadRecord(context, body, snippetUri, recipients, date, count, return new ThreadRecord(context, body, snippetUri, recipients, date, count, read == 1,
read == 1, threadId, type, distributionType, archived); threadId, receiptCount, status, type, distributionType, archived);
} }
private DisplayRecord.Body getPlaintextBody(Cursor cursor) { private DisplayRecord.Body getPlaintextBody(Cursor cursor) {

@ -43,10 +43,11 @@ public class ConversationListLoader extends AbstractCursorLoader {
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT, ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
ThreadDatabase.RECIPIENT_IDS, ThreadDatabase.SNIPPET, ThreadDatabase.READ, ThreadDatabase.RECIPIENT_IDS, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI, ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI,
ThreadDatabase.ARCHIVED}, 1); ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.RECEIPT_COUNT}, 1);
switchToArchiveCursor.addRow(new Object[] {-1L, System.currentTimeMillis(), archivedCount, switchToArchiveCursor.addRow(new Object[] {-1L, System.currentTimeMillis(), archivedCount,
"-1", null, 1, ThreadDatabase.DistributionTypes.ARCHIVE, 0, null, 0}); "-1", null, 1, ThreadDatabase.DistributionTypes.ARCHIVE,
0, null, 0, -1, 0});
cursorList.add(switchToArchiveCursor); cursorList.add(switchToArchiveCursor);
} }

@ -19,6 +19,7 @@ package org.thoughtcrime.securesms.database.model;
import android.content.Context; import android.content.Context;
import android.text.SpannableString; import android.text.SpannableString;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
@ -40,9 +41,11 @@ public abstract class DisplayRecord {
private final long dateReceived; private final long dateReceived;
private final long threadId; private final long threadId;
private final Body body; private final Body body;
private final int deliveryStatus;
private final int receiptCount;
public DisplayRecord(Context context, Body body, Recipients recipients, long dateSent, public DisplayRecord(Context context, Body body, Recipients recipients, long dateSent,
long dateReceived, long threadId, long type) long dateReceived, long threadId, int deliveryStatus, int receiptCount, long type)
{ {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.threadId = threadId; this.threadId = threadId;
@ -51,12 +54,29 @@ public abstract class DisplayRecord {
this.dateReceived = dateReceived; this.dateReceived = dateReceived;
this.type = type; this.type = type;
this.body = body; this.body = body;
this.receiptCount = receiptCount;
this.deliveryStatus = deliveryStatus;
} }
public Body getBody() { public Body getBody() {
return body; return body;
} }
public boolean isFailed() {
return
MmsSmsColumns.Types.isFailedMessageType(type) ||
MmsSmsColumns.Types.isPendingSecureSmsFallbackType(type) ||
deliveryStatus >= SmsDatabase.Status.STATUS_FAILED;
}
public boolean isPending() {
return MmsSmsColumns.Types.isPendingMessageType(type);
}
public boolean isOutgoing() {
return MmsSmsColumns.Types.isOutgoingMessageType(type);
}
public abstract SpannableString getDisplayBody(); public abstract SpannableString getDisplayBody();
public Recipients getRecipients() { public Recipients getRecipients() {
@ -115,6 +135,23 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isMissedCall(type); return SmsDatabase.Types.isMissedCall(type);
} }
public int getDeliveryStatus() {
return deliveryStatus;
}
public int getReceiptCount() {
return receiptCount;
}
public boolean isDelivered() {
return (deliveryStatus >= SmsDatabase.Status.STATUS_COMPLETE &&
deliveryStatus < SmsDatabase.Status.STATUS_PENDING) || receiptCount > 0;
}
public boolean isPendingInsecureSmsFallback() {
return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type);
}
public static class Body { public static class Body {
private final String body; private final String body;
private final boolean plaintext; private final boolean plaintext;

@ -21,6 +21,7 @@ import android.support.annotation.NonNull;
import android.text.SpannableString; import android.text.SpannableString;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
@ -47,16 +48,15 @@ public class MediaMmsMessageRecord extends MessageRecord {
public MediaMmsMessageRecord(Context context, long id, Recipients recipients, public MediaMmsMessageRecord(Context context, long id, Recipients recipients,
Recipient individualRecipient, int recipientDeviceId, Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, int deliveredCount, long dateSent, long dateReceived, int receiptCount,
long threadId, Body body, long threadId, Body body,
@NonNull SlideDeck slideDeck, @NonNull SlideDeck slideDeck,
int partCount, long mailbox, int partCount, long mailbox,
List<IdentityKeyMismatch> mismatches, List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> failures) List<NetworkFailure> failures)
{ {
super(context, id, body, recipients, individualRecipient, recipientDeviceId, super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent,
dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, deliveredCount, mailbox, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures);
mismatches, failures);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.partCount = partCount; this.partCount = partCount;

@ -43,18 +43,11 @@ import java.util.List;
*/ */
public abstract class MessageRecord extends DisplayRecord { public abstract class MessageRecord extends DisplayRecord {
public static final int DELIVERY_STATUS_NONE = 0;
public static final int DELIVERY_STATUS_RECEIVED = 1;
public static final int DELIVERY_STATUS_PENDING = 2;
public static final int DELIVERY_STATUS_FAILED = 3;
private static final int MAX_DISPLAY_LENGTH = 2000; private static final int MAX_DISPLAY_LENGTH = 2000;
private final Recipient individualRecipient; private final Recipient individualRecipient;
private final int recipientDeviceId; private final int recipientDeviceId;
private final long id; private final long id;
private final int deliveryStatus;
private final int receiptCount;
private final List<IdentityKeyMismatch> mismatches; private final List<IdentityKeyMismatch> mismatches;
private final List<NetworkFailure> networkFailures; private final List<NetworkFailure> networkFailures;
@ -65,12 +58,11 @@ public abstract class MessageRecord extends DisplayRecord {
List<IdentityKeyMismatch> mismatches, List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> networkFailures) List<NetworkFailure> networkFailures)
{ {
super(context, body, recipients, dateSent, dateReceived, threadId, type); super(context, body, recipients, dateSent, dateReceived, threadId, deliveryStatus, receiptCount,
type);
this.id = id; this.id = id;
this.individualRecipient = individualRecipient; this.individualRecipient = individualRecipient;
this.recipientDeviceId = recipientDeviceId; this.recipientDeviceId = recipientDeviceId;
this.deliveryStatus = deliveryStatus;
this.receiptCount = receiptCount;
this.mismatches = mismatches; this.mismatches = mismatches;
this.networkFailures = networkFailures; this.networkFailures = networkFailures;
} }
@ -78,21 +70,6 @@ public abstract class MessageRecord extends DisplayRecord {
public abstract boolean isMms(); public abstract boolean isMms();
public abstract boolean isMmsNotification(); public abstract boolean isMmsNotification();
public boolean isFailed() {
return
MmsSmsColumns.Types.isFailedMessageType(type) ||
MmsSmsColumns.Types.isPendingSecureSmsFallbackType(type) ||
getDeliveryStatus() == DELIVERY_STATUS_FAILED;
}
public boolean isOutgoing() {
return MmsSmsColumns.Types.isOutgoingMessageType(type);
}
public boolean isPending() {
return MmsSmsColumns.Types.isPendingMessageType(type);
}
public boolean isSecure() { public boolean isSecure() {
return MmsSmsColumns.Types.isSecureType(type); return MmsSmsColumns.Types.isSecureType(type);
} }
@ -134,14 +111,6 @@ public abstract class MessageRecord extends DisplayRecord {
return id; return id;
} }
public int getDeliveryStatus() {
return deliveryStatus;
}
public boolean isDelivered() {
return getDeliveryStatus() == DELIVERY_STATUS_RECEIVED || receiptCount > 0;
}
public boolean isPush() { public boolean isPush() {
return SmsDatabase.Types.isPushType(type) && !SmsDatabase.Types.isForcedSms(type); return SmsDatabase.Types.isPushType(type) && !SmsDatabase.Types.isForcedSms(type);
} }
@ -158,10 +127,6 @@ public abstract class MessageRecord extends DisplayRecord {
return SmsDatabase.Types.isProcessedKeyExchange(type); return SmsDatabase.Types.isProcessedKeyExchange(type);
} }
public boolean isPendingInsecureSmsFallback() {
return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type);
}
public boolean isIdentityMismatchFailure() { public boolean isIdentityMismatchFailure() {
return mismatches != null && !mismatches.isEmpty(); return mismatches != null && !mismatches.isEmpty();
} }

@ -20,6 +20,7 @@ import android.content.Context;
import android.text.SpannableString; import android.text.SpannableString;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
@ -51,7 +52,7 @@ public class NotificationMmsMessageRecord extends MessageRecord {
long expiry, int status, byte[] transactionId, long mailbox) long expiry, int status, byte[] transactionId, long mailbox)
{ {
super(context, id, new Body("", true), recipients, individualRecipient, recipientDeviceId, super(context, id, new Body("", true), recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, receiptCount, mailbox, dateSent, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox,
new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>()); new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>());
this.contentLocation = contentLocation; this.contentLocation = contentLocation;

@ -50,7 +50,7 @@ public class SmsMessageRecord extends MessageRecord {
int status, List<IdentityKeyMismatch> mismatches) int status, List<IdentityKeyMismatch> mismatches)
{ {
super(context, id, body, recipients, individualRecipient, recipientDeviceId, super(context, id, body, recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, getGenericDeliveryStatus(status), receiptCount, type, dateSent, dateReceived, threadId, status, receiptCount, type,
mismatches, new LinkedList<NetworkFailure>()); mismatches, new LinkedList<NetworkFailure>());
} }
@ -104,16 +104,4 @@ public class SmsMessageRecord extends MessageRecord {
public boolean isMmsNotification() { public boolean isMmsNotification() {
return false; return false;
} }
private static int getGenericDeliveryStatus(int status) {
if (status == SmsDatabase.Status.STATUS_NONE) {
return MessageRecord.DELIVERY_STATUS_NONE;
} else if (status >= SmsDatabase.Status.STATUS_FAILED) {
return MessageRecord.DELIVERY_STATUS_FAILED;
} else if (status >= SmsDatabase.Status.STATUS_PENDING) {
return MessageRecord.DELIVERY_STATUS_PENDING;
} else {
return MessageRecord.DELIVERY_STATUS_RECEIVED;
}
}
} }

@ -28,6 +28,7 @@ import android.text.style.StyleSpan;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
@ -48,9 +49,10 @@ public class ThreadRecord extends DisplayRecord {
public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri, public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri,
@NonNull Recipients recipients, long date, long count, boolean read, @NonNull Recipients recipients, long date, long count, boolean read,
long threadId, long snippetType, int distributionType, boolean archived) long threadId, int receiptCount, int status, long snippetType,
int distributionType, boolean archived)
{ {
super(context, body, recipients, date, date, threadId, snippetType); super(context, body, recipients, date, date, threadId, status, receiptCount, snippetType);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.snippetUri = snippetUri; this.snippetUri = snippetUri;
this.count = count; this.count = count;

Loading…
Cancel
Save