diff --git a/build.gradle b/build.gradle
index 3035c1cf45..4cace387c3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -63,7 +63,7 @@ dependencies {
compile 'org.whispersystems:jobmanager:1.0.2'
compile 'org.whispersystems:libpastelog:1.0.7'
- compile 'org.whispersystems:signal-service-android:2.6.5'
+ compile 'org.whispersystems:signal-service-android:2.6.6'
compile 'org.whispersystems:webrtc-android:M59-S1'
compile "me.leolin:ShortcutBadger:1.1.16"
@@ -138,7 +138,7 @@ dependencyVerification {
'com.google.android.exoplayer:exoplayer:955085aa611a8f7cf6c61b88ae03d1a392f4ad94c9bfbc153f3dedb9ffb14718',
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
'org.whispersystems:libpastelog:bb331d9a98240fc139101128ba836c1edec3c40e000597cdbb29ebf4cbf34d88',
- 'org.whispersystems:signal-service-android:690e04d53c8b5ec8cda064b242d7c00b0e5321851b811798dd0ec3712f5e1a85',
+ 'org.whispersystems:signal-service-android:731fc8c45f38f42b2d0da1053cf32adcd175708e0c126571150690637108971a',
'org.whispersystems:webrtc-android:de647643afbbea45a26a4f24db75aa10bc8de45426e8eb0d9d563cc10af4f582',
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
@@ -173,7 +173,7 @@ dependencyVerification {
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d',
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70',
'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1',
- 'org.whispersystems:signal-service-java:438e8330cf806152e7e226d8241dd6388ee4005f0ea7d2aaa99d7ef514012dca',
+ 'org.whispersystems:signal-service-java:90aadf941cc31cb0f5af9e1adffa248556f1cfc56a62c141bc62812e7bf3ed52',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541',
diff --git a/res/layout/delivery_status_view.xml b/res/layout/delivery_status_view.xml
index a28be96731..4281be8828 100644
--- a/res/layout/delivery_status_view.xml
+++ b/res/layout/delivery_status_view.xml
@@ -27,6 +27,18 @@
android:paddingBottom="2dp"
android:visibility="gone"
android:contentDescription="@string/conversation_item_sent__delivered_description"
+ tools:visibility="gone"/>
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6ec9785e12..85d85cad5a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1456,7 +1456,9 @@
Transport icon
-
+ Message read
+ Read receipts
+ If you read receipts are disabled, you won\'t be able to see read receipts from others.
diff --git a/res/xml/preferences_app_protection.xml b/res/xml/preferences_app_protection.xml
index d103fe7430..a0fa003aed 100644
--- a/res/xml/preferences_app_protection.xml
+++ b/res/xml/preferences_app_protection.xml
@@ -40,6 +40,12 @@
android:title="@string/preferences_advanced__always_relay_calls"
android:summary="@string/preferences_advanced__relay_all_calls_through_the_signal_server_to_avoid_revealing_your_ip_address"/>
+
+
diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java
index 5202747e5c..e45fbbfec3 100644
--- a/src/org/thoughtcrime/securesms/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationItem.java
@@ -443,10 +443,11 @@ public class ConversationItem extends LinearLayout
} else {
alertView.setNone();
- if (!messageRecord.isOutgoing()) deliveryStatusIndicator.setNone();
- else if (messageRecord.isPending()) deliveryStatusIndicator.setPending();
- else if (messageRecord.isDelivered()) deliveryStatusIndicator.setDelivered();
- else deliveryStatusIndicator.setSent();
+ if (!messageRecord.isOutgoing()) deliveryStatusIndicator.setNone();
+ else if (messageRecord.isPending()) deliveryStatusIndicator.setPending();
+ else if (messageRecord.isRemoteRead()) deliveryStatusIndicator.setRead();
+ else if (messageRecord.isDelivered()) deliveryStatusIndicator.setDelivered();
+ else deliveryStatusIndicator.setSent();
}
}
diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java
index 75438e56cf..a5cfdb32bd 100644
--- a/src/org/thoughtcrime/securesms/ConversationListItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationListItem.java
@@ -214,9 +214,10 @@ public class ConversationListItem extends RelativeLayout
} else {
alertView.setNone();
- if (thread.isPending()) deliveryStatusIndicator.setPending();
- else if (thread.isDelivered()) deliveryStatusIndicator.setDelivered();
- else deliveryStatusIndicator.setSent();
+ if (thread.isPending()) deliveryStatusIndicator.setPending();
+ else if (thread.isRemoteRead()) deliveryStatusIndicator.setRead();
+ else if (thread.isDelivered()) deliveryStatusIndicator.setDelivered();
+ else deliveryStatusIndicator.setSent();
}
}
diff --git a/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java b/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java
index f1d723e182..1fec151451 100644
--- a/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java
+++ b/src/org/thoughtcrime/securesms/PassphraseCreateActivity.java
@@ -68,6 +68,7 @@ public class PassphraseCreateActivity extends PassphraseActivity {
VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this);
TextSecurePreferences.setLastExperienceVersionCode(PassphraseCreateActivity.this, Util.getCurrentApkReleaseVersion(PassphraseCreateActivity.this));
TextSecurePreferences.setPasswordDisabled(PassphraseCreateActivity.this, true);
+ TextSecurePreferences.setReadReceiptsEnabled(PassphraseCreateActivity.this, true);
return null;
}
diff --git a/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java b/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
index faedeafbd6..ffb53c6d78 100644
--- a/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
+++ b/src/org/thoughtcrime/securesms/components/DeliveryStatusView.java
@@ -22,6 +22,7 @@ public class DeliveryStatusView extends FrameLayout {
private final ViewGroup pendingIndicatorStub;
private final ImageView sentIndicator;
private final ImageView deliveredIndicator;
+ private final ImageView readIndicator;
public DeliveryStatusView(Context context) {
this(context, null);
@@ -39,6 +40,7 @@ public class DeliveryStatusView extends FrameLayout {
this.deliveredIndicator = (ImageView) findViewById(R.id.delivered_indicator);
this.sentIndicator = (ImageView) findViewById(R.id.sent_indicator);
this.pendingIndicatorStub = (ViewGroup) findViewById(R.id.pending_indicator_stub);
+ this.readIndicator = (ImageView) findViewById(R.id.read_indicator);
int iconColor = Color.GRAY;
@@ -71,6 +73,7 @@ public class DeliveryStatusView extends FrameLayout {
pendingIndicatorStub.setVisibility(View.VISIBLE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.GONE);
+ readIndicator.setVisibility(View.GONE);
}
public void setSent() {
@@ -78,6 +81,7 @@ public class DeliveryStatusView extends FrameLayout {
pendingIndicatorStub.setVisibility(View.GONE);
sentIndicator.setVisibility(View.VISIBLE);
deliveredIndicator.setVisibility(View.GONE);
+ readIndicator.setVisibility(View.GONE);
}
public void setDelivered() {
@@ -85,5 +89,14 @@ public class DeliveryStatusView extends FrameLayout {
pendingIndicatorStub.setVisibility(View.GONE);
sentIndicator.setVisibility(View.GONE);
deliveredIndicator.setVisibility(View.VISIBLE);
+ readIndicator.setVisibility(View.GONE);
+ }
+
+ public void setRead() {
+ this.setVisibility(View.VISIBLE);
+ pendingIndicatorStub.setVisibility(View.GONE);
+ sentIndicator.setVisibility(View.GONE);
+ deliveredIndicator.setVisibility(View.GONE);
+ readIndicator.setVisibility(View.VISIBLE);
}
}
diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
index 9922d5fa56..2ca34af052 100644
--- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
+++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
@@ -106,7 +106,8 @@ public class DatabaseFactory {
private static final int PROFILES = 41;
private static final int PROFILE_SHARING_APPROVAL = 42;
private static final int UNSEEN_NUMBER_OFFER = 43;
- private static final int DATABASE_VERSION = 43;
+ private static final int READ_RECEIPTS = 44;
+ private static final int DATABASE_VERSION = 44;
private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object();
@@ -1321,6 +1322,12 @@ public class DatabaseFactory {
db.execSQL("ALTER TABLE thread ADD COLUMN has_sent INTEGER DEFAULT 0");
}
+ if (oldVersion < READ_RECEIPTS) {
+ db.execSQL("ALTER TABLE sms ADD COLUMN read_receipt_count INTEGER DEFAULT 0");
+ db.execSQL("ATLER TABLE mms ADD COLUMN read_receipt_count INTEGER DEFAULT 0");
+ db.execSQL("ALTER TABLE thread ADD COLUMN read_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 0e3aea9db9..7ff7447cf8 100644
--- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.util.JsonUtils;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.InvalidMessageException;
@@ -101,10 +102,11 @@ public class MmsDatabase extends MessagingDatabase {
STATUS + " INTEGER, " + TRANSACTION_ID + " TEXT, " + "retr_st" + " INTEGER, " +
"retr_txt" + " TEXT, " + "retr_txt_cs" + " INTEGER, " + "read_status" + " INTEGER, " +
"ct_cls" + " INTEGER, " + "resp_txt" + " TEXT, " + "d_tm" + " INTEGER, " +
- RECEIPT_COUNT + " INTEGER DEFAULT 0, " + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " +
+ DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " +
NETWORK_FAILURE + " TEXT DEFAULT NULL," + "d_rpt" + " INTEGER, " +
SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " + EXPIRES_IN + " INTEGER DEFAULT 0, " +
- EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " INTEGER DEFAULT 0);";
+ EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " INTEGER DEFAULT 0, " +
+ READ_RECEIPT_COUNT + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@@ -123,7 +125,7 @@ public class MmsDatabase extends MessagingDatabase {
CONTENT_LOCATION, EXPIRY, MESSAGE_TYPE,
MESSAGE_SIZE, STATUS, TRANSACTION_ID,
BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID,
- RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
+ DELIVERY_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED,
AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + " AS " + AttachmentDatabase.ATTACHMENT_ID_ALIAS,
AttachmentDatabase.UNIQUE_ID,
@@ -144,7 +146,9 @@ public class MmsDatabase extends MessagingDatabase {
private static final String RAW_ID_WHERE = TABLE_NAME + "._id = ?";
- private final EarlyReceiptCache earlyReceiptCache = new EarlyReceiptCache();
+ private final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache();
+ private final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache();
+
private final JobManager jobManager;
public MmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
@@ -190,7 +194,7 @@ public class MmsDatabase extends MessagingDatabase {
}
}
- public void incrementDeliveryReceiptCount(SyncMessageId messageId) {
+ public void incrementReceiptCount(SyncMessageId messageId, boolean deliveryReceipt, boolean readReceipt) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
boolean found = false;
@@ -201,7 +205,8 @@ public class MmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
- Address ourAddress = messageId.getAddress();
+ Address ourAddress = messageId.getAddress();
+ String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
@@ -210,7 +215,7 @@ public class MmsDatabase extends MessagingDatabase {
found = true;
database.execSQL("UPDATE " + TABLE_NAME + " SET " +
- RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?",
+ columnName + " = " + columnName + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
@@ -220,7 +225,8 @@ public class MmsDatabase extends MessagingDatabase {
}
if (!found) {
- earlyReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
+ if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
+ if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
}
} finally {
if (cursor != null)
@@ -803,7 +809,8 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn());
contentValues.put(ADDRESS, message.getRecipient().getAddress().serialize());
- contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(), message.getRecipient().getAddress()));
+ contentValues.put(DELIVERY_RECEIPT_COUNT, earlyDeliveryReceiptCache.remove(message.getSentTimeMillis(), message.getRecipient().getAddress()));
+ contentValues.put(READ_RECEIPT_COUNT, earlyReadReceiptCache.remove(message.getSentTimeMillis(), message.getRecipient().getAddress()));
long messageId = insertMediaMessage(masterSecret, message.getBody(), message.getAttachments(), contentValues, insertListener);
@@ -1060,7 +1067,7 @@ public class MmsDatabase extends MessagingDatabase {
new LinkedList(),
message.getSubscriptionId(),
message.getExpiresIn(),
- System.currentTimeMillis());
+ System.currentTimeMillis(), 0);
}
}
@@ -1096,22 +1103,27 @@ public class MmsDatabase extends MessagingDatabase {
}
private NotificationMmsMessageRecord getNotificationMmsMessageRecord(Cursor cursor) {
- long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
- long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT));
- long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
- long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
- long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
- String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
- int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
- Recipient recipient = getRecipientFor(address);
+ long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
+ long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT));
+ long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
+ long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
+ long mailbox = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
+ String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
+ int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
+ Recipient recipient = getRecipientFor(address);
- String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION));
- String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID));
- long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_SIZE));
- long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY));
- int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.STATUS));
- int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.RECEIPT_COUNT));
- int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
+ String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION));
+ String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID));
+ long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_SIZE));
+ long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY));
+ int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.STATUS));
+ int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT));
+ int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT));
+ int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
+
+ if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
+ readReceiptCount = 0;
+ }
byte[]contentLocationBytes = null;
byte[]transactionIdBytes = null;
@@ -1126,27 +1138,33 @@ public class MmsDatabase extends MessagingDatabase {
return new NotificationMmsMessageRecord(context, id, recipient, recipient,
- addressDeviceId, dateSent, dateReceived, receiptCount, threadId,
+ addressDeviceId, dateSent, dateReceived, deliveryReceiptCount, threadId,
contentLocationBytes, messageSize, expiry, status,
- transactionIdBytes, mailbox, subscriptionId, slideDeck);
+ transactionIdBytes, mailbox, subscriptionId, slideDeck,
+ readReceiptCount);
}
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
- long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
- long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT));
- long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
- long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
- long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
- String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
- int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
- int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.RECEIPT_COUNT));
- DisplayRecord.Body body = getBody(cursor);
- int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT));
- String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
- String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
- int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
- long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
- long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
+ long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
+ long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_SENT));
+ long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.NORMALIZED_DATE_RECEIVED));
+ long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
+ long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
+ String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
+ int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
+ int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.DELIVERY_RECEIPT_COUNT));
+ int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.READ_RECEIPT_COUNT));
+ DisplayRecord.Body body = getBody(cursor);
+ int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT));
+ String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
+ String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
+ int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
+ long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
+ long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
+
+ if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
+ readReceiptCount = 0;
+ }
Recipient recipient = getRecipientFor(address);
List mismatches = getMismatchedIdentities(mismatchDocument);
@@ -1154,9 +1172,10 @@ public class MmsDatabase extends MessagingDatabase {
SlideDeck slideDeck = getSlideDeck(cursor);
return new MediaMmsMessageRecord(context, id, recipient, recipient,
- addressDeviceId, dateSent, dateReceived, receiptCount,
+ addressDeviceId, dateSent, dateReceived, deliveryReceiptCount,
threadId, body, slideDeck, partCount, box, mismatches,
- networkFailures, subscriptionId, expiresIn, expireStarted);
+ networkFailures, subscriptionId, expiresIn, expireStarted,
+ readReceiptCount);
}
private Recipient getRecipientFor(String serialized) {
diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
index 59849c96ff..840bafb72e 100644
--- a/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
+++ b/src/org/thoughtcrime/securesms/database/MmsSmsColumns.java
@@ -11,7 +11,8 @@ public interface MmsSmsColumns {
public static final String BODY = "body";
public static final String ADDRESS = "address";
public static final String ADDRESS_DEVICE_ID = "address_device_id";
- public static final String RECEIPT_COUNT = "delivery_receipt_count";
+ public static final String DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
+ public static final String READ_RECEIPT_COUNT = "read_receipt_count";
public static final String MISMATCHED_IDENTITIES = "mismatched_identities";
public static final String UNIQUE_ROW_ID = "unique_row_id";
public static final String SUBSCRIPTION_ID = "subscription_id";
diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java
index 61f6320ab1..801ff2d2a8 100644
--- a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java
@@ -25,7 +25,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
-import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -52,7 +51,9 @@ public class MmsSmsDatabase extends Database {
SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY,
- MmsDatabase.STATUS, MmsSmsColumns.RECEIPT_COUNT,
+ MmsDatabase.STATUS,
+ MmsSmsColumns.DELIVERY_RECEIPT_COUNT,
+ MmsSmsColumns.READ_RECEIPT_COUNT,
MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsDatabase.NETWORK_FAILURE,
MmsSmsColumns.SUBSCRIPTION_ID,
@@ -137,8 +138,13 @@ public class MmsSmsDatabase extends Database {
}
public void incrementDeliveryReceiptCount(SyncMessageId syncMessageId) {
- DatabaseFactory.getSmsDatabase(context).incrementDeliveryReceiptCount(syncMessageId);
- DatabaseFactory.getMmsDatabase(context).incrementDeliveryReceiptCount(syncMessageId);
+ DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, true, false);
+ DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, true, false);
+ }
+
+ public void incrementReadReceiptCount(SyncMessageId syncMessageId) {
+ DatabaseFactory.getSmsDatabase(context).incrementReceiptCount(syncMessageId, false, true);
+ DatabaseFactory.getMmsDatabase(context).incrementReceiptCount(syncMessageId, false, true);
}
private Cursor queryTables(String[] projection, String selection, String order, String limit) {
@@ -154,10 +160,11 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
- MmsSmsColumns.RECEIPT_COUNT, MmsSmsColumns.MISMATCHED_IDENTITIES,
+ MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT,
+ MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsSmsColumns.SUBSCRIPTION_ID, MmsSmsColumns.EXPIRES_IN, MmsSmsColumns.EXPIRE_STARTED,
MmsSmsColumns.NOTIFIED,
- MmsDatabase.NETWORK_FAILURE, TRANSPORT,
+ MmsDatabase.NETWORK_FAILURE, TRANSPORT,
AttachmentDatabase.UNIQUE_ID,
AttachmentDatabase.MMS_ID,
AttachmentDatabase.SIZE,
@@ -185,7 +192,8 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
- MmsSmsColumns.RECEIPT_COUNT, MmsSmsColumns.MISMATCHED_IDENTITIES,
+ MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT,
+ MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsSmsColumns.SUBSCRIPTION_ID, MmsSmsColumns.EXPIRES_IN, MmsSmsColumns.EXPIRE_STARTED,
MmsSmsColumns.NOTIFIED,
MmsDatabase.NETWORK_FAILURE, TRANSPORT,
@@ -227,7 +235,8 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(MmsSmsColumns.BODY);
mmsColumnsPresent.add(MmsSmsColumns.ADDRESS);
mmsColumnsPresent.add(MmsSmsColumns.ADDRESS_DEVICE_ID);
- mmsColumnsPresent.add(MmsSmsColumns.RECEIPT_COUNT);
+ mmsColumnsPresent.add(MmsSmsColumns.DELIVERY_RECEIPT_COUNT);
+ mmsColumnsPresent.add(MmsSmsColumns.READ_RECEIPT_COUNT);
mmsColumnsPresent.add(MmsSmsColumns.MISMATCHED_IDENTITIES);
mmsColumnsPresent.add(MmsSmsColumns.SUBSCRIPTION_ID);
mmsColumnsPresent.add(MmsSmsColumns.EXPIRES_IN);
@@ -268,7 +277,8 @@ public class MmsSmsDatabase extends Database {
smsColumnsPresent.add(MmsSmsColumns.ADDRESS_DEVICE_ID);
smsColumnsPresent.add(MmsSmsColumns.READ);
smsColumnsPresent.add(MmsSmsColumns.THREAD_ID);
- smsColumnsPresent.add(MmsSmsColumns.RECEIPT_COUNT);
+ smsColumnsPresent.add(MmsSmsColumns.DELIVERY_RECEIPT_COUNT);
+ smsColumnsPresent.add(MmsSmsColumns.READ_RECEIPT_COUNT);
smsColumnsPresent.add(MmsSmsColumns.MISMATCHED_IDENTITIES);
smsColumnsPresent.add(MmsSmsColumns.SUBSCRIPTION_ID);
smsColumnsPresent.add(MmsSmsColumns.EXPIRES_IN);
diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
index 342fc20bd7..1f3e5f0a7a 100644
--- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
@@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.JsonUtils;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -73,9 +74,10 @@ public class SmsDatabase extends MessagingDatabase {
THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " +
DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " +
STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " +
- RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " +
+ DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " +
MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT, " + SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
- EXPIRES_IN + " INTEGER DEFAULT 0, " + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " DEFAULT 0);";
+ EXPIRES_IN + " INTEGER DEFAULT 0, " + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " DEFAULT 0, " +
+ READ_RECEIPT_COUNT + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@@ -91,12 +93,14 @@ public class SmsDatabase extends MessagingDatabase {
DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED,
DATE_SENT + " AS " + NORMALIZED_DATE_SENT,
PROTOCOL, READ, STATUS, TYPE,
- REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, RECEIPT_COUNT,
+ REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT,
MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED,
- NOTIFIED
+ NOTIFIED, READ_RECEIPT_COUNT
};
- private static final EarlyReceiptCache earlyReceiptCache = new EarlyReceiptCache();
+ private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache();
+ private static final EarlyReceiptCache earlyReadReceiptCache = new EarlyReceiptCache();
+
private final JobManager jobManager;
public SmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
@@ -277,7 +281,7 @@ public class SmsDatabase extends MessagingDatabase {
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
}
- public void incrementDeliveryReceiptCount(SyncMessageId messageId) {
+ public void incrementReceiptCount(SyncMessageId messageId, boolean deliveryReceipt, boolean readReceipt) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
boolean foundMessage = false;
@@ -291,12 +295,13 @@ public class SmsDatabase extends MessagingDatabase {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
Address theirAddress = messageId.getAddress();
Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
+ String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
if (ourAddress.equals(theirAddress)) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
database.execSQL("UPDATE " + TABLE_NAME +
- " SET " + RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " +
+ " SET " + columnName + " = " + columnName + " + 1 WHERE " +
ID + " = ?",
new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))});
@@ -308,7 +313,8 @@ public class SmsDatabase extends MessagingDatabase {
}
if (!foundMessage) {
- earlyReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
+ if (deliveryReceipt) earlyDeliveryReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
+ if (readReceipt) earlyReadReceiptCache.increment(messageId.getTimetamp(), messageId.getAddress());
}
} finally {
@@ -600,7 +606,8 @@ public class SmsDatabase extends MessagingDatabase {
contentValues.put(TYPE, type);
contentValues.put(SUBSCRIPTION_ID, message.getSubscriptionId());
contentValues.put(EXPIRES_IN, message.getExpiresIn());
- contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(date, address));
+ contentValues.put(DELIVERY_RECEIPT_COUNT, earlyDeliveryReceiptCache.remove(date, address));
+ contentValues.put(READ_RECEIPT_COUNT, earlyReadReceiptCache.remove(date, address));
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
@@ -789,7 +796,7 @@ public class SmsDatabase extends MessagingDatabase {
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
threadId, 0, new LinkedList(),
message.getSubscriptionId(), message.getExpiresIn(),
- System.currentTimeMillis());
+ System.currentTimeMillis(), 0);
}
}
@@ -814,19 +821,24 @@ public class SmsDatabase extends MessagingDatabase {
}
public SmsMessageRecord getCurrent() {
- long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
- Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)));
- int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID));
- long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
- long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED));
- long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT));
- long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
- int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS));
- int receiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.RECEIPT_COUNT));
- String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES));
- int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID));
- long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN));
- long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
+ long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
+ Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)));
+ int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID));
+ long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
+ long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED));
+ long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT));
+ long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
+ int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS));
+ int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.DELIVERY_RECEIPT_COUNT));
+ int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.READ_RECEIPT_COUNT));
+ String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.MISMATCHED_IDENTITIES));
+ int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.SUBSCRIPTION_ID));
+ long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN));
+ long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
+
+ if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
+ readReceiptCount = 0;
+ }
List mismatches = getMismatches(mismatchDocument);
Recipient recipient = Recipient.from(context, address, true);
@@ -835,9 +847,9 @@ public class SmsDatabase extends MessagingDatabase {
return new SmsMessageRecord(context, messageId, body, recipient,
recipient,
addressDeviceId,
- dateSent, dateReceived, receiptCount, type,
+ dateSent, dateReceived, deliveryReceiptCount, type,
threadId, status, mismatches, subscriptionId,
- expiresIn, expireStarted);
+ expiresIn, expireStarted, readReceiptCount);
}
private List getMismatches(String document) {
diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
index 5f8c4dd3ec..66a1ebac64 100644
--- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
@@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DelimiterUtil;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.Pair;
@@ -56,34 +57,36 @@ public class ThreadDatabase extends Database {
private static final String TAG = ThreadDatabase.class.getSimpleName();
- static final String TABLE_NAME = "thread";
- public static final String ID = "_id";
- public static final String DATE = "date";
- public static final String MESSAGE_COUNT = "message_count";
- public static final String ADDRESS = "recipient_ids";
- public static final String SNIPPET = "snippet";
- private static final String SNIPPET_CHARSET = "snippet_cs";
- public static final String READ = "read";
- public static final String TYPE = "type";
- private static final String ERROR = "error";
- 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 EXPIRES_IN = "expires_in";
- public static final String LAST_SEEN = "last_seen";
- private static final String HAS_SENT = "has_sent";
+ static final String TABLE_NAME = "thread";
+ public static final String ID = "_id";
+ public static final String DATE = "date";
+ public static final String MESSAGE_COUNT = "message_count";
+ public static final String ADDRESS = "recipient_ids";
+ public static final String SNIPPET = "snippet";
+ private static final String SNIPPET_CHARSET = "snippet_cs";
+ public static final String READ = "read";
+ public static final String TYPE = "type";
+ private static final String ERROR = "error";
+ 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 DELIVERY_RECEIPT_COUNT = "delivery_receipt_count";
+ public static final String READ_RECEIPT_COUNT = "read_receipt_count";
+ public static final String EXPIRES_IN = "expires_in";
+ public static final String LAST_SEEN = "last_seen";
+ private static final String HAS_SENT = "has_sent";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
- MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " +
+ MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " 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, " + EXPIRES_IN + " INTEGER DEFAULT 0, " +
- LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0);";
+ DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " +
+ LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " +
+ READ_RECEIPT_COUNT + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");",
@@ -92,7 +95,7 @@ public class ThreadDatabase extends Database {
private static final String[] THREAD_PROJECTION = {
ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, TYPE, ERROR, SNIPPET_TYPE,
- SNIPPET_URI, ARCHIVED, STATUS, RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN
+ SNIPPET_URI, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT
};
private static final List TYPED_THREAD_PROJECTION = Stream.of(THREAD_PROJECTION)
@@ -125,8 +128,8 @@ public class ThreadDatabase extends Database {
}
private void updateThread(long threadId, long count, String body, @Nullable Uri attachment,
- long date, int status, int receiptCount, long type, boolean unarchive,
- long expiresIn)
+ long date, int status, int deliveryReceiptCount, long type, boolean unarchive,
+ long expiresIn, int readReceiptCount)
{
ContentValues contentValues = new ContentValues(7);
contentValues.put(DATE, date - date % 1000);
@@ -135,7 +138,8 @@ public class ThreadDatabase extends Database {
contentValues.put(SNIPPET_URI, attachment == null ? null : attachment.toString());
contentValues.put(SNIPPET_TYPE, type);
contentValues.put(STATUS, status);
- contentValues.put(RECEIPT_COUNT, receiptCount);
+ contentValues.put(DELIVERY_RECEIPT_COUNT, deliveryReceiptCount);
+ contentValues.put(READ_RECEIPT_COUNT, readReceiptCount);
contentValues.put(EXPIRES_IN, expiresIn);
if (unarchive) {
@@ -550,8 +554,8 @@ public class ThreadDatabase extends Database {
if (reader != null && (record = reader.getNext()) != null) {
updateThread(threadId, count, record.getBody().getBody(), getAttachmentUriFor(record),
- record.getTimestamp(), record.getDeliveryStatus(), record.getReceiptCount(),
- record.getType(), unarchive, record.getExpiresIn());
+ record.getTimestamp(), record.getDeliveryStatus(), record.getDeliveryReceiptCount(),
+ record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount());
notifyConversationListListeners();
return false;
} else {
@@ -633,22 +637,27 @@ public class ThreadDatabase extends Database {
groupRecord = Optional.absent();
}
- Recipient recipient = Recipient.from(context, address, settings, groupRecord, true);
- DisplayRecord.Body body = getPlaintextBody(cursor);
- long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
- long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT));
- long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ));
- long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_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));
- long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.EXPIRES_IN));
- long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN));
- Uri snippetUri = getSnippetUri(cursor);
+ Recipient recipient = Recipient.from(context, address, settings, groupRecord, true);
+ DisplayRecord.Body body = getPlaintextBody(cursor);
+ long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
+ long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT));
+ long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ));
+ long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
+ boolean archived = cursor.getInt(cursor.getColumnIndex(ThreadDatabase.ARCHIVED)) != 0;
+ int status = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.STATUS));
+ int deliveryReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.DELIVERY_RECEIPT_COUNT));
+ int readReceiptCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.READ_RECEIPT_COUNT));
+ long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.EXPIRES_IN));
+ long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN));
+ Uri snippetUri = getSnippetUri(cursor);
+
+ if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
+ readReceiptCount = 0;
+ }
return new ThreadRecord(context, body, snippetUri, recipient, date, count, read == 1,
- threadId, receiptCount, status, type, distributionType, archived,
- expiresIn, lastSeen);
+ threadId, deliveryReceiptCount, status, type, distributionType, archived,
+ expiresIn, lastSeen, readReceiptCount);
}
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 30b29ce76c..1d527fd948 100644
--- a/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java
+++ b/src/org/thoughtcrime/securesms/database/loaders/ConversationListLoader.java
@@ -44,12 +44,12 @@ public class ConversationListLoader extends AbstractCursorLoader {
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
ThreadDatabase.ADDRESS, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
ThreadDatabase.TYPE, ThreadDatabase.SNIPPET_TYPE, ThreadDatabase.SNIPPET_URI,
- ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.RECEIPT_COUNT,
- ThreadDatabase.EXPIRES_IN, ThreadDatabase.LAST_SEEN}, 1);
+ ThreadDatabase.ARCHIVED, ThreadDatabase.STATUS, ThreadDatabase.DELIVERY_RECEIPT_COUNT,
+ ThreadDatabase.EXPIRES_IN, ThreadDatabase.LAST_SEEN, ThreadDatabase.READ_RECEIPT_COUNT}, 1);
switchToArchiveCursor.addRow(new Object[] {-1L, System.currentTimeMillis(), archivedCount,
"-1", null, 1, ThreadDatabase.DistributionTypes.ARCHIVE,
- 0, null, 0, -1, 0, 0, 0});
+ 0, null, 0, -1, 0, 0, 0, -1});
cursorList.add(switchToArchiveCursor);
}
diff --git a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
index 803fc6104d..b5bad1f4b1 100644
--- a/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/DisplayRecord.java
@@ -42,10 +42,12 @@ public abstract class DisplayRecord {
private final long threadId;
private final Body body;
private final int deliveryStatus;
- private final int receiptCount;
+ private final int deliveryReceiptCount;
+ private final int readReceiptCount;
public DisplayRecord(Context context, Body body, Recipient recipient, long dateSent,
- long dateReceived, long threadId, int deliveryStatus, int receiptCount, long type)
+ long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount,
+ long type, int readReceiptCount)
{
this.context = context.getApplicationContext();
this.threadId = threadId;
@@ -54,7 +56,8 @@ public abstract class DisplayRecord {
this.dateReceived = dateReceived;
this.type = type;
this.body = body;
- this.receiptCount = receiptCount;
+ this.deliveryReceiptCount = deliveryReceiptCount;
+ this.readReceiptCount = readReceiptCount;
this.deliveryStatus = deliveryStatus;
}
@@ -145,13 +148,21 @@ public abstract class DisplayRecord {
return deliveryStatus;
}
- public int getReceiptCount() {
- return receiptCount;
+ public int getDeliveryReceiptCount() {
+ return deliveryReceiptCount;
+ }
+
+ public int getReadReceiptCount() {
+ return readReceiptCount;
}
public boolean isDelivered() {
return (deliveryStatus >= SmsDatabase.Status.STATUS_COMPLETE &&
- deliveryStatus < SmsDatabase.Status.STATUS_PENDING) || receiptCount > 0;
+ deliveryStatus < SmsDatabase.Status.STATUS_PENDING) || deliveryReceiptCount > 0;
+ }
+
+ public boolean isRemoteRead() {
+ return readReceiptCount > 0;
}
public boolean isPendingInsecureSmsFallback() {
diff --git a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java
index 4b126753ce..2888e1ab72 100644
--- a/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MediaMmsMessageRecord.java
@@ -46,17 +46,17 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
public MediaMmsMessageRecord(Context context, long id, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId,
- long dateSent, long dateReceived, int receiptCount,
+ long dateSent, long dateReceived, int deliveryReceiptCount,
long threadId, Body body,
@NonNull SlideDeck slideDeck,
int partCount, long mailbox,
List mismatches,
List failures, int subscriptionId,
- long expiresIn, long expireStarted)
+ long expiresIn, long expireStarted, int readReceiptCount)
{
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
- dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures,
- subscriptionId, expiresIn, expireStarted, slideDeck);
+ dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures,
+ subscriptionId, expiresIn, expireStarted, slideDeck, readReceiptCount);
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 8906c9297f..d1707cbe1c 100644
--- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
@@ -57,13 +57,14 @@ public abstract class MessageRecord extends DisplayRecord {
MessageRecord(Context context, long id, Body body, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, long threadId,
- int deliveryStatus, int receiptCount, long type,
+ int deliveryStatus, int deliveryReceiptCount, long type,
List mismatches,
List networkFailures,
- int subscriptionId, long expiresIn, long expireStarted)
+ int subscriptionId, long expiresIn, long expireStarted,
+ int readReceiptCount)
{
- super(context, body, conversationRecipient, dateSent, dateReceived, threadId, deliveryStatus, receiptCount,
- type);
+ super(context, body, conversationRecipient, dateSent, dateReceived,
+ threadId, deliveryStatus, deliveryReceiptCount, type, readReceiptCount);
this.id = id;
this.individualRecipient = individualRecipient;
this.recipientDeviceId = recipientDeviceId;
diff --git a/src/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java
index 5c16f6022c..94fccc3c1a 100644
--- a/src/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MmsMessageRecord.java
@@ -18,12 +18,12 @@ public abstract class MmsMessageRecord extends MessageRecord {
MmsMessageRecord(Context context, long id, Body body, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId, long dateSent,
- long dateReceived, long threadId, int deliveryStatus, int receiptCount,
+ long dateReceived, long threadId, int deliveryStatus, int deliveryReceiptCount,
long type, List mismatches,
List networkFailures, int subscriptionId, long expiresIn,
- long expireStarted, @NonNull SlideDeck slideDeck)
+ long expireStarted, @NonNull SlideDeck slideDeck, int readReceiptCount)
{
- super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, receiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted);
+ super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount);
this.slideDeck = slideDeck;
}
diff --git a/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java
index 8a63c1cf0b..be67d3d297 100644
--- a/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/NotificationMmsMessageRecord.java
@@ -47,15 +47,15 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
public NotificationMmsMessageRecord(Context context, long id, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId,
- long dateSent, long dateReceived, int receiptCount,
+ long dateSent, long dateReceived, int deliveryReceiptCount,
long threadId, byte[] contentLocation, long messageSize,
long expiry, int status, byte[] transactionId, long mailbox,
- int subscriptionId, SlideDeck slideDeck)
+ int subscriptionId, SlideDeck slideDeck, int readReceiptCount)
{
super(context, id, new Body("", true), conversationRecipient, individualRecipient, recipientDeviceId,
- dateSent, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox,
+ dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
new LinkedList(), new LinkedList(), subscriptionId,
- 0, 0, slideDeck);
+ 0, 0, slideDeck, readReceiptCount);
this.contentLocation = contentLocation;
this.messageSize = messageSize;
diff --git a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
index e92ceef601..8dc52063e9 100644
--- a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
@@ -44,15 +44,16 @@ public class SmsMessageRecord extends MessageRecord {
Recipient individualRecipient,
int recipientDeviceId,
long dateSent, long dateReceived,
- int receiptCount,
+ int deliveryReceiptCount,
long type, long threadId,
int status, List mismatches,
- int subscriptionId, long expiresIn, long expireStarted)
+ int subscriptionId, long expiresIn, long expireStarted,
+ int readReceiptCount)
{
super(context, id, body, recipient, individualRecipient, recipientDeviceId,
- dateSent, dateReceived, threadId, status, receiptCount, type,
+ dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
mismatches, new LinkedList(), subscriptionId,
- expiresIn, expireStarted);
+ expiresIn, expireStarted, readReceiptCount);
}
public long getType() {
diff --git a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
index aac177c28c..8ea53a899a 100644
--- a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
@@ -50,10 +50,11 @@ public class ThreadRecord extends DisplayRecord {
public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri,
@NonNull Recipient recipient, long date, long count, boolean read,
- long threadId, int receiptCount, int status, long snippetType,
- int distributionType, boolean archived, long expiresIn, long lastSeen)
+ long threadId, int deliveryReceiptCount, int status, long snippetType,
+ int distributionType, boolean archived, long expiresIn, long lastSeen,
+ int readReceiptCount)
{
- super(context, body, recipient, date, date, threadId, status, receiptCount, snippetType);
+ super(context, body, recipient, date, date, threadId, status, deliveryReceiptCount, snippetType, readReceiptCount);
this.context = context.getApplicationContext();
this.snippetUri = snippetUri;
this.count = count;
diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
index 2271bffb18..b32f7c02b8 100644
--- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
@@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
-import org.thoughtcrime.securesms.jobs.DeliveryReceiptJob;
import org.thoughtcrime.securesms.jobs.GcmRefreshJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
@@ -29,6 +28,7 @@ import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
+import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.push.SecurityEventListener;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.service.MessageRetrievalService;
@@ -45,7 +45,6 @@ import dagger.Provides;
@Module(complete = false, injects = {CleanPreKeysJob.class,
CreateSignedPreKeyJob.class,
- DeliveryReceiptJob.class,
PushGroupSendJob.class,
PushTextSendJob.class,
PushMediaSendJob.class,
@@ -69,48 +68,56 @@ import dagger.Provides;
MultiDeviceVerifiedUpdateJob.class,
CreateProfileActivity.class,
RetrieveProfileAvatarJob.class,
- MultiDeviceProfileKeyUpdateJob.class})
+ MultiDeviceProfileKeyUpdateJob.class,
+ SendReadReceiptJob.class})
public class SignalCommunicationModule {
- private final Context context;
- private final SignalServiceNetworkAccess networkAccess;
+ private final Context context;
+ private final SignalServiceNetworkAccess networkAccess;
+
+ private SignalServiceAccountManager accountManager;
+ private SignalServiceMessageSender messageSender;
+ private SignalServiceMessageReceiver messageReceiver;
public SignalCommunicationModule(Context context, SignalServiceNetworkAccess networkAccess) {
this.context = context;
this.networkAccess = networkAccess;
}
- @Provides SignalServiceAccountManager provideSignalAccountManager() {
- return new SignalServiceAccountManager(networkAccess.getConfiguration(context),
- TextSecurePreferences.getLocalNumber(context),
- TextSecurePreferences.getPushServerPassword(context),
- BuildConfig.USER_AGENT);
+ @Provides
+ synchronized SignalServiceAccountManager provideSignalAccountManager() {
+ if (this.accountManager == null) {
+ this.accountManager = new SignalServiceAccountManager(networkAccess.getConfiguration(context),
+ new DynamicCredentialsProvider(context),
+ BuildConfig.USER_AGENT);
+ }
+
+ return this.accountManager;
}
@Provides
- SignalMessageSenderFactory provideSignalMessageSenderFactory() {
- return new SignalMessageSenderFactory() {
- @Override
- public SignalServiceMessageSender create() {
- return new SignalServiceMessageSender(networkAccess.getConfiguration(context),
- TextSecurePreferences.getLocalNumber(context),
- TextSecurePreferences.getPushServerPassword(context),
- new SignalProtocolStoreImpl(context),
- BuildConfig.USER_AGENT,
- Optional.fromNullable(MessageRetrievalService.getPipe()),
- Optional.of(new SecurityEventListener(context)));
- }
- };
+ synchronized SignalServiceMessageSender provideSignalMessageSender() {
+ if (this.messageSender == null) {
+ this.messageSender = new SignalServiceMessageSender(networkAccess.getConfiguration(context),
+ new DynamicCredentialsProvider(context),
+ new SignalProtocolStoreImpl(context),
+ BuildConfig.USER_AGENT,
+ Optional.fromNullable(MessageRetrievalService.getPipe()),
+ Optional.of(new SecurityEventListener(context)));
+ }
+
+ return this.messageSender;
}
- @Provides SignalServiceMessageReceiver provideSignalMessageReceiver() {
- return new SignalServiceMessageReceiver(networkAccess.getConfiguration(context),
- new DynamicCredentialsProvider(context),
- BuildConfig.USER_AGENT);
- }
+ @Provides
+ synchronized SignalServiceMessageReceiver provideSignalMessageReceiver() {
+ if (this.messageReceiver == null) {
+ this.messageReceiver = new SignalServiceMessageReceiver(networkAccess.getConfiguration(context),
+ new DynamicCredentialsProvider(context),
+ BuildConfig.USER_AGENT);
+ }
- public static interface SignalMessageSenderFactory {
- public SignalServiceMessageSender create();
+ return this.messageReceiver;
}
private static class DynamicCredentialsProvider implements CredentialsProvider {
diff --git a/src/org/thoughtcrime/securesms/gcm/GcmBroadcastReceiver.java b/src/org/thoughtcrime/securesms/gcm/GcmBroadcastReceiver.java
index ac54bafd44..32c433b4f7 100644
--- a/src/org/thoughtcrime/securesms/gcm/GcmBroadcastReceiver.java
+++ b/src/org/thoughtcrime/securesms/gcm/GcmBroadcastReceiver.java
@@ -30,11 +30,9 @@ public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
return;
}
- String messageData = intent.getStringExtra("message");
String receiptData = intent.getStringExtra("receipt");
- if (!TextUtils.isEmpty(messageData)) handleReceivedMessage(context, messageData);
- else if (!TextUtils.isEmpty(receiptData)) handleReceivedMessage(context, receiptData);
+ if (!TextUtils.isEmpty(receiptData)) handleReceivedMessage(context, receiptData);
else if (intent.hasExtra("notification")) handleReceivedNotification(context);
}
}
diff --git a/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java
deleted file mode 100644
index 066ba56dae..0000000000
--- a/src/org/thoughtcrime/securesms/jobs/DeliveryReceiptJob.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package org.thoughtcrime.securesms.jobs;
-
-import android.content.Context;
-import android.util.Log;
-
-
-import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.whispersystems.jobqueue.JobParameters;
-import org.whispersystems.jobqueue.requirements.NetworkRequirement;
-import org.whispersystems.libsignal.util.guava.Optional;
-import org.whispersystems.signalservice.api.SignalServiceMessageSender;
-import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
-import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
-
-import java.io.IOException;
-
-import javax.inject.Inject;
-
-import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
-
-public class DeliveryReceiptJob extends ContextJob implements InjectableType {
-
- private static final long serialVersionUID = 1L;
-
- private static final String TAG = DeliveryReceiptJob.class.getSimpleName();
-
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
-
- private final String destination;
- private final long timestamp;
- private final String relay;
-
- public DeliveryReceiptJob(Context context, String destination, long timestamp, String relay) {
- super(context, JobParameters.newBuilder()
- .withRequirement(new NetworkRequirement(context))
- .withPersistence()
- .withRetryCount(50)
- .create());
-
- this.destination = destination;
- this.timestamp = timestamp;
- this.relay = relay;
- }
-
- @Override
- public void onAdded() {}
-
- @Override
- public void onRun() throws IOException {
- Log.w("DeliveryReceiptJob", "Sending delivery receipt...");
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
- SignalServiceAddress textSecureAddress = new SignalServiceAddress(destination, Optional.fromNullable(relay));
-
- messageSender.sendDeliveryReceipt(textSecureAddress, timestamp);
- }
-
- @Override
- public void onCanceled() {
- Log.w(TAG, "Failed to send receipt after retry exhausted!");
- }
-
- @Override
- public boolean onShouldRetry(Exception exception) {
- Log.w(TAG, exception);
- if (exception instanceof NonSuccessfulResponseCodeException) return false;
- if (exception instanceof PushNetworkException) return true;
-
- return false;
- }
-}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java
index 4dd86760e7..e4e6bd538d 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java
@@ -7,7 +7,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.BlockedReader;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.jobqueue.JobParameters;
@@ -30,7 +29,7 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
private static final String TAG = MultiDeviceBlockedUpdateJob.class.getSimpleName();
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceBlockedUpdateJob(Context context) {
super(context, JobParameters.newBuilder()
@@ -45,10 +44,9 @@ public class MultiDeviceBlockedUpdateJob extends MasterSecretJob implements Inje
public void onRun(MasterSecret masterSecret)
throws IOException, UntrustedIdentityException
{
- RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
- BlockedReader reader = database.readerForBlocked(database.getBlocked());
- List blocked = new LinkedList<>();
+ RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
+ BlockedReader reader = database.readerForBlocked(database.getBlocked());
+ List blocked = new LinkedList<>();
Recipient recipient;
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
index f3ea85cb1d..6e4bfa9ad8 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
@@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64;
@@ -54,7 +53,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
private static final String TAG = MultiDeviceContactUpdateJob.class.getSimpleName();
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
private final @Nullable String address;
@@ -90,8 +89,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
private void generateSingleContactUpdate(@NonNull Address address)
throws IOException, UntrustedIdentityException, NetworkException
{
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
- File contactDataFile = createTempFile("multidevice-contact-update");
+ File contactDataFile = createTempFile("multidevice-contact-update");
try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
@@ -119,8 +117,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
private void generateFullContactUpdate()
throws IOException, UntrustedIdentityException, NetworkException
{
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
- File contactDataFile = createTempFile("multidevice-contact-update");
+ File contactDataFile = createTempFile("multidevice-contact-update");
try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
index 5a48e76387..8265432fd6 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
@@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@@ -38,8 +37,7 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
private static final long serialVersionUID = 1L;
private static final String TAG = MultiDeviceGroupUpdateJob.class.getSimpleName();
- @Inject
- transient SignalCommunicationModule.SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceGroupUpdateJob(Context context) {
super(context, JobParameters.newBuilder()
@@ -52,9 +50,8 @@ public class MultiDeviceGroupUpdateJob extends MasterSecretJob implements Inject
@Override
public void onRun(MasterSecret masterSecret) throws Exception {
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
- File contactDataFile = createTempFile("multidevice-contact-update");
- GroupDatabase.Reader reader = null;
+ File contactDataFile = createTempFile("multidevice-contact-update");
+ GroupDatabase.Reader reader = null;
GroupDatabase.GroupRecord record;
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java
index b91aab8da7..091805d1df 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java
@@ -7,11 +7,11 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.util.guava.Optional;
+import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
@@ -33,7 +33,7 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
private static final long serialVersionUID = 1L;
private static final String TAG = MultiDeviceProfileKeyUpdateJob.class.getSimpleName();
- @Inject SignalMessageSenderFactory messageSender;
+ @Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceProfileKeyUpdateJob(Context context) {
super(context, JobParameters.newBuilder()
@@ -71,7 +71,7 @@ public class MultiDeviceProfileKeyUpdateJob extends MasterSecretJob implements I
SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));
- messageSender.create().sendMessage(syncMessage);
+ messageSender.sendMessage(syncMessage);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java
index 27155cdd6f..5b9a3acc30 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java
@@ -6,7 +6,6 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobParameters;
@@ -31,8 +30,7 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
private final List messageIds;
- @Inject
- transient SignalCommunicationModule.SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
public MultiDeviceReadUpdateJob(Context context, List messageIds) {
super(context, JobParameters.newBuilder()
@@ -62,7 +60,6 @@ public class MultiDeviceReadUpdateJob extends MasterSecretJob implements Injecta
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
}
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages));
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java
index 141b60e3cd..f6ce372968 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java
@@ -7,7 +7,6 @@ import android.util.Log;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@@ -30,7 +29,7 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
private static final String TAG = MultiDeviceVerifiedUpdateJob.class.getSimpleName();
@Inject
- transient SignalCommunicationModule.SignalMessageSenderFactory messageSenderFactory;
+ transient SignalServiceMessageSender messageSender;
private final String destination;
private final byte[] identityKey;
@@ -65,7 +64,6 @@ public class MultiDeviceVerifiedUpdateJob extends ContextJob implements Injectab
Address canonicalDestination = Address.fromSerialized(destination);
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage));
diff --git a/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java
index 11e41c86ee..512f636235 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushContentReceiveJob.java
@@ -39,7 +39,7 @@ public class PushContentReceiveJob extends PushReceivedJob {
String sessionKey = TextSecurePreferences.getSignalingKey(context);
SignalServiceEnvelope envelope = new SignalServiceEnvelope(data, sessionKey);
- handle(envelope, true);
+ handle(envelope);
} catch (IOException | InvalidVersionException e) {
Log.w(TAG, e);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
index 11166c6050..8ff3c03745 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
@@ -68,6 +68,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
+import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import org.whispersystems.signalservice.api.messages.calls.AnswerMessage;
import org.whispersystems.signalservice.api.messages.calls.BusyMessage;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
@@ -186,6 +187,11 @@ public class PushDecryptJob extends ContextJob {
else if (message.getIceUpdateMessages().isPresent()) handleCallIceUpdateMessage(envelope, message.getIceUpdateMessages().get());
else if (message.getHangupMessage().isPresent()) handleCallHangupMessage(envelope, message.getHangupMessage().get(), smsMessageId);
else if (message.getBusyMessage().isPresent()) handleCallBusyMessage(envelope, message.getBusyMessage().get());
+ } else if (content.getReceiptMessage().isPresent()) {
+ SignalServiceReceiptMessage message = content.getReceiptMessage().get();
+
+ if (message.isReadReceipt()) handleReadReceipt(envelope, message);
+ else if (message.isDeliveryReceipt()) handleDeliveryReceipt(envelope, message);
} else {
Log.w(TAG, "Got unrecognized message...");
}
@@ -824,6 +830,29 @@ public class PushDecryptJob extends ContextJob {
}
}
+ private void handleDeliveryReceipt(@NonNull SignalServiceEnvelope envelope,
+ @NonNull SignalServiceReceiptMessage message)
+ {
+ for (long timestamp : message.getTimestamps()) {
+ Log.w(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
+ DatabaseFactory.getMmsSmsDatabase(context)
+ .incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), timestamp));
+ }
+ }
+
+ private void handleReadReceipt(@NonNull SignalServiceEnvelope envelope,
+ @NonNull SignalServiceReceiptMessage message)
+ {
+ if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
+ for (long timestamp : message.getTimestamps()) {
+ Log.w(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
+
+ DatabaseFactory.getMmsSmsDatabase(context)
+ .incrementReadReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()), timestamp));
+ }
+ }
+ }
+
private Optional insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
index 8735a7fdc6..20e07126bb 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
@@ -43,15 +43,13 @@ import java.util.List;
import javax.inject.Inject;
-import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
-
public class PushGroupSendJob extends PushSendJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = PushGroupSendJob.class.getSimpleName();
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
private final long messageId;
private final long filterRecipientId; // Deprecated
@@ -137,7 +135,6 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
throws IOException, RecipientFormattingException, InvalidNumberException,
EncapsulatedExceptions, UndeliverableMessageException
{
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
String groupId = message.getRecipient().getAddress().toGroupString();
Optional profileKey = getProfileKey(message.getRecipient());
List recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java
index cfb463b497..d57e8d3ca2 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java
@@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@@ -37,7 +36,7 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 0L;
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
private final String source;
private final byte[] groupId;
@@ -59,10 +58,9 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType {
@Override
public void onRun() throws IOException, UntrustedIdentityException {
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
- GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
- Optional record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false));
- SignalServiceAttachment avatar = null;
+ GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
+ Optional record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false));
+ SignalServiceAttachment avatar = null;
if (record == null) {
Log.w(TAG, "No information for group record info request: " + new String(groupId));
diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
index 0f37cf3709..ddd75dab0b 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
@@ -32,15 +32,13 @@ import java.util.List;
import javax.inject.Inject;
-import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
-
public class PushMediaSendJob extends PushSendJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = PushMediaSendJob.class.getSimpleName();
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
private final long messageId;
@@ -107,8 +105,6 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
throw new UndeliverableMessageException("No destination address.");
}
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
-
try {
SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
diff --git a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
index e028e44226..ccb0c9a12c 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
@@ -4,7 +4,6 @@ import android.content.Context;
import android.util.Log;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
@@ -37,7 +36,7 @@ public class PushNotificationReceiveJob extends PushReceivedJob implements Injec
receiver.retrieveMessages(new SignalServiceMessageReceiver.MessageReceivedCallback() {
@Override
public void onMessage(SignalServiceEnvelope envelope) {
- handle(envelope, false);
+ handle(envelope);
}
});
}
diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java
index 51734245d9..cee65de3db 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java
@@ -9,17 +9,12 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.RecipientDatabase;
-import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
-import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.jobqueue.JobParameters;
-import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
-import java.util.LinkedList;
-
public abstract class PushReceivedJob extends ContextJob {
private static final String TAG = PushReceivedJob.class.getSimpleName();
@@ -28,7 +23,7 @@ public abstract class PushReceivedJob extends ContextJob {
super(context, parameters);
}
- public void handle(SignalServiceEnvelope envelope, boolean sendExplicitReceipt) {
+ public void handle(SignalServiceEnvelope envelope) {
Address source = Address.fromExternal(context, envelope.getSource());
Recipient recipient = Recipient.from(context, source, false);
@@ -40,13 +35,13 @@ public abstract class PushReceivedJob extends ContextJob {
if (envelope.isReceipt()) {
handleReceipt(envelope);
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage()) {
- handleMessage(envelope, source, sendExplicitReceipt);
+ handleMessage(envelope, source);
} else {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
}
}
- private void handleMessage(SignalServiceEnvelope envelope, Address source, boolean sendExplicitReceipt) {
+ private void handleMessage(SignalServiceEnvelope envelope, Address source) {
Recipient recipients = Recipient.from(context, source, false);
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
@@ -56,12 +51,6 @@ public abstract class PushReceivedJob extends ContextJob {
} else {
Log.w(TAG, "*** Received blocked push message, ignoring...");
}
-
- if (sendExplicitReceipt) {
- jobManager.add(new DeliveryReceiptJob(context, envelope.getSource(),
- envelope.getTimestamp(),
- envelope.getRelay()));
- }
}
private void handleReceipt(SignalServiceEnvelope envelope) {
diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
index 2ef512e1f3..011d48951f 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
@@ -27,15 +27,13 @@ import java.io.IOException;
import javax.inject.Inject;
-import static org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
-
public class PushTextSendJob extends PushSendJob implements InjectableType {
private static final long serialVersionUID = 1L;
private static final String TAG = PushTextSendJob.class.getSimpleName();
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
private final long messageId;
@@ -101,7 +99,6 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
{
try {
SignalServiceAddress address = getPushAddress(message.getIndividualRecipient().getAddress());
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
Optional profileKey = getProfileKey(message.getIndividualRecipient());
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
diff --git a/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java b/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java
index a2679d4ebd..fe1b568afe 100644
--- a/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java
@@ -4,7 +4,6 @@ import android.content.Context;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
@@ -25,7 +24,7 @@ public class RequestGroupInfoJob extends ContextJob implements InjectableType {
private static final long serialVersionUID = 0L;
- @Inject transient SignalMessageSenderFactory messageSenderFactory;
+ @Inject transient SignalServiceMessageSender messageSender;
private final String source;
private final byte[] groupId;
@@ -46,16 +45,14 @@ public class RequestGroupInfoJob extends ContextJob implements InjectableType {
@Override
public void onRun() throws IOException, UntrustedIdentityException {
- SignalServiceMessageSender messageSender = messageSenderFactory.create();
+ SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO)
+ .withId(groupId)
+ .build();
- SignalServiceGroup group = SignalServiceGroup.newBuilder(Type.REQUEST_INFO)
- .withId(groupId)
- .build();
-
- SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
- .asGroupMessage(group)
- .withTimestamp(System.currentTimeMillis())
- .build();
+ SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
+ .asGroupMessage(group)
+ .withTimestamp(System.currentTimeMillis())
+ .build();
messageSender.sendMessage(new SignalServiceAddress(source), message);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java
new file mode 100644
index 0000000000..abbeb68a13
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java
@@ -0,0 +1,69 @@
+package org.thoughtcrime.securesms.jobs;
+
+
+import android.content.Context;
+import android.util.Log;
+
+import org.thoughtcrime.securesms.database.Address;
+import org.thoughtcrime.securesms.dependencies.InjectableType;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+import org.whispersystems.jobqueue.JobParameters;
+import org.whispersystems.jobqueue.requirements.NetworkRequirement;
+import org.whispersystems.signalservice.api.SignalServiceMessageSender;
+import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
+import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
+import org.whispersystems.signalservice.api.push.SignalServiceAddress;
+import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.inject.Inject;
+
+public class SendReadReceiptJob extends ContextJob implements InjectableType {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final String TAG = SendReadReceiptJob.class.getSimpleName();
+
+ @Inject transient SignalServiceMessageSender messageSender;
+
+ private final String address;
+ private final List messageIds;
+ private final long timestamp;
+
+ public SendReadReceiptJob(Context context, Address address, List messageIds) {
+ super(context, JobParameters.newBuilder()
+ .withRequirement(new NetworkRequirement(context))
+ .withPersistence()
+ .create());
+
+ this.address = address.serialize();
+ this.messageIds = messageIds;
+ this.timestamp = System.currentTimeMillis();
+ }
+
+ @Override
+ public void onAdded() {}
+
+ @Override
+ public void onRun() throws IOException, UntrustedIdentityException {
+ if (!TextSecurePreferences.isReadReceiptsEnabled(context)) return;
+
+ SignalServiceAddress remoteAddress = new SignalServiceAddress(address);
+ SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
+
+ messageSender.sendReceipt(remoteAddress, receiptMessage);
+ }
+
+ @Override
+ public boolean onShouldRetry(Exception e) {
+ if (e instanceof PushNetworkException) return true;
+ return false;
+ }
+
+ @Override
+ public void onCanceled() {
+ Log.w(TAG, "Failed to send read receipts to: " + address);
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
index 1d7d8cdede..bbc3401a12 100644
--- a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
+++ b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
@@ -8,17 +8,23 @@ import android.support.annotation.Nullable;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
+import com.annimon.stream.Collectors;
+import com.annimon.stream.Stream;
+
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
+import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
public class MarkReadReceiver extends MasterSecretBroadcastReceiver {
@@ -72,6 +78,18 @@ public class MarkReadReceiver extends MasterSecretBroadcastReceiver {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new MultiDeviceReadUpdateJob(context, syncMessageIds));
+
+ Map> addressMap = Stream.of(markedReadMessages)
+ .map(MarkedMessageInfo::getSyncMessageId)
+ .collect(Collectors.groupingBy(SyncMessageId::getAddress));
+
+ for (Address address : addressMap.keySet()) {
+ List timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
+
+ ApplicationContext.getInstance(context)
+ .getJobManager()
+ .add(new SendReadReceiptJob(context, address, timestamps));
+ }
}
private static void scheduleDeletion(Context context, ExpirationInfo expirationInfo) {
diff --git a/src/org/thoughtcrime/securesms/service/MessageRetrievalService.java b/src/org/thoughtcrime/securesms/service/MessageRetrievalService.java
index 51698d8df9..fb4d914cf6 100644
--- a/src/org/thoughtcrime/securesms/service/MessageRetrievalService.java
+++ b/src/org/thoughtcrime/securesms/service/MessageRetrievalService.java
@@ -214,7 +214,7 @@ public class MessageRetrievalService extends Service implements InjectableType,
Log.w(TAG, "Retrieved envelope! " + envelope.getSource());
PushContentReceiveJob receiveJob = new PushContentReceiveJob(MessageRetrievalService.this);
- receiveJob.handle(envelope, false);
+ receiveJob.handle(envelope);
decrementPushReceived();
}
diff --git a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java
index 39e4c3ccc0..246083a7d3 100644
--- a/src/org/thoughtcrime/securesms/service/WebRtcCallService.java
+++ b/src/org/thoughtcrime/securesms/service/WebRtcCallService.java
@@ -29,7 +29,6 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -148,10 +147,9 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
private boolean remoteVideoEnabled = false;
private boolean bluetoothAvailable = false;
- @Inject public SignalMessageSenderFactory messageSenderFactory;
+ @Inject public SignalServiceMessageSender messageSender;
@Inject public SignalServiceAccountManager accountManager;
- private SignalServiceMessageSender messageSender;
private PeerConnectionFactory peerConnectionFactory;
private SignalAudioManager audioManager;
private BluetoothStateManager bluetoothStateManager;
@@ -271,7 +269,6 @@ public class WebRtcCallService extends Service implements InjectableType, PeerCo
this.peerConnectionFactory = new PeerConnectionFactory(new PeerConnectionFactoryOptions());
this.audioManager = new SignalAudioManager(this);
this.bluetoothStateManager = new BluetoothStateManager(this, this);
- this.messageSender = messageSenderFactory.create();
this.messageSender.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10));
this.accountManager.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10));
}
diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
index 4efce0b15c..7149b7bee3 100644
--- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
+++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
@@ -113,6 +113,15 @@ public class TextSecurePreferences {
private static final String ALWAYS_RELAY_CALLS_PREF = "pref_turn_only";
private static final String PROFILE_KEY_PREF = "pref_profile_key";
private static final String PROFILE_NAME_PREF = "pref_profile_name";
+ private static final String READ_RECEIPTS_PREF = "pref_read_receipts";
+
+ public static boolean isReadReceiptsEnabled(Context context) {
+ return getBooleanPreference(context, READ_RECEIPTS_PREF, false);
+ }
+
+ public static void setReadReceiptsEnabled(Context context, boolean enabled) {
+ setBooleanPreference(context, READ_RECEIPTS_PREF, enabled);
+ }
public static @Nullable String getProfileKey(Context context) {
return getStringPreference(context, PROFILE_KEY_PREF, null);
diff --git a/test/unitTest/java/org/thoughtcrime/securesms/jobs/DeliveryReceiptJobTest.java b/test/unitTest/java/org/thoughtcrime/securesms/jobs/DeliveryReceiptJobTest.java
deleted file mode 100644
index ead94c85c0..0000000000
--- a/test/unitTest/java/org/thoughtcrime/securesms/jobs/DeliveryReceiptJobTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package org.thoughtcrime.securesms.jobs;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
-import org.thoughtcrime.securesms.BaseUnitTest;
-import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalMessageSenderFactory;
-import org.whispersystems.signalservice.api.SignalServiceMessageSender;
-import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
-import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
-
-import java.io.IOException;
-
-import dagger.Module;
-import dagger.ObjectGraph;
-import dagger.Provides;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class DeliveryReceiptJobTest extends BaseUnitTest {
- @Test
- public void testDelivery() throws IOException {
- SignalServiceMessageSender textSecureMessageSender = mock(SignalServiceMessageSender.class);
- long timestamp = System.currentTimeMillis();
-
- DeliveryReceiptJob deliveryReceiptJob = new DeliveryReceiptJob(context,
- "+14152222222",
- timestamp, "foo");
-
- ObjectGraph objectGraph = ObjectGraph.create(new TestModule(textSecureMessageSender));
- objectGraph.inject(deliveryReceiptJob);
-
- deliveryReceiptJob.onRun();
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(SignalServiceAddress.class);
- verify(textSecureMessageSender).sendDeliveryReceipt(captor.capture(), eq(timestamp));
-
- assertTrue(captor.getValue().getRelay().get().equals("foo"));
- assertTrue(captor.getValue().getNumber().equals("+14152222222"));
- }
-
- @Test
- public void testNetworkError() throws IOException {
- SignalServiceMessageSender textSecureMessageSender = mock(SignalServiceMessageSender.class);
- long timestamp = System.currentTimeMillis();
-
- Mockito.doThrow(new PushNetworkException("network error"))
- .when(textSecureMessageSender)
- .sendDeliveryReceipt(any(SignalServiceAddress.class), eq(timestamp));
-
-
- DeliveryReceiptJob deliveryReceiptJob = new DeliveryReceiptJob(context,
- "+14152222222",
- timestamp, "foo");
-
- ObjectGraph objectGraph = ObjectGraph.create(new TestModule(textSecureMessageSender));
- objectGraph.inject(deliveryReceiptJob);
-
- try {
- deliveryReceiptJob.onRun();
- throw new AssertionError();
- } catch (IOException e) {
- assertTrue(deliveryReceiptJob.onShouldRetry(e));
- }
-
- Mockito.doThrow(new NotFoundException("not found"))
- .when(textSecureMessageSender)
- .sendDeliveryReceipt(any(SignalServiceAddress.class), eq(timestamp));
-
- try {
- deliveryReceiptJob.onRun();
- throw new AssertionError();
- } catch (IOException e) {
- assertFalse(deliveryReceiptJob.onShouldRetry(e));
- }
- }
-
- @Module(injects = DeliveryReceiptJob.class)
- public static class TestModule {
-
- private final SignalServiceMessageSender textSecureMessageSender;
-
- public TestModule(SignalServiceMessageSender textSecureMessageSender) {
- this.textSecureMessageSender = textSecureMessageSender;
- }
-
- @Provides
- SignalMessageSenderFactory provideSignalServiceMessageSenderFactory() {
- return new SignalMessageSenderFactory() {
- @Override
- public SignalServiceMessageSender create() {
- return textSecureMessageSender;
- }
- };
- }
- }
-
-}