Support for synchronizing read state to/from desktop

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2016-02-19 17:07:41 -08:00
parent f5c90df780
commit 08e2221dc0
15 changed files with 369 additions and 38 deletions

View File

@@ -165,4 +165,23 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
cursor.close();
}
}
public static class SyncMessageId {
private final String address;
private final long timetamp;
public SyncMessageId(String address, long timetamp) {
this.address = address;
this.timetamp = timetamp;
}
public String getAddress() {
return address;
}
public long getTimetamp() {
return timetamp;
}
}
}

View File

@@ -190,14 +190,14 @@ public class MmsDatabase extends MessagingDatabase {
}
}
public void incrementDeliveryReceiptCount(String address, long timestamp) {
public void incrementDeliveryReceiptCount(SyncMessageId messageId) {
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
boolean found = false;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX}, DATE_SENT + " = ?", new String[] {String.valueOf(timestamp)}, null, null, null, null);
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
@@ -205,7 +205,7 @@ public class MmsDatabase extends MessagingDatabase {
for (String storedAddress : addresses) {
try {
String ourAddress = canonicalizeNumber(context, address);
String ourAddress = canonicalizeNumber(context, messageId.getAddress());
String theirAddress = canonicalizeNumberOrGroup(context, storedAddress);
if (ourAddress.equals(theirAddress) || GroupUtil.isEncodedGroup(theirAddress)) {
@@ -230,7 +230,7 @@ public class MmsDatabase extends MessagingDatabase {
if (!found) {
try {
earlyReceiptCache.increment(timestamp, canonicalizeNumber(context, address));
earlyReceiptCache.increment(messageId.getTimetamp(), canonicalizeNumber(context, messageId.getAddress()));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
@@ -432,12 +432,72 @@ public class MmsDatabase extends MessagingDatabase {
notifyConversationListeners(threadId);
}
public void setMessagesRead(long threadId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1);
public List<SyncMessageId> setMessagesRead(long threadId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
String where = THREAD_ID + " = ? AND " + READ + " = 0";
String[] selection = new String[]{String.valueOf(threadId)};
List<SyncMessageId> result = new LinkedList<>();
Cursor cursor = null;
database.update(TABLE_NAME, contentValues, THREAD_ID + " = ?", new String[] {threadId + ""});
database.beginTransaction();
try {
cursor = database.query(TABLE_NAME, new String[] {ADDRESS, DATE_SENT, MESSAGE_BOX}, where, selection, null, null, null);
while(cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(2))) {
result.add(new SyncMessageId(cursor.getString(0), cursor.getLong(1)));
}
}
ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1);
database.update(TABLE_NAME, contentValues, where, selection);
database.setTransactionSuccessful();
} finally {
if (cursor != null) cursor.close();
database.endTransaction();
}
return result;
}
public void setTimestampRead(SyncMessageId messageId) {
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) {
List<String> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
for (String storedAddress : addresses) {
try {
String ourAddress = canonicalizeNumber(context, messageId.getAddress());
String theirAddress = canonicalizeNumberOrGroup(context, storedAddress);
if (ourAddress.equals(theirAddress) || GroupUtil.isEncodedGroup(theirAddress)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
database.execSQL("UPDATE " + TABLE_NAME + " SET " + READ + " = 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
notifyConversationListeners(threadId);
}
} catch (InvalidNumberException e) {
Log.w("MmsDatabase", e);
}
}
}
} finally {
if (cursor != null)
cursor.close();
}
}
public void setAllMessagesRead() {

View File

@@ -26,12 +26,15 @@ import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.util.HashSet;
import java.util.Set;
import ws.com.google.android.mms.pdu.PduHeaders;
public class MmsSmsDatabase extends Database {
private static final String TAG = MmsSmsDatabase.class.getSimpleName();
@@ -107,6 +110,17 @@ public class MmsSmsDatabase extends Database {
return queryTables(PROJECTION, selection, order, null);
}
public int getUnreadCount(long threadId) {
String selection = MmsSmsColumns.READ + " = 0 AND " + MmsSmsColumns.THREAD_ID + " = " + threadId;
Cursor cursor = queryTables(PROJECTION, selection, null, null);
try {
return cursor != null ? cursor.getCount() : 0;
} finally {
if (cursor != null) cursor.close();;
}
}
public int getConversationCount(long threadId) {
int count = DatabaseFactory.getSmsDatabase(context).getMessageCountForThread(threadId);
count += DatabaseFactory.getMmsDatabase(context).getMessageCountForThread(threadId);
@@ -114,9 +128,9 @@ public class MmsSmsDatabase extends Database {
return count;
}
public void incrementDeliveryReceiptCount(String address, long timestamp) {
DatabaseFactory.getSmsDatabase(context).incrementDeliveryReceiptCount(address, timestamp);
DatabaseFactory.getMmsDatabase(context).incrementDeliveryReceiptCount(address, timestamp);
public void incrementDeliveryReceiptCount(SyncMessageId syncMessageId) {
DatabaseFactory.getSmsDatabase(context).incrementDeliveryReceiptCount(syncMessageId);
DatabaseFactory.getMmsDatabase(context).incrementDeliveryReceiptCount(syncMessageId);
}
private Cursor queryTables(String[] projection, String selection, String order, String limit) {

View File

@@ -252,20 +252,20 @@ public class SmsDatabase extends MessagingDatabase {
updateTypeBitmask(id, Types.BASE_TYPE_MASK, Types.BASE_SENT_FAILED_TYPE);
}
public void incrementDeliveryReceiptCount(String address, long timestamp) {
public void incrementDeliveryReceiptCount(SyncMessageId messageId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
boolean foundMessage = false;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE},
DATE_SENT + " = ?", new String[] {String.valueOf(timestamp)},
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
null, null, null, null);
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
try {
String theirAddress = canonicalizeNumber(context, address);
String theirAddress = canonicalizeNumber(context, messageId.getAddress());
String ourAddress = canonicalizeNumber(context, cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (ourAddress.equals(theirAddress)) {
@@ -288,7 +288,7 @@ public class SmsDatabase extends MessagingDatabase {
if (!foundMessage) {
try {
earlyReceiptCache.increment(timestamp, canonicalizeNumber(context, address));
earlyReceiptCache.increment(messageId.getTimetamp(), canonicalizeNumber(context, messageId.getAddress()));
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
@@ -300,14 +300,68 @@ public class SmsDatabase extends MessagingDatabase {
}
}
public void setMessagesRead(long threadId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1);
public List<SyncMessageId> setMessagesRead(long threadId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
String where = THREAD_ID + " = ? AND " + READ + " = 0";
String[] selection = new String[]{String.valueOf(threadId)};
List<SyncMessageId> results = new LinkedList<>();
Cursor cursor = null;
database.update(TABLE_NAME, contentValues,
THREAD_ID + " = ? AND " + READ + " = 0",
new String[] {threadId+""});
database.beginTransaction();
try {
cursor = database.query(TABLE_NAME, new String[] {ADDRESS, DATE_SENT, TYPE}, where, selection, null, null, null);
while (cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(2))) {
results.add(new SyncMessageId(cursor.getString(0), cursor.getLong(1)));
}
}
ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1);
database.update(TABLE_NAME, contentValues, where, selection);
database.setTransactionSuccessful();
} finally {
if (cursor != null) cursor.close();
database.endTransaction();
}
return results;
}
public void setTimestampRead(SyncMessageId messageId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, ADDRESS, TYPE},
DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())},
null, null, null, null);
while (cursor.moveToNext()) {
try {
String theirAddress = canonicalizeNumber(context, messageId.getAddress());
String ourAddress = canonicalizeNumber(context, cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (ourAddress.equals(theirAddress)) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
ContentValues contentValues = new ContentValues();
contentValues.put(READ, 1);
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {cursor.getLong(cursor.getColumnIndexOrThrow(ID)) + ""});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
notifyConversationListeners(threadId);
}
} catch (InvalidNumberException e) {
Log.w(TAG, e);
}
}
} finally {
if (cursor != null) cursor.close();
}
}
public void setAllMessagesRead() {

View File

@@ -29,6 +29,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -257,16 +258,22 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners();
}
public void setRead(long threadId) {
public List<SyncMessageId> setRead(long threadId) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(READ, 1);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""});
DatabaseFactory.getSmsDatabase(context).setMessagesRead(threadId);
DatabaseFactory.getMmsDatabase(context).setMessagesRead(threadId);
final List<SyncMessageId> smsRecords = DatabaseFactory.getSmsDatabase(context).setMessagesRead(threadId);
final List<SyncMessageId> mmsRecords = DatabaseFactory.getMmsDatabase(context).setMessagesRead(threadId);
notifyConversationListListeners();
return new LinkedList<SyncMessageId>() {{
addAll(smsRecords);
addAll(mmsRecords);
}};
}
public void setUnread(long threadId) {
@@ -465,6 +472,18 @@ public class ThreadDatabase extends Database {
return null;
}
public void updateReadState(long threadId) {
int unreadCount = DatabaseFactory.getMmsSmsDatabase(context).getUnreadCount(threadId);
ContentValues contentValues = new ContentValues();
contentValues.put(READ, unreadCount == 0);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,ID_WHERE,
new String[] {String.valueOf(threadId)});
notifyConversationListListeners();
}
public boolean update(long threadId, boolean unarchive) {
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
long count = mmsSmsDatabase.getConversationCount(threadId);