Switch MMS groups to use the group database infrastructure

Eliminate the concept of 'Recipients' (plural). There is now just
a 'Recipient', which contains an Address that is either an individual
or a group ID.

MMS groups now exist as part of the group database, just like push
groups.

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2017-08-01 08:56:00 -07:00
parent 81682e0302
commit 375207f073
106 changed files with 1587 additions and 2192 deletions

View File

@@ -96,20 +96,14 @@ public class Address implements Parcelable, Comparable<Address> {
return Util.join(escapedAddresses, delimiter + "");
}
public static Address[] fromParcelable(Parcelable[] parcelables) {
Address[] addresses = new Address[parcelables.length];
for (int i=0;i<parcelables.length;i++) {
addresses[i] = (Address)parcelables[i];
}
return addresses;
}
public boolean isGroup() {
return GroupUtil.isEncodedGroup(address);
}
public boolean isMmsGroup() {
return GroupUtil.isMmsGroup(address);
}
public boolean isEmail() {
return NumberUtil.isValidEmail(address);
}
@@ -204,9 +198,9 @@ public class Address implements Parcelable, Comparable<Address> {
}
public String format(@Nullable String number) {
if (number == null) return "Unknown";
if (number.startsWith("__textsecure_group__!")) return number;
if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
if (number == null) return "Unknown";
if (GroupUtil.isEncodedGroup(number)) return number;
if (ALPHA_PATTERN.matcher(number).find()) return number.trim();
String bareNumber = number.replaceAll("[^0-9+]", "");

View File

@@ -28,8 +28,6 @@ import android.text.TextUtils;
import android.util.Log;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
@@ -41,10 +39,10 @@ import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -56,12 +54,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DatabaseFactory {
@@ -102,7 +100,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_IDENTITY_TIMESTAMP = 35;
private static final int SANIFY_ATTACHMENT_DOWNLOAD = 36;
private static final int NO_MORE_CANONICAL_ADDRESS_DATABASE = 37;
private static final int DATABASE_VERSION = 37;
private static final int NO_MORE_RECIPIENTS_PLURAL = 38;
private static final int DATABASE_VERSION = 38;
private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object();
@@ -117,7 +116,6 @@ public class DatabaseFactory {
private final AttachmentDatabase attachments;
private final MediaDatabase media;
private final ThreadDatabase thread;
private final MmsAddressDatabase mmsAddress;
private final MmsSmsDatabase mmsSmsDatabase;
private final IdentityDatabase identityDatabase;
private final DraftDatabase draftDatabase;
@@ -163,10 +161,6 @@ public class DatabaseFactory {
return getInstance(context).media;
}
public static MmsAddressDatabase getMmsAddressDatabase(Context context) {
return getInstance(context).mmsAddress;
}
public static IdentityDatabase getIdentityDatabase(Context context) {
return getInstance(context).identityDatabase;
}
@@ -199,7 +193,6 @@ public class DatabaseFactory {
this.attachments = new AttachmentDatabase(context, databaseHelper);
this.media = new MediaDatabase(context, databaseHelper);
this.thread = new ThreadDatabase(context, databaseHelper);
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
this.draftDatabase = new DraftDatabase(context, databaseHelper);
@@ -218,7 +211,6 @@ public class DatabaseFactory {
this.mms.reset(databaseHelper);
this.attachments.reset(databaseHelper);
this.thread.reset(databaseHelper);
this.mmsAddress.reset(databaseHelper);
this.mmsSmsDatabase.reset(databaseHelper);
this.identityDatabase.reset(databaseHelper);
this.draftDatabase.reset(databaseHelper);
@@ -533,7 +525,6 @@ public class DatabaseFactory {
db.execSQL(MmsDatabase.CREATE_TABLE);
db.execSQL(AttachmentDatabase.CREATE_TABLE);
db.execSQL(ThreadDatabase.CREATE_TABLE);
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
db.execSQL(IdentityDatabase.CREATE_TABLE);
db.execSQL(DraftDatabase.CREATE_TABLE);
db.execSQL(PushDatabase.CREATE_TABLE);
@@ -544,7 +535,6 @@ public class DatabaseFactory {
executeStatements(db, MmsDatabase.CREATE_INDEXS);
executeStatements(db, AttachmentDatabase.CREATE_INDEXS);
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
executeStatements(db, DraftDatabase.CREATE_INDEXS);
executeStatements(db, GroupDatabase.CREATE_INDEXS);
}
@@ -1209,6 +1199,80 @@ public class DatabaseFactory {
}
if (oldVersion < NO_MORE_RECIPIENTS_PLURAL) {
db.execSQL("ALTER TABLE groups ADD COLUMN mms INTEGER DEFAULT 0");
Cursor cursor = db.query("thread", new String[] {"_id", "recipient_ids"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long threadId = cursor.getLong(0);
String addressListString = cursor.getString(1);
String[] addressList = DelimiterUtil.split(addressListString, ' ');
if (addressList.length == 1) {
ContentValues contentValues = new ContentValues();
contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' '));
db.update("thread", contentValues, "_id = ?", new String[] {String.valueOf(threadId)});
} else {
byte[] groupId = new byte[16];
List<String> members = new LinkedList<>();
new SecureRandom().nextBytes(groupId);
for (String address : addressList) {
members.add(DelimiterUtil.escape(DelimiterUtil.unescape(address, ' '), ','));
}
String encodedGroupId = "__signal_mms_group__!" + Hex.toStringCondensed(groupId);
ContentValues groupValues = new ContentValues();
ContentValues threadValues = new ContentValues();
groupValues.put("group_id", encodedGroupId);
groupValues.put("members", Util.join(members, ","));
groupValues.put("mms", 1);
threadValues.put("recipient_ids", encodedGroupId);
db.insert("groups", null, groupValues);
db.update("thread", threadValues, "_id = ?", new String[] {String.valueOf(threadId)});
db.update("recipient_preferences", threadValues, "recipient_ids = ?", new String[] {addressListString});
}
}
if (cursor != null) cursor.close();
cursor = db.query("recipient_preferences", new String[] {"_id", "recipient_ids"}, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
String addressListString = cursor.getString(1);
String[] addressList = DelimiterUtil.split(addressListString, ' ');
if (addressList.length == 1) {
ContentValues contentValues = new ContentValues();
contentValues.put("recipient_ids", DelimiterUtil.unescape(addressListString, ' '));
db.update("recipient_preferences", contentValues, "_id = ?", new String[] {String.valueOf(id)});
} else {
Log.w(TAG, "Found preferences for MMS thread that appears to be gone: " + addressListString);
db.delete("recipient_preferences", "_id = ?", new String[] {String.valueOf(id)});
}
}
if (cursor != null) cursor.close();
cursor = db.rawQuery("SELECT mms._id, thread.recipient_ids FROM mms, thread WHERE mms.address IS NULL AND mms.thread_id = thread._id", null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
ContentValues contentValues = new ContentValues(1);
contentValues.put("address", cursor.getString(1));
db.update("mms", contentValues, "_id = ?", new String[] {String.valueOf(id)});
}
if (cursor != null) cursor.close();
}
db.setTransactionSuccessful();
db.endTransaction();
}

View File

@@ -14,7 +14,6 @@ import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.Util;
@@ -23,6 +22,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPoin
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -45,6 +45,7 @@ public class GroupDatabase extends Database {
private static final String AVATAR_DIGEST = "avatar_digest";
private static final String TIMESTAMP = "timestamp";
private static final String ACTIVE = "active";
private static final String MMS = "mms";
public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME +
@@ -59,7 +60,8 @@ public class GroupDatabase extends Database {
AVATAR_RELAY + " TEXT, " +
TIMESTAMP + " INTEGER, " +
ACTIVE + " INTEGER DEFAULT 1, " +
AVATAR_DIGEST + " BLOB);";
AVATAR_DIGEST + " BLOB, " +
MMS + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");",
@@ -69,10 +71,10 @@ public class GroupDatabase extends Database {
super(context, databaseHelper);
}
public @Nullable GroupRecord getGroup(byte[] groupId) {
public @Nullable GroupRecord getGroup(String groupId) {
@SuppressLint("Recycle")
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)},
new String[] {groupId},
null, null, null);
Reader reader = new Reader(cursor);
@@ -82,7 +84,7 @@ public class GroupDatabase extends Database {
return record;
}
public boolean isUnknownGroup(byte[] groupId) {
public boolean isUnknownGroup(String groupId) {
return getGroup(groupId) == null;
}
@@ -94,12 +96,32 @@ public class GroupDatabase extends Database {
return new Reader(cursor);
}
public String getOrCreateGroupForMembers(List<Address> members, boolean mms) {
Collections.sort(members);
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID},
MEMBERS + " = ? AND " + MMS + " = ?",
new String[] {Address.toSerializedList(members, ','), mms ? "1" : "0"},
null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
return cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID));
} else {
String groupId = GroupUtil.getEncodedId(allocateGroupId(), mms);
create(groupId, null, members, null, null);
return groupId;
}
} finally {
if (cursor != null) cursor.close();
}
}
public Reader getGroups() {
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null);
return new Reader(cursor);
}
public @NonNull Recipients getGroupMembers(byte[] groupId, boolean includeSelf) {
public @NonNull List<Recipient> getGroupMembers(String groupId, boolean includeSelf) {
List<Address> members = getCurrentMembers(groupId);
List<Recipient> recipients = new LinkedList<>();
@@ -110,14 +132,16 @@ public class GroupDatabase extends Database {
recipients.add(RecipientFactory.getRecipientFor(context, member, false));
}
return RecipientFactory.getRecipientsFor(context, recipients, false);
return recipients;
}
public void create(byte[] groupId, String title, List<Address> members,
SignalServiceAttachmentPointer avatar, String relay)
public void create(@NonNull String groupId, @Nullable String title, @NonNull List<Address> members,
@Nullable SignalServiceAttachmentPointer avatar, @Nullable String relay)
{
Collections.sort(members);
ContentValues contentValues = new ContentValues();
contentValues.put(GROUP_ID, GroupUtil.getEncodedId(groupId));
contentValues.put(GROUP_ID, groupId);
contentValues.put(TITLE, title);
contentValues.put(MEMBERS, Address.toSerializedList(members, ','));
@@ -131,13 +155,14 @@ public class GroupDatabase extends Database {
contentValues.put(AVATAR_RELAY, relay);
contentValues.put(TIMESTAMP, System.currentTimeMillis());
contentValues.put(ACTIVE, 1);
contentValues.put(MMS, GroupUtil.isMmsGroup(groupId));
databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
RecipientFactory.clearCache(context);
notifyConversationListListeners();
}
public void update(byte[] groupId, String title, SignalServiceAttachmentPointer avatar) {
public void update(String groupId, String title, SignalServiceAttachmentPointer avatar) {
ContentValues contentValues = new ContentValues();
if (title != null) contentValues.put(TITLE, title);
@@ -150,65 +175,67 @@ public class GroupDatabase extends Database {
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)});
new String[] {groupId});
RecipientFactory.clearCache(context);
notifyDatabaseListeners();
notifyConversationListListeners();
}
public void updateTitle(byte[] groupId, String title) {
public void updateTitle(String groupId, String title) {
ContentValues contentValues = new ContentValues();
contentValues.put(TITLE, title);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)});
new String[] {groupId});
RecipientFactory.clearCache(context);
notifyDatabaseListeners();
}
public void updateAvatar(byte[] groupId, Bitmap avatar) {
public void updateAvatar(String groupId, Bitmap avatar) {
updateAvatar(groupId, BitmapUtil.toByteArray(avatar));
}
public void updateAvatar(byte[] groupId, byte[] avatar) {
public void updateAvatar(String groupId, byte[] avatar) {
ContentValues contentValues = new ContentValues();
contentValues.put(AVATAR, avatar);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(groupId)});
new String[] {groupId});
RecipientFactory.clearCache(context);
notifyDatabaseListeners();
}
public void updateMembers(byte[] id, List<Address> members) {
public void updateMembers(String groupId, List<Address> members) {
Collections.sort(members);
ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.toSerializedList(members, ','));
contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(id)});
new String[] {groupId});
}
public void remove(byte[] id, Address source) {
List<Address> currentMembers = getCurrentMembers(id);
public void remove(String groupId, Address source) {
List<Address> currentMembers = getCurrentMembers(groupId);
currentMembers.remove(source);
ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.toSerializedList(currentMembers, ','));
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(id)});
new String[] {groupId});
}
private List<Address> getCurrentMembers(byte[] id) {
private List<Address> getCurrentMembers(String groupId) {
Cursor cursor = null;
try {
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS},
GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(id)},
new String[] {groupId},
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
@@ -223,16 +250,16 @@ public class GroupDatabase extends Database {
}
}
public boolean isActive(byte[] id) {
GroupRecord record = getGroup(id);
public boolean isActive(String groupId) {
GroupRecord record = getGroup(groupId);
return record != null && record.isActive();
}
public void setActive(byte[] id, boolean active) {
public void setActive(String groupId, boolean active) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ACTIVE, active ? 1 : 0);
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {GroupUtil.getEncodedId(id)});
database.update(TABLE_NAME, values, GROUP_ID + " = ?", new String[] {groupId});
}
@@ -273,7 +300,8 @@ public class GroupDatabase extends Database {
cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_CONTENT_TYPE)),
cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_RELAY)),
cursor.getInt(cursor.getColumnIndexOrThrow(ACTIVE)) == 1,
cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_DIGEST)));
cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_DIGEST)),
cursor.getInt(cursor.getColumnIndexOrThrow(MMS)) == 1);
}
public void close() {
@@ -294,10 +322,11 @@ public class GroupDatabase extends Database {
private final String avatarContentType;
private final String relay;
private final boolean active;
private final boolean mms;
public GroupRecord(String id, String title, String members, byte[] avatar,
long avatarId, byte[] avatarKey, String avatarContentType,
String relay, boolean active, byte[] avatarDigest)
String relay, boolean active, byte[] avatarDigest, boolean mms)
{
this.id = id;
this.title = title;
@@ -309,6 +338,7 @@ public class GroupDatabase extends Database {
this.avatarContentType = avatarContentType;
this.relay = relay;
this.active = active;
this.mms = mms;
}
public byte[] getId() {
@@ -358,5 +388,9 @@ public class GroupDatabase extends Database {
public boolean isActive() {
return active;
}
public boolean isMms() {
return mms;
}
}
}

View File

@@ -1,150 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import com.google.android.mms.pdu_alt.PduHeaders;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList;
import java.util.List;
public class MmsAddressDatabase extends Database {
private static final String TAG = MmsAddressDatabase.class.getSimpleName();
private static final String TABLE_NAME = "mms_addresses";
private static final String ID = "_id";
private static final String MMS_ID = "mms_id";
private static final String TYPE = "type";
private static final String ADDRESS = "address";
private static final String ADDRESS_CHARSET = "address_charset";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + TYPE + " INTEGER, " + ADDRESS + " TEXT, " +
ADDRESS_CHARSET + " INTEGER);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_addresses_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
};
public MmsAddressDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
}
private void insertAddress(long messageId, int type, @NonNull Address value) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(MMS_ID, messageId);
contentValues.put(TYPE, type);
contentValues.put(ADDRESS, value.serialize());
contentValues.put(ADDRESS_CHARSET, "UTF-8");
database.insert(TABLE_NAME, null, contentValues);
}
private void insertAddress(long messageId, int type, @NonNull List<Address> addresses) {
for (Address address : addresses) {
insertAddress(messageId, type, address);
}
}
public void insertAddressesForId(long messageId, MmsAddresses addresses) {
if (addresses.getFrom() != null) {
insertAddress(messageId, PduHeaders.FROM, addresses.getFrom());
}
insertAddress(messageId, PduHeaders.TO, addresses.getTo());
insertAddress(messageId, PduHeaders.CC, addresses.getCc());
insertAddress(messageId, PduHeaders.BCC, addresses.getBcc());
}
public MmsAddresses getAddressesForId(long messageId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
Address from = null;
List<Address> to = new LinkedList<>();
List<Address> cc = new LinkedList<>();
List<Address> bcc = new LinkedList<>();
try {
cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {messageId+""}, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE));
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (type == PduHeaders.FROM) from = address;
if (type == PduHeaders.TO) to.add(address);
if (type == PduHeaders.CC) cc.add(address);
if (type == PduHeaders.BCC) bcc.add(address);
}
} finally {
if (cursor != null)
cursor.close();
}
return new MmsAddresses(from, to, cc, bcc);
}
public List<Address> getAddressesListForId(long messageId) {
List<Address> results = new LinkedList<>();
MmsAddresses addresses = getAddressesForId(messageId);
if (addresses.getFrom() != null) {
results.add(addresses.getFrom());
}
results.addAll(addresses.getTo());
results.addAll(addresses.getCc());
results.addAll(addresses.getBcc());
return results;
}
public Recipients getRecipientsForId(long messageId) {
List<Address> addresses = getAddressesListForId(messageId);
List<Recipient> results = new LinkedList<>();
for (Address address : addresses) {
if (!PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.equals(address.serialize())) {
results.add(RecipientFactory.getRecipientFor(context, address, false));
}
}
return RecipientFactory.getRecipientsFor(context, results, false);
}
public void deleteAddressesForId(long messageId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {messageId+""});
}
public void deleteAllAddresses() {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, null, null);
}
}

View File

@@ -1,56 +0,0 @@
package org.thoughtcrime.securesms.database;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.LinkedList;
import java.util.List;
public class MmsAddresses {
private final @Nullable Address from;
private final @NonNull List<Address> to;
private final @NonNull List<Address> cc;
private final @NonNull List<Address> bcc;
public MmsAddresses(@Nullable Address from, @NonNull List<Address> to,
@NonNull List<Address> cc, @NonNull List<Address> bcc)
{
this.from = from;
this.to = to;
this.cc = cc;
this.bcc = bcc;
}
@NonNull
public List<Address> getTo() {
return to;
}
@NonNull
public List<Address> getCc() {
return cc;
}
@NonNull
public List<Address> getBcc() {
return bcc;
}
@Nullable
public Address getFrom() {
return from;
}
public static MmsAddresses forTo(@NonNull List<Address> to) {
return new MmsAddresses(null, to, new LinkedList<Address>(), new LinkedList<Address>());
}
public static MmsAddresses forBcc(@NonNull List<Address> bcc) {
return new MmsAddresses(null, new LinkedList<Address>(), new LinkedList<Address>(), bcc);
}
public static MmsAddresses forFrom(@NonNull Address from) {
return new MmsAddresses(from, new LinkedList<Address>(), new LinkedList<Address>(), new LinkedList<Address>());
}
}

View File

@@ -30,7 +30,6 @@ import android.util.Pair;
import com.google.android.mms.pdu_alt.NotificationInd;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
@@ -57,12 +56,10 @@ import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.InvalidMessageException;
@@ -195,34 +192,30 @@ public class MmsDatabase extends MessagingDatabase {
}
public void incrementDeliveryReceiptCount(SyncMessageId messageId) {
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
boolean found = false;
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(messageId.getTimetamp())}, null, null, null, null);
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
List<Address> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress();
for (Address theirAddress : addresses) {
Address ourAddress = messageId.getAddress();
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
found = true;
found = true;
database.execSQL("UPDATE " + TABLE_NAME + " SET " +
RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
database.execSQL("UPDATE " + TABLE_NAME + " SET " +
RECEIPT_COUNT + " = " + RECEIPT_COUNT + " + 1 WHERE " + ID + " = ?",
new String[] {String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
}
DatabaseFactory.getThreadDatabase(context).update(threadId, false);
notifyConversationListeners(threadId);
}
}
}
@@ -257,66 +250,20 @@ public class MmsDatabase extends MessagingDatabase {
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException, MmsException {
if (retrieved.getGroupId() != null) {
Recipients groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {retrieved.getGroupId()}, true);
Recipient groupRecipients = RecipientFactory.getRecipientFor(context, retrieved.getGroupId(), true);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
}
String localNumber;
Set<Address> group = new HashSet<>();
if (retrieved.getAddresses().getFrom() == null) {
throw new MmsException("FROM value in PduHeaders did not exist.");
}
group.add(retrieved.getAddresses().getFrom());
if (TextSecurePreferences.isPushRegistered(context)) {
localNumber = TextSecurePreferences.getLocalNumber(context);
} else {
localNumber = ServiceUtil.getTelephonyManager(context).getLine1Number();
Recipient sender = RecipientFactory.getRecipientFor(context, retrieved.getFrom(), true);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(sender);
}
for (Address cc : retrieved.getAddresses().getCc()) {
PhoneNumberUtil.MatchType match;
if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH;
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, cc.serialize());
if (match == PhoneNumberUtil.MatchType.NO_MATCH ||
match == PhoneNumberUtil.MatchType.NOT_A_NUMBER)
{
group.add(cc);
}
}
if (retrieved.getAddresses().getTo().size() > 1) {
for (Address to : retrieved.getAddresses().getTo()) {
PhoneNumberUtil.MatchType match;
if (localNumber == null) match = PhoneNumberUtil.MatchType.NO_MATCH;
else match = PhoneNumberUtil.getInstance().isNumberMatch(localNumber, to.serialize());
if (match == PhoneNumberUtil.MatchType.NO_MATCH ||
match == PhoneNumberUtil.MatchType.NOT_A_NUMBER)
{
group.add(to);
}
}
}
Recipients recipients = RecipientFactory.getRecipientsFor(context, group.toArray(new Address[0]), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
}
private long getThreadIdFor(@NonNull NotificationInd notification) {
String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null
? Util.toIsoString(notification.getFrom().getTextString())
: "";
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, fromString)}, false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromExternal(context, fromString), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
}
private Cursor rawQuery(@NonNull String where, @Nullable String[] arguments) {
@@ -489,39 +436,35 @@ public class MmsDatabase extends MessagingDatabase {
}
public List<Pair<Long, Long>> setTimestampRead(SyncMessageId messageId, long expireStarted) {
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
List<Pair<Long, Long>> expiring = new LinkedList<>();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) {
List<Address> addresses = addressDatabase.getAddressesListForId(cursor.getLong(cursor.getColumnIndexOrThrow(ID)));
Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress();
for (Address theirAddress : addresses) {
Address ourAddress = messageId.getAddress();
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
ContentValues values = new ContentValues();
values.put(READ, 1);
ContentValues values = new ContentValues();
values.put(READ, 1);
if (expiresIn > 0) {
values.put(EXPIRE_STARTED, expireStarted);
expiring.add(new Pair<>(id, expiresIn));
}
database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
if (expiresIn > 0) {
values.put(EXPIRE_STARTED, expireStarted);
expiring.add(new Pair<>(id, expiresIn));
}
database.update(TABLE_NAME, values, ID_WHERE, new String[]{String.valueOf(id)});
DatabaseFactory.getThreadDatabase(context).updateReadState(threadId);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
notifyConversationListeners(threadId);
}
}
} finally {
@@ -592,7 +535,6 @@ public class MmsDatabase extends MessagingDatabase {
public OutgoingMediaMessage getOutgoingMessage(MasterSecret masterSecret, long messageId)
throws MmsException, NoSuchMessageException
{
MmsAddressDatabase addr = DatabaseFactory.getMmsAddressDatabase(context);
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
Cursor cursor = null;
@@ -600,31 +542,27 @@ public class MmsDatabase extends MessagingDatabase {
cursor = rawQuery(RAW_ID_WHERE, new String[] {String.valueOf(messageId)});
if (cursor != null && cursor.moveToNext()) {
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
List<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(masterSecret, messageId));
MmsAddresses addresses = addr.getAddressesForId(messageId);
List<Address> destinations = new LinkedList<>();
String body = getDecryptedBody(masterSecret, messageText, outboxType);
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
List<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(masterSecret, messageId));
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
String body = getDecryptedBody(masterSecret, messageText, outboxType);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId);
destinations.addAll(addresses.getBcc());
destinations.addAll(addresses.getCc());
destinations.addAll(addresses.getTo());
Recipients recipients = RecipientFactory.getRecipientsFor(context, destinations.toArray(new Address[0]), false);
Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(address), false);
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
return new OutgoingGroupMediaMessage(recipients, body, attachments, timestamp, 0);
return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0);
} else if (Types.isExpirationTimerUpdate(outboxType)) {
return new OutgoingExpirationUpdateMessage(recipients, timestamp, expiresIn);
return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
}
OutgoingMediaMessage message = new OutgoingMediaMessage(recipients, body, attachments, timestamp, subscriptionId, expiresIn,
!addresses.getBcc().isEmpty() ? ThreadDatabase.DistributionTypes.BROADCAST :
ThreadDatabase.DistributionTypes.DEFAULT);
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType);
if (Types.isSecureType(outboxType)) {
return new OutgoingSecureMediaMessage(message);
}
@@ -645,7 +583,7 @@ public class MmsDatabase extends MessagingDatabase {
try {
OutgoingMediaMessage request = getOutgoingMessage(masterSecret, messageId);
ContentValues contentValues = new ContentValues();
contentValues.put(ADDRESS, request.getRecipients().getPrimaryRecipient().getAddress().serialize());
contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize());
contentValues.put(DATE_SENT, request.getSentTimeMillis());
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.ENCRYPTION_SYMMETRIC_BIT);
contentValues.put(THREAD_ID, getThreadIdForMessage(messageId));
@@ -674,7 +612,6 @@ public class MmsDatabase extends MessagingDatabase {
}
return insertMediaMessage(new MasterSecretUnion(masterSecret),
MmsAddresses.forTo(request.getRecipients().getAddressesList()),
request.getBody(),
attachments,
contentValues,
@@ -703,7 +640,7 @@ public class MmsDatabase extends MessagingDatabase {
ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, retrieved.getSentTimeMillis());
contentValues.put(ADDRESS, retrieved.getAddresses().getFrom().serialize());
contentValues.put(ADDRESS, retrieved.getFrom().serialize());
contentValues.put(MESSAGE_BOX, mailbox);
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
@@ -725,9 +662,7 @@ public class MmsDatabase extends MessagingDatabase {
return Optional.absent();
}
long messageId = insertMediaMessage(masterSecret, retrieved.getAddresses(),
retrieved.getBody(), retrieved.getAttachments(),
contentValues, null);
long messageId = insertMediaMessage(masterSecret, retrieved.getBody(), retrieved.getAttachments(), contentValues, null);
if (!Types.isExpirationTimerUpdate(mailbox)) {
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
@@ -789,8 +724,7 @@ public class MmsDatabase extends MessagingDatabase {
}
public Pair<Long, Long> insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long threadId = getThreadIdFor(notification);
ContentValues contentValues = new ContentValues();
ContentValuesBuilder contentBuilder = new ContentValuesBuilder(contentValues);
@@ -806,9 +740,7 @@ public class MmsDatabase extends MessagingDatabase {
contentBuilder.add(MESSAGE_TYPE, notification.getMessageType());
if (notification.getFrom() != null) {
contentBuilder.add(ADDRESS, notification.getFrom().getTextString());
} else {
contentBuilder.add(ADDRESS, null);
contentValues.put(ADDRESS, Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())).serialize());
}
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE);
@@ -823,10 +755,6 @@ public class MmsDatabase extends MessagingDatabase {
long messageId = db.insert(TABLE_NAME, null, contentValues);
if (notification.getFrom() != null) {
addressDatabase.insertAddressesForId(messageId, MmsAddresses.forFrom(Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString()))));
}
return new Pair<>(messageId, threadId);
}
@@ -864,18 +792,6 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
}
List<Address> recipientNumbers = message.getRecipients().getAddressesList();
MmsAddresses addresses;
if (!message.getRecipients().isSingleRecipient() &&
message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST)
{
addresses = MmsAddresses.forBcc(recipientNumbers);
} else {
addresses = MmsAddresses.forTo(recipientNumbers);
}
ContentValues contentValues = new ContentValues();
contentValues.put(DATE_SENT, message.getSentTimeMillis());
contentValues.put(MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
@@ -886,15 +802,10 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
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()));
if (message.getRecipients().isSingleRecipient()) {
contentValues.put(RECEIPT_COUNT, earlyReceiptCache.remove(message.getSentTimeMillis(), message.getRecipients().getPrimaryRecipient().getAddress()));
}
contentValues.remove(ADDRESS);
long messageId = insertMediaMessage(masterSecret, addresses, message.getBody(),
message.getAttachments(), contentValues, insertListener);
long messageId = insertMediaMessage(masterSecret, message.getBody(), message.getAttachments(), contentValues, insertListener);
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
jobManager.add(new TrimThreadJob(context, threadId));
@@ -928,7 +839,6 @@ public class MmsDatabase extends MessagingDatabase {
}
private long insertMediaMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull MmsAddresses addresses,
@Nullable String body,
@NonNull List<Attachment> attachments,
@NonNull ContentValues contentValues,
@@ -937,7 +847,6 @@ public class MmsDatabase extends MessagingDatabase {
{
SQLiteDatabase db = databaseHelper.getWritableDatabase();
AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context);
MmsAddressDatabase addressDatabase = DatabaseFactory.getMmsAddressDatabase(context);
if (Types.isSymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)) ||
Types.isAsymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)))
@@ -953,7 +862,6 @@ public class MmsDatabase extends MessagingDatabase {
try {
long messageId = db.insert(TABLE_NAME, null, contentValues);
addressDatabase.insertAddressesForId(messageId, addresses);
partsDatabase.insertAttachmentsForMessage(masterSecret, messageId, attachments);
db.setTransactionSuccessful();
@@ -972,10 +880,8 @@ public class MmsDatabase extends MessagingDatabase {
public boolean delete(long messageId) {
long threadId = getThreadIdForMessage(messageId);
MmsAddressDatabase addrDatabase = DatabaseFactory.getMmsAddressDatabase(context);
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
attachmentDatabase.deleteAttachmentsForMessage(messageId);
addrDatabase.deleteAddressesForId(messageId);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""});
@@ -993,7 +899,7 @@ public class MmsDatabase extends MessagingDatabase {
private boolean isDuplicate(IncomingMediaMessage message, long threadId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, null, DATE_SENT + " = ? AND " + ADDRESS + " = ? AND " + THREAD_ID + " = ?",
new String[]{String.valueOf(message.getSentTimeMillis()), message.getAddresses().getFrom().serialize(), String.valueOf(threadId)},
new String[]{String.valueOf(message.getSentTimeMillis()), message.getFrom().serialize(), String.valueOf(threadId)},
null, null, null, "1");
try {
@@ -1058,7 +964,6 @@ public class MmsDatabase extends MessagingDatabase {
public void deleteAllThreads() {
DatabaseFactory.getAttachmentDatabase(context).deleteAllAttachments();
DatabaseFactory.getMmsAddressDatabase(context).deleteAllAddresses();
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, null, null);
@@ -1139,8 +1044,7 @@ public class MmsDatabase extends MessagingDatabase {
public MessageRecord getCurrent() {
SlideDeck slideDeck = new SlideDeck(context, message.getAttachments());
return new MediaMmsMessageRecord(context, id, message.getRecipients(),
message.getRecipients().getPrimaryRecipient(),
return new MediaMmsMessageRecord(context, id, message.getRecipient(), message.getRecipient(),
1, System.currentTimeMillis(), System.currentTimeMillis(),
0, threadId, new DisplayRecord.Body(message.getBody(), true),
slideDeck, slideDeck.getSlides().size(),
@@ -1192,7 +1096,7 @@ public class MmsDatabase extends MessagingDatabase {
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));
Recipients recipients = getRecipientsFor(address);
Recipient recipient = getRecipientFor(address);
String contentLocation = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.CONTENT_LOCATION));
String transactionId = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.TRANSACTION_ID));
@@ -1214,7 +1118,7 @@ public class MmsDatabase extends MessagingDatabase {
SlideDeck slideDeck = new SlideDeck(context, new MmsNotificationAttachment(status, messageSize));
return new NotificationMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(),
return new NotificationMmsMessageRecord(context, id, recipient, recipient,
addressDeviceId, dateSent, dateReceived, receiptCount, threadId,
contentLocationBytes, messageSize, expiry, status,
transactionIdBytes, mailbox, subscriptionId, slideDeck);
@@ -1237,18 +1141,18 @@ public class MmsDatabase extends MessagingDatabase {
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
Recipients recipients = getRecipientsFor(address);
Recipient recipient = getRecipientFor(address);
List<IdentityKeyMismatch> mismatches = getMismatchedIdentities(mismatchDocument);
List<NetworkFailure> networkFailures = getFailures(networkDocument);
SlideDeck slideDeck = getSlideDeck(cursor);
return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(),
return new MediaMmsMessageRecord(context, id, recipient, recipient,
addressDeviceId, dateSent, dateReceived, receiptCount,
threadId, body, slideDeck, partCount, box, mismatches,
networkFailures, subscriptionId, expiresIn, expireStarted);
}
private Recipients getRecipientsFor(String serialized) {
private Recipient getRecipientFor(String serialized) {
Address address;
if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) {
@@ -1257,7 +1161,7 @@ public class MmsDatabase extends MessagingDatabase {
address = Address.fromSerialized(serialized);
}
return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true);
return RecipientFactory.getRecipientFor(context, address, true);
}
private List<IdentityKeyMismatch> getMismatchedIdentities(String document) {

View File

@@ -8,8 +8,8 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -34,8 +34,8 @@ public class PlaintextBackupImporter {
XmlBackup.XmlBackupItem item;
while ((item = backup.getNext()) != null) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {Address.fromExternal(context, item.getAddress())}, false);
long threadId = threads.getThreadIdFor(recipients);
Recipient recipient = RecipientFactory.getRecipientFor(context, Address.fromExternal(context, item.getAddress()), false);
long threadId = threads.getThreadIdFor(recipient);
SQLiteStatement statement = db.createInsertStatement(transaction);
if (item.getAddress() == null || item.getAddress().equals("null"))

View File

@@ -12,13 +12,10 @@ import android.util.Log;
import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Arrays;
import java.util.List;
public class RecipientPreferenceDatabase extends Database {
private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName();
@@ -26,7 +23,7 @@ public class RecipientPreferenceDatabase extends Database {
private static final String TABLE_NAME = "recipient_preferences";
private static final String ID = "_id";
private static final String ADDRESSES = "recipient_ids";
private static final String ADDRESS = "recipient_ids";
private static final String BLOCK = "block";
private static final String NOTIFICATION = "notification";
private static final String VIBRATE = "vibrate";
@@ -57,7 +54,7 @@ public class RecipientPreferenceDatabase extends Database {
public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
ADDRESSES + " TEXT UNIQUE, " +
ADDRESS + " TEXT UNIQUE, " +
BLOCK + " INTEGER DEFAULT 0," +
NOTIFICATION + " TEXT DEFAULT NULL, " +
VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
@@ -74,7 +71,7 @@ public class RecipientPreferenceDatabase extends Database {
public Cursor getBlocked() {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESSES}, BLOCK + " = 1",
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, BLOCK + " = 1",
null, null, null, null, null);
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(RECIPIENT_PREFERENCES_URI));
@@ -85,14 +82,13 @@ public class RecipientPreferenceDatabase extends Database {
return new BlockedReader(context, cursor);
}
public Optional<RecipientsPreferences> getRecipientsPreferences(@NonNull Address[] addresses) {
public Optional<RecipientsPreferences> getRecipientsPreferences(@NonNull Address address) {
SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = database.query(TABLE_NAME, null, ADDRESSES + " = ?",
new String[] {Address.toSerializedList(Arrays.asList(addresses), ' ')},
null, null, null);
cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null);
if (cursor != null && cursor.moveToNext()) {
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
@@ -128,69 +124,68 @@ public class RecipientPreferenceDatabase extends Database {
}
}
public void setColor(Recipients recipients, MaterialColor color) {
public void setColor(Recipient recipient, MaterialColor color) {
ContentValues values = new ContentValues();
values.put(COLOR, color.serialize());
updateOrInsert(recipients, values);
updateOrInsert(recipient, values);
}
public void setDefaultSubscriptionId(@NonNull Recipients recipients, int defaultSubscriptionId) {
public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) {
ContentValues values = new ContentValues();
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
updateOrInsert(recipients, values);
EventBus.getDefault().post(new RecipientPreferenceEvent(recipients));
updateOrInsert(recipient, values);
EventBus.getDefault().post(new RecipientPreferenceEvent(recipient));
}
public void setBlocked(Recipients recipients, boolean blocked) {
public void setBlocked(Recipient recipient, boolean blocked) {
ContentValues values = new ContentValues();
values.put(BLOCK, blocked ? 1 : 0);
updateOrInsert(recipients, values);
updateOrInsert(recipient, values);
}
public void setRingtone(Recipients recipients, @Nullable Uri notification) {
public void setRingtone(Recipient recipient, @Nullable Uri notification) {
ContentValues values = new ContentValues();
values.put(NOTIFICATION, notification == null ? null : notification.toString());
updateOrInsert(recipients, values);
updateOrInsert(recipient, values);
}
public void setVibrate(Recipients recipients, @NonNull VibrateState enabled) {
public void setVibrate(Recipient recipient, @NonNull VibrateState enabled) {
ContentValues values = new ContentValues();
values.put(VIBRATE, enabled.getId());
updateOrInsert(recipients, values);
updateOrInsert(recipient, values);
}
public void setMuted(Recipients recipients, long until) {
public void setMuted(Recipient recipient, long until) {
Log.w(TAG, "Setting muted until: " + until);
ContentValues values = new ContentValues();
values.put(MUTE_UNTIL, until);
updateOrInsert(recipients, values);
updateOrInsert(recipient, values);
}
public void setSeenInviteReminder(Recipients recipients, boolean seen) {
public void setSeenInviteReminder(Recipient recipient, boolean seen) {
ContentValues values = new ContentValues(1);
values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0);
updateOrInsert(recipients, values);
updateOrInsert(recipient, values);
}
public void setExpireMessages(Recipients recipients, int expiration) {
recipients.setExpireMessages(expiration);
public void setExpireMessages(Recipient recipient, int expiration) {
recipient.setExpireMessages(expiration);
ContentValues values = new ContentValues(1);
values.put(EXPIRE_MESSAGES, expiration);
updateOrInsert(recipients, values);
updateOrInsert(recipient, values);
}
private void updateOrInsert(Recipients recipients, ContentValues contentValues) {
private void updateOrInsert(Recipient recipient, ContentValues contentValues) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.beginTransaction();
List<Address> addresses = recipients.getAddressesList();
String serializedAddresses = Address.toSerializedList(addresses, ' ');
int updated = database.update(TABLE_NAME, contentValues, ADDRESSES + " = ?", new String[]{serializedAddresses});
int updated = database.update(TABLE_NAME, contentValues, ADDRESS + " = ?",
new String[] {recipient.getAddress().serialize()});
if (updated < 1) {
contentValues.put(ADDRESSES, serializedAddresses);
contentValues.put(ADDRESS, recipient.getAddress().serialize());
database.insert(TABLE_NAME, null, contentValues);
}
@@ -271,14 +266,12 @@ public class RecipientPreferenceDatabase extends Database {
this.cursor = cursor;
}
public @NonNull Recipients getCurrent() {
String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESSES));
List<Address> addressList = Address.fromSerializedList(serialized, ' ');
return RecipientFactory.getRecipientsFor(context, addressList.toArray(new Address[0]), false);
public @NonNull Recipient getCurrent() {
String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
return RecipientFactory.getRecipientFor(context, Address.fromSerialized(serialized), false);
}
public @Nullable Recipients getNext() {
public @Nullable Recipient getNext() {
if (!cursor.moveToNext()) {
return null;
}
@@ -289,14 +282,14 @@ public class RecipientPreferenceDatabase extends Database {
public static class RecipientPreferenceEvent {
private final Recipients recipients;
private final Recipient recipient;
RecipientPreferenceEvent(Recipients recipients) {
this.recipients = recipients;
public RecipientPreferenceEvent(Recipient recipients) {
this.recipient = recipients;
}
public Recipients getRecipients() {
return recipients;
public Recipient getRecipient() {
return recipient;
}
}
}

View File

@@ -34,8 +34,8 @@ import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@@ -457,8 +457,8 @@ public class SmsDatabase extends MessagingDatabase {
}
private @NonNull Pair<Long, Long> insertCallLog(@NonNull Address address, long type, boolean unread) {
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {address}, true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
Recipient recipient = RecipientFactory.getRecipientFor(context, address, true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
ContentValues values = new ContentValues(6);
values.put(ADDRESS, address.serialize());
@@ -506,14 +506,14 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
Recipients recipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getSender()}, true);
Recipient recipient = RecipientFactory.getRecipientFor(context, message.getSender(), true);
Recipients groupRecipients;
Recipient groupRecipient;
if (message.getGroupId() == null) {
groupRecipients = null;
groupRecipient = null;
} else {
groupRecipients = RecipientFactory.getRecipientsFor(context, new Address[] {message.getGroupId()}, true);
groupRecipient = RecipientFactory.getRecipientFor(context, message.getGroupId(), true);
}
boolean unread = (org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) ||
@@ -522,8 +522,8 @@ public class SmsDatabase extends MessagingDatabase {
long threadId;
if (groupRecipients == null) threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
if (groupRecipient == null) threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
ContentValues values = new ContentValues(6);
values.put(ADDRESS, message.getSender().serialize());
@@ -560,7 +560,7 @@ public class SmsDatabase extends MessagingDatabase {
}
if (message.getSubscriptionId() != -1) {
DatabaseFactory.getRecipientPreferenceDatabase(context).setDefaultSubscriptionId(recipients, message.getSubscriptionId());
DatabaseFactory.getRecipientPreferenceDatabase(context).setDefaultSubscriptionId(recipient, message.getSubscriptionId());
}
notifyConversationListeners(threadId);
@@ -589,7 +589,7 @@ public class SmsDatabase extends MessagingDatabase {
if (message.isIdentityVerified()) type |= Types.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT;
else if (message.isIdentityDefault()) type |= Types.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT;
Address address = message.getRecipients().getPrimaryRecipient().getAddress();
Address address = message.getRecipient().getAddress();
ContentValues contentValues = new ContentValues(6);
contentValues.put(ADDRESS, address.serialize());
@@ -783,7 +783,7 @@ public class SmsDatabase extends MessagingDatabase {
public MessageRecord getCurrent() {
return new SmsMessageRecord(context, id, new DisplayRecord.Body(message.getMessageBody(), true),
message.getRecipients(), message.getRecipients().getPrimaryRecipient(),
message.getRecipient(), message.getRecipient(),
1, System.currentTimeMillis(), System.currentTimeMillis(),
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
threadId, 0, new LinkedList<IdentityKeyMismatch>(),
@@ -828,21 +828,17 @@ public class SmsDatabase extends MessagingDatabase {
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
List<IdentityKeyMismatch> mismatches = getMismatches(mismatchDocument);
Recipients recipients = getRecipientsFor(address);
Recipient recipient = RecipientFactory.getRecipientFor(context, address, true);
DisplayRecord.Body body = getBody(cursor);
return new SmsMessageRecord(context, messageId, body, recipients,
recipients.getPrimaryRecipient(),
return new SmsMessageRecord(context, messageId, body, recipient,
recipient,
addressDeviceId,
dateSent, dateReceived, receiptCount, type,
threadId, status, mismatches, subscriptionId,
expiresIn, expireStarted);
}
private Recipients getRecipientsFor(Address address) {
return RecipientFactory.getRecipientsFor(context, new Address[] {address}, true);
}
private List<IdentityKeyMismatch> getMismatches(String document) {
try {
if (!TextUtils.isEmpty(document)) {

View File

@@ -22,16 +22,19 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.text.TextUtils;
import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
public class SmsMigrator {
@@ -140,21 +143,21 @@ public class SmsMigrator {
}
}
private static Recipients getOurRecipients(Context context, String theirRecipients) {
StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " ");
List<Address> addressList = new LinkedList<>();
private static @Nullable Set<Recipient> getOurRecipients(Context context, String theirRecipients) {
StringTokenizer tokenizer = new StringTokenizer(theirRecipients.trim(), " ");
Set<Recipient> recipientList = new HashSet<>();
while (tokenizer.hasMoreTokens()) {
String theirRecipientId = tokenizer.nextToken();
String address = getTheirCanonicalAddress(context, theirRecipientId);
if (address != null) {
addressList.add(Address.fromExternal(context, address));
recipientList.add(RecipientFactory.getRecipientFor(context, Address.fromExternal(context, address), true));
}
}
if (addressList.isEmpty()) return null;
else return RecipientFactory.getRecipientsFor(context, addressList.toArray(new Address[0]), true);
if (recipientList.isEmpty()) return null;
else return recipientList;
}
private static String encrypt(MasterSecret masterSecret, String body)
@@ -221,16 +224,30 @@ public class SmsMigrator {
cursor = context.getContentResolver().query(threadListUri, null, null, null, "date ASC");
while (cursor != null && cursor.moveToNext()) {
long theirThreadId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
String theirRecipients = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
Recipients ourRecipients = getOurRecipients(context, theirRecipients);
ProgressDescription progress = new ProgressDescription(cursor.getCount(), cursor.getPosition(), 100, 0);
long theirThreadId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
String theirRecipients = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
Set<Recipient> ourRecipients = getOurRecipients(context, theirRecipients);
ProgressDescription progress = new ProgressDescription(cursor.getCount(), cursor.getPosition(), 100, 0);
if (ourRecipients != null) {
long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients);
migrateConversation(context, masterSecret,
listener, progress,
theirThreadId, ourThreadId);
if (ourRecipients.size() == 1) {
long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients.iterator().next());
migrateConversation(context, masterSecret, listener, progress, theirThreadId, ourThreadId);
} else if (ourRecipients.size() > 1) {
ourRecipients.add(RecipientFactory.getRecipientFor(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true));
List<Address> memberAddresses = new LinkedList<>();
for (Recipient recipient : ourRecipients) {
memberAddresses.add(recipient.getAddress());
}
String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(memberAddresses, true);
Recipient ourGroupRecipient = RecipientFactory.getRecipientFor(context, Address.fromSerialized(ourGroupId), true);
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
migrateConversation(context, masterSecret, listener, progress, theirThreadId, ourThreadId);
}
}
progress.incrementPrimaryComplete();

View File

@@ -36,13 +36,12 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.InvalidMessageException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -55,7 +54,7 @@ public class ThreadDatabase extends Database {
public static final String ID = "_id";
public static final String DATE = "date";
public static final String MESSAGE_COUNT = "message_count";
public static final String ADDRESSES = "recipient_ids";
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";
@@ -71,7 +70,7 @@ public class ThreadDatabase extends Database {
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" +
ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " +
MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESSES + " 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, " +
@@ -80,7 +79,7 @@ public class ThreadDatabase extends Database {
LAST_SEEN + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESSES + ");",
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");",
"CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
};
@@ -88,14 +87,14 @@ public class ThreadDatabase extends Database {
super(context, databaseHelper);
}
private long createThreadForRecipients(Address[] addresses, int recipientCount, int distributionType) {
private long createThreadForRecipient(Address address, boolean group, int distributionType) {
ContentValues contentValues = new ContentValues(4);
long date = System.currentTimeMillis();
contentValues.put(DATE, date - date % 1000);
contentValues.put(ADDRESSES, Address.toSerializedList(Arrays.asList(addresses), ' '));
contentValues.put(ADDRESS, address.serialize());
if (recipientCount > 1)
if (group)
contentValues.put(TYPE, distributionType);
contentValues.put(MESSAGE_COUNT, 0);
@@ -272,6 +271,22 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners();
}
public int getDistributionType(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, new String[]{TYPE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
return cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
}
return DistributionTypes.DEFAULT;
} finally {
if (cursor != null) cursor.close();
}
}
public Cursor getFilteredConversationList(List<Address> filter) {
if (filter == null || filter.size() == 0)
return null;
@@ -281,11 +296,11 @@ public class ThreadDatabase extends Database {
List<Cursor> cursors = new LinkedList<>();
for (List<Address> addresses : partitionedAddresses) {
String selection = ADDRESSES + " = ?";
String selection = ADDRESS + " = ?";
String[] selectionArgs = new String[addresses.size()];
for (int i=0;i<addresses.size()-1;i++)
selection += (" OR " + ADDRESSES + " = ?");
selection += (" OR " + ADDRESS + " = ?");
int i= 0;
for (Address address : addresses) {
@@ -410,12 +425,11 @@ public class ThreadDatabase extends Database {
deleteAllThreads();
}
public long getThreadIdIfExistsFor(Recipients recipients) {
List<Address> addresses = Arrays.asList(recipients.getAddresses());
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESSES + " = ?";
String[] recipientsArg = new String[]{Address.toSerializedList(addresses, ' ')};
Cursor cursor = null;
public long getThreadIdIfExistsFor(Recipient recipient) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESS + " = ?";
String[] recipientsArg = new String[] {recipient.getAddress().serialize()};
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
@@ -430,15 +444,14 @@ public class ThreadDatabase extends Database {
}
}
public long getThreadIdFor(Recipients recipients) {
return getThreadIdFor(recipients, DistributionTypes.DEFAULT);
public long getThreadIdFor(Recipient recipient) {
return getThreadIdFor(recipient, DistributionTypes.DEFAULT);
}
public long getThreadIdFor(Recipients recipients, int distributionType) {
Address[] addresses = recipients.getAddresses();
public long getThreadIdFor(Recipient recipient, int distributionType) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = ADDRESSES + " = ?";
String[] recipientsArg = new String[]{Address.toSerializedList(Arrays.asList(addresses), ' ')};
String where = ADDRESS + " = ?";
String[] recipientsArg = new String[]{recipient.getAddress().serialize()};
Cursor cursor = null;
try {
@@ -447,7 +460,7 @@ public class ThreadDatabase extends Database {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
} else {
return createThreadForRecipients(addresses, recipients.getRecipientsList().size(), distributionType);
return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType);
}
} finally {
if (cursor != null)
@@ -455,7 +468,7 @@ public class ThreadDatabase extends Database {
}
}
public @Nullable Recipients getRecipientsForThreadId(long threadId) {
public @Nullable Recipient getRecipientForThreadId(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
@@ -463,8 +476,8 @@ public class ThreadDatabase extends Database {
cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
List<Address> addresses = Address.fromSerializedList(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESSES)), ' ');
return RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), false);
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
return RecipientFactory.getRecipientFor(context, address, false);
}
} finally {
if (cursor != null)
@@ -561,9 +574,9 @@ public class ThreadDatabase extends Database {
}
public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
List<Address> addresses = Address.fromSerializedList(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESSES)), ' ');
Recipients recipients = RecipientFactory.getRecipientsFor(context, addresses.toArray(new Address[0]), true);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS)));
Recipient recipient = RecipientFactory.getRecipientFor(context, address, true);
DisplayRecord.Body body = getPlaintextBody(cursor);
long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
@@ -578,7 +591,7 @@ public class ThreadDatabase extends Database {
long lastSeen = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.LAST_SEEN));
Uri snippetUri = getSnippetUri(cursor);
return new ThreadRecord(context, body, snippetUri, recipients, date, count, read == 1,
return new ThreadRecord(context, body, snippetUri, recipient, date, count, read == 1,
threadId, receiptCount, status, type, distributionType, archived,
expiresIn, lastSeen);
}

View File

@@ -42,7 +42,7 @@ public class ConversationListLoader extends AbstractCursorLoader {
if (archivedCount > 0) {
MatrixCursor switchToArchiveCursor = new MatrixCursor(new String[] {
ThreadDatabase.ID, ThreadDatabase.DATE, ThreadDatabase.MESSAGE_COUNT,
ThreadDatabase.ADDRESSES, ThreadDatabase.SNIPPET, ThreadDatabase.READ,
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);

View File

@@ -21,7 +21,7 @@ import android.text.SpannableString;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.Recipient;
/**
* The base class for all message record models. Encapsulates basic data
@@ -36,7 +36,7 @@ public abstract class DisplayRecord {
protected final Context context;
protected final long type;
private final Recipients recipients;
private final Recipient recipient;
private final long dateSent;
private final long dateReceived;
private final long threadId;
@@ -44,12 +44,12 @@ public abstract class DisplayRecord {
private final int deliveryStatus;
private final int receiptCount;
public DisplayRecord(Context context, Body body, Recipients recipients, long dateSent,
public DisplayRecord(Context context, Body body, Recipient recipient, long dateSent,
long dateReceived, long threadId, int deliveryStatus, int receiptCount, long type)
{
this.context = context.getApplicationContext();
this.threadId = threadId;
this.recipients = recipients;
this.recipient = recipient;
this.dateSent = dateSent;
this.dateReceived = dateReceived;
this.type = type;
@@ -81,8 +81,8 @@ public abstract class DisplayRecord {
public abstract SpannableString getDisplayBody();
public Recipients getRecipients() {
return recipients;
public Recipient getRecipient() {
return recipient;
}
public long getDateSent() {

View File

@@ -27,7 +27,6 @@ import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.List;
@@ -45,7 +44,7 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
private final Context context;
private final int partCount;
public MediaMmsMessageRecord(Context context, long id, Recipients recipients,
public MediaMmsMessageRecord(Context context, long id, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, int receiptCount,
long threadId, Body body,
@@ -55,7 +54,7 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
List<NetworkFailure> failures, int subscriptionId,
long expiresIn, long expireStarted)
{
super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent,
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox, mismatches, failures,
subscriptionId, expiresIn, expireStarted, slideDeck);

View File

@@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
@@ -55,7 +54,7 @@ public abstract class MessageRecord extends DisplayRecord {
private final long expiresIn;
private final long expireStarted;
MessageRecord(Context context, long id, Body body, Recipients recipients,
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,
@@ -63,7 +62,7 @@ public abstract class MessageRecord extends DisplayRecord {
List<NetworkFailure> networkFailures,
int subscriptionId, long expiresIn, long expireStarted)
{
super(context, body, recipients, dateSent, dateReceived, threadId, deliveryStatus, receiptCount,
super(context, body, conversationRecipient, dateSent, dateReceived, threadId, deliveryStatus, receiptCount,
type);
this.id = id;
this.individualRecipient = individualRecipient;

View File

@@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.List;
@@ -17,14 +16,14 @@ public abstract class MmsMessageRecord extends MessageRecord {
private final @NonNull SlideDeck slideDeck;
MmsMessageRecord(Context context, long id, Body body, Recipients recipients,
MmsMessageRecord(Context context, long id, Body body, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId, long dateSent,
long dateReceived, long threadId, int deliveryStatus, int receiptCount,
long type, List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> networkFailures, int subscriptionId, long expiresIn,
long expireStarted, @NonNull SlideDeck slideDeck)
{
super(context, id, body, recipients, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, receiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted);
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, receiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted);
this.slideDeck = slideDeck;
}

View File

@@ -20,13 +20,12 @@ import android.content.Context;
import android.text.SpannableString;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.SmsDatabase.Status;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList;
@@ -46,14 +45,14 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
private final int status;
private final byte[] transactionId;
public NotificationMmsMessageRecord(Context context, long id, Recipients recipients,
public NotificationMmsMessageRecord(Context context, long id, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, int receiptCount,
long threadId, byte[] contentLocation, long messageSize,
long expiry, int status, byte[] transactionId, long mailbox,
int subscriptionId, SlideDeck slideDeck)
{
super(context, id, new Body("", true), recipients, individualRecipient, recipientDeviceId,
super(context, id, new Body("", true), conversationRecipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, Status.STATUS_NONE, receiptCount, mailbox,
new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId,
0, 0, slideDeck);

View File

@@ -26,7 +26,6 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList;
import java.util.List;
@@ -41,7 +40,7 @@ import java.util.List;
public class SmsMessageRecord extends MessageRecord {
public SmsMessageRecord(Context context, long id,
Body body, Recipients recipients,
Body body, Recipient recipient,
Recipient individualRecipient,
int recipientDeviceId,
long dateSent, long dateReceived,
@@ -50,7 +49,7 @@ public class SmsMessageRecord extends MessageRecord {
int status, List<IdentityKeyMismatch> mismatches,
int subscriptionId, long expiresIn, long expireStarted)
{
super(context, id, body, recipients, individualRecipient, recipientDeviceId,
super(context, id, body, recipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, status, receiptCount, type,
mismatches, new LinkedList<NetworkFailure>(), subscriptionId,
expiresIn, expireStarted);

View File

@@ -28,7 +28,7 @@ import android.text.style.StyleSpan;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ExpirationUtil;
/**
@@ -49,11 +49,11 @@ public class ThreadRecord extends DisplayRecord {
private final long lastSeen;
public ThreadRecord(@NonNull Context context, @NonNull Body body, @Nullable Uri snippetUri,
@NonNull Recipients recipients, long date, long count, boolean read,
@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)
{
super(context, body, recipients, date, date, threadId, status, receiptCount, snippetType);
super(context, body, recipient, date, date, threadId, status, receiptCount, snippetType);
this.context = context.getApplicationContext();
this.snippetUri = snippetUri;
this.count = count;
@@ -98,13 +98,13 @@ public class ThreadRecord extends DisplayRecord {
} else if (SmsDatabase.Types.isMissedCall(type)) {
return emphasisAdded(context.getString(org.thoughtcrime.securesms.R.string.ThreadRecord_missed_call));
} else if (SmsDatabase.Types.isJoinedType(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipients().getPrimaryRecipient().toShortString()));
return emphasisAdded(context.getString(R.string.ThreadRecord_s_is_on_signal, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isExpirationTimerUpdate(type)) {
String time = ExpirationUtil.getExpirationDisplayValue(context, (int) (getExpiresIn() / 1000));
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
} else if (SmsDatabase.Types.isIdentityUpdate(type)) {
if (getRecipients().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipients().getPrimaryRecipient().toShortString()));
if (getRecipient().isGroupRecipient()) return emphasisAdded(context.getString(R.string.ThreadRecord_safety_number_changed));
else return emphasisAdded(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, getRecipient().toShortString()));
} else if (SmsDatabase.Types.isIdentityVerified(type)) {
return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_verified));
} else if (SmsDatabase.Types.isIdentityDefault(type)) {