diff --git a/res/drawable-xxhdpi/ic_error_red_18dp.png b/res/drawable-xxhdpi/ic_error_red_18dp.png
new file mode 100644
index 000000000..ed4faed50
Binary files /dev/null and b/res/drawable-xxhdpi/ic_error_red_18dp.png differ
diff --git a/res/layout/alert_view.xml b/res/layout/alert_view.xml
new file mode 100644
index 000000000..80c2daa6e
--- /dev/null
+++ b/res/layout/alert_view.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/conversation_item_pending.xml b/res/layout/conversation_item_pending.xml
index 116287978..fd402078d 100644
--- a/res/layout/conversation_item_pending.xml
+++ b/res/layout/conversation_item_pending.xml
@@ -2,9 +2,8 @@
-
-
-
-
-
+
-
-
-
-
-
-
-
+ android:layout_alignParentRight="true"
+ android:orientation="vertical"
+ android:gravity="center_vertical"/>
-
-
-
-
-
-
-
+ android:orientation="vertical"
+ android:gravity="left|center_vertical"
+ android:layout_marginLeft="6dp"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"/>
-
-
-
-
-
+
-
+
+
diff --git a/res/layout/delivery_status_view.xml b/res/layout/delivery_status_view.xml
new file mode 100644
index 000000000..8fe3b40cd
--- /dev/null
+++ b/res/layout/delivery_status_view.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index a58d10e9d..6f47480aa 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -139,6 +139,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java
index e08cfcdb3..0866bd7db 100644
--- a/src/org/thoughtcrime/securesms/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationItem.java
@@ -23,7 +23,6 @@ import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
-import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
@@ -31,7 +30,6 @@ import android.text.TextUtils;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -42,6 +40,8 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.AudioView;
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.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
@@ -90,22 +90,18 @@ public class ConversationItem extends LinearLayout
private boolean groupThread;
private Recipient recipient;
- private View bodyBubble;
- private TextView bodyText;
- private TextView dateText;
- private TextView indicatorText;
- private TextView groupStatusText;
- private ImageView secureImage;
- private AvatarImageView contactPhoto;
- private ImageView failedIndicator;
- private ImageView deliveredIndicator;
- private ImageView sentIndicator;
- private View pendingIndicator;
- private ImageView pendingApprovalIndicator;
+ private View bodyBubble;
+ private TextView bodyText;
+ private TextView dateText;
+ private TextView indicatorText;
+ private TextView groupStatusText;
+ private ImageView secureImage;
+ private AvatarImageView contactPhoto;
+ private DeliveryStatusView deliveryStatusIndicator;
+ private AlertView alertView;
private @NonNull Set batchSelected = new HashSet<>();
private @Nullable Recipients conversationRecipients;
- private @NonNull StatusManager statusManager;
private @NonNull ThumbnailView mediaThumbnail;
private @NonNull AudioView audioView;
private @NonNull Button mmsDownloadButton;
@@ -136,31 +132,20 @@ public class ConversationItem extends LinearLayout
super.onFinishInflate();
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.dateText = (TextView) findViewById(R.id.conversation_item_date);
- this.indicatorText = (TextView) findViewById(R.id.indicator_text);
- this.groupStatusText = (TextView) findViewById(R.id.group_message_status);
- this.secureImage = (ImageView) findViewById(R.id.secure_indicator);
- this.failedIndicator = (ImageView) findViewById(R.id.sms_failed_indicator);
- this.mmsDownloadButton = (Button) findViewById(R.id.mms_download_button);
- this.mmsDownloadingLabel = (TextView) findViewById(R.id.mms_label_downloading);
- 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.pendingApprovalIndicator = (ImageView) findViewById(R.id.pending_approval_indicator);
- this.pendingIndicator = findViewById(R.id.pending_indicator);
- this.mediaThumbnail = (ThumbnailView) findViewById(R.id.image_view);
- this.audioView = (AudioView) findViewById(R.id.audio_view);
- this.statusManager = new StatusManager(pendingIndicator, sentIndicator, deliveredIndicator, failedIndicator, pendingApprovalIndicator);
+ this.bodyText = (TextView) findViewById(R.id.conversation_item_body);
+ this.dateText = (TextView) findViewById(R.id.conversation_item_date);
+ this.indicatorText = (TextView) findViewById(R.id.indicator_text);
+ this.groupStatusText = (TextView) findViewById(R.id.group_message_status);
+ this.secureImage = (ImageView) findViewById(R.id.secure_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.mmsDownloadingLabel = (TextView) findViewById(R.id.mms_label_downloading);
+ this.contactPhoto = (AvatarImageView) findViewById(R.id.contact_photo);
+ this.bodyBubble = findViewById(R.id.body_bubble);
+ this.mediaThumbnail = (ThumbnailView) findViewById(R.id.image_view);
+ this.audioView = (AudioView) findViewById(R.id.audio_view);
setOnClickListener(new ClickListener(null));
PassthroughClickListener passthroughClickListener = new PassthroughClickListener();
@@ -334,16 +319,25 @@ public class ConversationItem extends LinearLayout
dateText.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, timestamp));
- if (messageRecord.isFailed()) setFailedStatusIcons();
- else if (messageRecord.isPendingInsecureSmsFallback()) setFallbackStatusIcons();
- else if (messageRecord.isPending()) statusManager.displayPending();
- else if (messageRecord.isDelivered()) statusManager.displayDelivered();
- else statusManager.displaySent();
+ if (messageRecord.isFailed()) {
+ setFailedStatusIcons();
+ } else if (messageRecord.isPendingInsecureSmsFallback()) {
+ setFallbackStatusIcons();
+ } 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() {
- statusManager.displayFailed();
+ alertView.setFailed();
+ deliveryStatusIndicator.setNone();
dateText.setText(R.string.ConversationItem_error_not_delivered);
+
if (messageRecord.isOutgoing()) {
indicatorText.setText(R.string.ConversationItem_click_for_details);
indicatorText.setVisibility(View.VISIBLE);
@@ -351,7 +345,8 @@ public class ConversationItem extends LinearLayout
}
private void setFallbackStatusIcons() {
- statusManager.displayPendingApproval();
+ alertView.setPendingApproval();
+ deliveryStatusIndicator.setNone();
indicatorText.setVisibility(View.VISIBLE);
indicatorText.setText(R.string.ConversationItem_click_to_approve_unencrypted);
}
@@ -612,72 +607,4 @@ public class ConversationItem extends LinearLayout
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);
- }
-
- }
-
}
diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java
index ce80d4ae2..edd48f001 100644
--- a/src/org/thoughtcrime/securesms/ConversationListItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationListItem.java
@@ -27,12 +27,13 @@ import android.os.Handler;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
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.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
@@ -63,13 +64,16 @@ public class ConversationListItem extends RelativeLayout
private final static Typeface BOLD_TYPEFACE = Typeface.create("sans-serif", Typeface.BOLD);
private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif-light", Typeface.NORMAL);
- private Set selectedThreads;
- private Recipients recipients;
- private long threadId;
- private TextView subjectView;
- private FromTextView fromView;
- private TextView dateView;
- private TextView archivedView;
+ private Set selectedThreads;
+ private Recipients recipients;
+ private long threadId;
+ private TextView subjectView;
+ private FromTextView fromView;
+ private TextView dateView;
+ private TextView archivedView;
+ private DeliveryStatusView deliveryStatusIndicator;
+ private AlertView alertView;
+
private boolean read;
private AvatarImageView contactPhotoImage;
private ThumbnailView thumbnailView;
@@ -93,12 +97,14 @@ public class ConversationListItem extends RelativeLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- this.subjectView = (TextView) findViewById(R.id.subject);
- this.fromView = (FromTextView) findViewById(R.id.from);
- this.dateView = (TextView) findViewById(R.id.date);
- this.contactPhotoImage = (AvatarImageView) findViewById(R.id.contact_photo_image);
- this.thumbnailView = (ThumbnailView) findViewById(R.id.thumbnail);
- this.archivedView = ViewUtil.findById(this, R.id.archived);
+ this.subjectView = (TextView) findViewById(R.id.subject);
+ this.fromView = (FromTextView) findViewById(R.id.from);
+ 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.thumbnailView = (ThumbnailView) findViewById(R.id.thumbnail);
+ this.archivedView = ViewUtil.findById(this, R.id.archived);
thumbnailView.setClickable(false);
}
@@ -129,6 +135,7 @@ public class ConversationListItem extends RelativeLayout
this.archivedView.setVisibility(View.GONE);
}
+ setStatusIcons(thread);
setThumbnailSnippet(masterSecret, thread);
setBatchState(batchMode);
setBackground(thread);
@@ -165,16 +172,35 @@ public class ConversationListItem extends RelativeLayout
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail);
this.subjectView.setLayoutParams(subjectParams);
- this.post(new ThumbnailPositioner(thumbnailView, archivedView, dateView));
+ this.post(new ThumbnailPositioner(thumbnailView, archivedView, deliveryStatusIndicator, dateView));
} else {
this.thumbnailView.setVisibility(View.GONE);
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);
}
}
+ 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) {
if (thread.isRead()) setBackgroundResource(readBackground);
else setBackgroundResource(unreadBackround);
@@ -204,20 +230,24 @@ public class ConversationListItem extends RelativeLayout
private final View thumbnailView;
private final View archivedView;
+ private final View deliveryStatusView;
private final View dateView;
- public ThumbnailPositioner(View thumbnailView, View archivedView, View dateView) {
- this.thumbnailView = thumbnailView;
- this.archivedView = archivedView;
- this.dateView = dateView;
+ public ThumbnailPositioner(View thumbnailView, View archivedView, View deliveryStatusView, View dateView) {
+ this.thumbnailView = thumbnailView;
+ this.archivedView = archivedView;
+ this.deliveryStatusView = deliveryStatusView;
+ this.dateView = dateView;
}
@Override
public void run() {
LayoutParams thumbnailParams = (RelativeLayout.LayoutParams)thumbnailView.getLayoutParams();
- if (archivedView.getVisibility() == View.VISIBLE && archivedView.getWidth() > dateView.getWidth()) {
- thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.archived);
+ if (archivedView.getVisibility() == View.VISIBLE &&
+ (archivedView.getWidth() + deliveryStatusView.getWidth()) > dateView.getWidth())
+ {
+ thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.delivery_status);
} else {
thumbnailParams.addRule(RelativeLayout.LEFT_OF, R.id.date);
}
diff --git a/src/org/thoughtcrime/securesms/components/AlertView.java b/src/org/thoughtcrime/securesms/components/AlertView.java
new file mode 100644
index 000000000..706f69ebe
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/AlertView.java
@@ -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);
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java b/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
new file mode 100644
index 000000000..faedeafbd
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
@@ -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);
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
index 4d16f8967..8044742c3 100644
--- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
+++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
@@ -69,7 +69,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22;
private static final int INTRODUCED_CONVERSATION_LIST_THUMBNAILS_VERSION = 23;
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 Object lock = new Object();
@@ -784,6 +785,11 @@ public class DatabaseFactory {
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.endTransaction();
}
diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
index a94234174..293452ff3 100644
--- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -214,6 +214,7 @@ public class MmsDatabase extends MessagingDatabase {
RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
+ DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
}
} catch (InvalidNumberException e) {
@@ -337,6 +338,8 @@ public class MmsDatabase extends MessagingDatabase {
db.execSQL("UPDATE " + TABLE_NAME +
" SET " + MESSAGE_BOX + " = (" + MESSAGE_BOX + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + " )" +
" WHERE " + ID + " = ?", new String[] {id + ""});
+
+ DatabaseFactory.getThreadDatabase(context).update(getThreadIdForMessage(id), false);
}
public void markAsOutbox(long messageId) {
diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
index 365c471fa..92bb00115 100644
--- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
@@ -120,7 +120,6 @@ public class SmsDatabase extends MessagingDatabase {
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
- notifyConversationListListeners();
}
public long getThreadIdForMessage(long id) {
@@ -265,12 +264,15 @@ public class SmsDatabase extends MessagingDatabase {
String ourAddress = canonicalizeNumber(context, cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (ourAddress.equals(theirAddress)) {
+ long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
+
database.execSQL("UPDATE " + TABLE_NAME +
" SET " + RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " +
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) {
Log.w("SmsDatabase", e);
diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
index b46815966..f5afa1c67 100644
--- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
@@ -64,12 +64,17 @@ public class ThreadDatabase extends Database {
public static final String SNIPPET_TYPE = "snippet_type";
public static final String SNIPPET_URI = "snippet_uri";
public static final String ARCHIVED = "archived";
+ public static final String STATUS = "status";
+ public static final String RECEIPT_COUNT = "delivery_receipt_count";
- public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
- DATE + " 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);";
+ public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
+ ID + " INTEGER PRIMARY KEY, " + DATE + " 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 = {
"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);
}
- 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(MESSAGE_COUNT, count);
contentValues.put(SNIPPET, body);
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
contentValues.put(SNIPPET_TYPE, type);
+ contentValues.put(STATUS, status);
+ contentValues.put(RECEIPT_COUNT, receiptCount);
if (unarchive) {
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) {
- ContentValues contentValues = new ContentValues(3);
+ ContentValues contentValues = new ContentValues(4);
contentValues.put(DATE, date - date % 1000);
contentValues.put(SNIPPET, snippet);
@@ -479,10 +487,9 @@ public class ThreadDatabase extends Database {
if (record.isPush()) timestamp = record.getDateSent();
else timestamp = record.getDateReceived();
- updateThread(threadId, count, record.getBody().getBody(),
- getAttachmentUriFor(record), timestamp,
+ updateThread(threadId, count, record.getBody().getBody(), getAttachmentUriFor(record),
+ timestamp, record.getDeliveryStatus(), record.getReceiptCount(),
record.getType(), unarchive);
-
notifyConversationListListeners();
return false;
} else {
@@ -549,10 +556,12 @@ public class ThreadDatabase extends Database {
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
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);
- return new ThreadRecord(context, body, snippetUri, recipients, date, count,
- read == 1, threadId, type, distributionType, archived);
+ return new ThreadRecord(context, body, snippetUri, recipients, date, count, read == 1,
+ threadId, receiptCount, status, type, distributionType, archived);
}
private DisplayRecord.Body getPlaintextBody(Cursor cursor) {
diff --git a/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java b/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java
index f0749c40f..9701d086f 100644
--- a/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java
+++ b/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java
@@ -43,10 +43,11 @@ public class ConversationListLoader extends AbstractCursorLoader {
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
ThreadDatabase.RECIPIENT_IDS, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
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,
- "-1", null, 1, ThreadDatabase.DistributionTypes.ARCHIVE, 0, null, 0});
+ "-1", null, 1, ThreadDatabase.DistributionTypes.ARCHIVE,
+ 0, null, 0, -1, 0});
cursorList.add(switchToArchiveCursor);
}
diff --git a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
index c47343882..f3a2c6d64 100644
--- a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
@@ -19,6 +19,7 @@ package org.thoughtcrime.securesms.database.model;
import android.content.Context;
import android.text.SpannableString;
+import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
@@ -40,9 +41,11 @@ public abstract class DisplayRecord {
private final long dateReceived;
private final long threadId;
private final Body body;
+ private final int deliveryStatus;
+ private final int receiptCount;
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.threadId = threadId;
@@ -51,12 +54,29 @@ public abstract class DisplayRecord {
this.dateReceived = dateReceived;
this.type = type;
this.body = body;
+ this.receiptCount = receiptCount;
+ this.deliveryStatus = deliveryStatus;
}
public Body getBody() {
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 Recipients getRecipients() {
@@ -115,6 +135,23 @@ public abstract class DisplayRecord {
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 {
private final String body;
private final boolean plaintext;
diff --git a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java
index c2473713c..deb32c37b 100644
--- a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java
@@ -21,6 +21,7 @@ import android.support.annotation.NonNull;
import android.text.SpannableString;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
@@ -47,16 +48,15 @@ public class MediaMmsMessageRecord extends MessageRecord {
public MediaMmsMessageRecord(Context context, long id, Recipients recipients,
Recipient individualRecipient, int recipientDeviceId,
- long dateSent, long dateReceived, int deliveredCount,
+ long dateSent, long dateReceived, int receiptCount,
long threadId, Body body,
@NonNull SlideDeck slideDeck,
int partCount, long mailbox,
List mismatches,
List failures)
{
- super(context, id, body, recipients, individualRecipient, recipientDeviceId,
- dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, deliveredCount, mailbox,
- mismatches, failures);
+ super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent,
+ dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures);
this.context = context.getApplicationContext();
this.partCount = partCount;
diff --git a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
index 5093d6616..1eb524daa 100644
--- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
@@ -43,18 +43,11 @@ import java.util.List;
*/
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 final Recipient individualRecipient;
private final int recipientDeviceId;
private final long id;
- private final int deliveryStatus;
- private final int receiptCount;
private final List mismatches;
private final List networkFailures;
@@ -65,12 +58,11 @@ public abstract class MessageRecord extends DisplayRecord {
List mismatches,
List networkFailures)
{
- super(context, body, recipients, dateSent, dateReceived, threadId, type);
+ super(context, body, recipients, dateSent, dateReceived, threadId, deliveryStatus, receiptCount,
+ type);
this.id = id;
this.individualRecipient = individualRecipient;
this.recipientDeviceId = recipientDeviceId;
- this.deliveryStatus = deliveryStatus;
- this.receiptCount = receiptCount;
this.mismatches = mismatches;
this.networkFailures = networkFailures;
}
@@ -78,21 +70,6 @@ public abstract class MessageRecord extends DisplayRecord {
public abstract boolean isMms();
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() {
return MmsSmsColumns.Types.isSecureType(type);
}
@@ -134,14 +111,6 @@ public abstract class MessageRecord extends DisplayRecord {
return id;
}
- public int getDeliveryStatus() {
- return deliveryStatus;
- }
-
- public boolean isDelivered() {
- return getDeliveryStatus() == DELIVERY_STATUS_RECEIVED || receiptCount > 0;
- }
-
public boolean isPush() {
return SmsDatabase.Types.isPushType(type) && !SmsDatabase.Types.isForcedSms(type);
}
@@ -158,10 +127,6 @@ public abstract class MessageRecord extends DisplayRecord {
return SmsDatabase.Types.isProcessedKeyExchange(type);
}
- public boolean isPendingInsecureSmsFallback() {
- return SmsDatabase.Types.isPendingInsecureSmsFallbackType(type);
- }
-
public boolean isIdentityMismatchFailure() {
return mismatches != null && !mismatches.isEmpty();
}
diff --git a/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java
index b9829d228..2dfa01547 100644
--- a/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.text.SpannableString;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
@@ -51,7 +52,7 @@ public class NotificationMmsMessageRecord extends MessageRecord {
long expiry, int status, byte[] transactionId, long mailbox)
{
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(), new LinkedList());
this.contentLocation = contentLocation;
diff --git a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
index 03a8d87fe..107df9951 100644
--- a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
@@ -50,7 +50,7 @@ public class SmsMessageRecord extends MessageRecord {
int status, List mismatches)
{
super(context, id, body, recipients, individualRecipient, recipientDeviceId,
- dateSent, dateReceived, threadId, getGenericDeliveryStatus(status), receiptCount, type,
+ dateSent, dateReceived, threadId, status, receiptCount, type,
mismatches, new LinkedList());
}
@@ -104,16 +104,4 @@ public class SmsMessageRecord extends MessageRecord {
public boolean isMmsNotification() {
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;
- }
- }
}
diff --git a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
index 471d26da6..f6a8af904 100644
--- a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
@@ -28,6 +28,7 @@ import android.text.style.StyleSpan;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
+import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
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,
@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.snippetUri = snippetUri;
this.count = count;