diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java index ae3f1d88b1..9138b02614 100644 --- a/src/org/thoughtcrime/securesms/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationActivity.java @@ -1497,8 +1497,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private boolean isActiveGroup() { if (!isGroupConversation()) return false; - GroupRecord record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString()); - return record != null && record.isActive(); + Optional record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString()); + return record.isPresent() && record.get().isActive(); } private boolean isSelfConversation() { diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java index 9d16504ae7..c9ecdad52f 100644 --- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java +++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -537,18 +537,18 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity @Override protected Optional doInBackground(String... groupIds) { - final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity); - final List recipients = db.getGroupMembers(groupIds[0], false); - final GroupRecord group = db.getGroup(groupIds[0]); - final Set existingContacts = new HashSet<>(recipients.size()); + final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity); + final List recipients = db.getGroupMembers(groupIds[0], false); + final Optional group = db.getGroup(groupIds[0]); + final Set existingContacts = new HashSet<>(recipients.size()); existingContacts.addAll(recipients); - if (group != null) { + if (group.isPresent()) { return Optional.of(new GroupData(groupIds[0], existingContacts, - BitmapUtil.fromByteArray(group.getAvatar()), - group.getAvatar(), - group.getTitle())); + BitmapUtil.fromByteArray(group.get().getAvatar()), + group.get().getAvatar(), + group.get().getTitle())); } else { return Optional.absent(); } diff --git a/src/org/thoughtcrime/securesms/database/GroupDatabase.java b/src/org/thoughtcrime/securesms/database/GroupDatabase.java index e4b6f4a690..c5eb5d5bdf 100644 --- a/src/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/src/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.database; -import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; import android.content.Intent; @@ -11,12 +10,17 @@ import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.Log; + +import com.annimon.stream.Stream; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.Util; +import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import java.io.IOException; @@ -32,9 +36,9 @@ public class GroupDatabase extends Database { private static final String TAG = GroupDatabase.class.getSimpleName(); - private static final String TABLE_NAME = "groups"; + static final String TABLE_NAME = "groups"; private static final String ID = "_id"; - private static final String GROUP_ID = "group_id"; + static final String GROUP_ID = "group_id"; private static final String TITLE = "title"; private static final String MEMBERS = "members"; private static final String AVATAR = "avatar"; @@ -67,21 +71,33 @@ public class GroupDatabase extends Database { "CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");", }; + private static final String[] GROUP_PROJECTION = { + GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST, + TIMESTAMP, ACTIVE, MMS + }; + + static final List TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList(); + public GroupDatabase(Context context, SQLiteOpenHelper databaseHelper) { super(context, databaseHelper); } - public @Nullable GroupRecord getGroup(String groupId) { - @SuppressLint("Recycle") - Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?", - new String[] {groupId}, - null, null, null); + public Optional getGroup(String groupId) { + try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?", + new String[] {groupId}, + null, null, null)) + { + if (cursor != null && cursor.moveToNext()) { + return getGroup(cursor); + } - Reader reader = new Reader(cursor); - GroupRecord record = reader.getNext(); + return Optional.absent(); + } + } - reader.close(); - return record; + Optional getGroup(Cursor cursor) { + Reader reader = new Reader(cursor); + return Optional.fromNullable(reader.getCurrent()); } public boolean isUnknownGroup(String groupId) { @@ -251,8 +267,8 @@ public class GroupDatabase extends Database { } public boolean isActive(String groupId) { - GroupRecord record = getGroup(groupId); - return record != null && record.isActive(); + Optional record = getGroup(groupId); + return record.isPresent() && record.get().isActive(); } public void setActive(String groupId, boolean active) { @@ -291,6 +307,14 @@ public class GroupDatabase extends Database { return null; } + return getCurrent(); + } + + public @Nullable GroupRecord getCurrent() { + if (cursor == null || cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)) == null) { + return null; + } + return new GroupRecord(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)), cursor.getString(cursor.getColumnIndexOrThrow(TITLE)), cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), @@ -330,7 +354,6 @@ public class GroupDatabase extends Database { { this.id = id; this.title = title; - this.members = Address.fromSerializedList(members, ','); this.avatar = avatar; this.avatarId = avatarId; this.avatarKey = avatarKey; @@ -339,6 +362,9 @@ public class GroupDatabase extends Database { this.relay = relay; this.active = active; this.mms = mms; + + if (!TextUtils.isEmpty(members)) this.members = Address.fromSerializedList(members, ','); + else this.members = new LinkedList<>(); } public byte[] getId() { diff --git a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java index 7c623e5229..b643c86316 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java @@ -30,7 +30,7 @@ public class RecipientPreferenceDatabase extends Database { static final String TABLE_NAME = "recipient_preferences"; private static final String ID = "_id"; - private static final String ADDRESS = "recipient_ids"; + static final String ADDRESS = "recipient_ids"; private static final String BLOCK = "block"; private static final String NOTIFICATION = "notification"; private static final String VIBRATE = "vibrate"; @@ -110,7 +110,7 @@ public class RecipientPreferenceDatabase extends Database { cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", new String[] {address.serialize()}, null, null, null); if (cursor != null && cursor.moveToNext()) { - return Optional.of(getRecipientPreferences(cursor)); + return getRecipientPreferences(cursor); } return Optional.absent(); @@ -119,7 +119,7 @@ public class RecipientPreferenceDatabase extends Database { } } - RecipientsPreferences getRecipientPreferences(@NonNull Cursor cursor) { + Optional getRecipientPreferences(@NonNull Cursor cursor) { boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1; String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION)); int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE)); @@ -141,11 +141,11 @@ public class RecipientPreferenceDatabase extends Database { color = null; } - return new RecipientsPreferences(blocked, muteUntil, - VibrateState.fromId(vibrateState), - notificationUri, color, seenInviteReminder, - defaultSubscriptionId, expireMessages, registered, - systemDisplayname); + return Optional.of(new RecipientsPreferences(blocked, muteUntil, + VibrateState.fromId(vibrateState), + notificationUri, color, seenInviteReminder, + defaultSubscriptionId, expireMessages, registered, + systemDisplayname)); } public void setColor(Recipient recipient, MaterialColor color) { diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java index 6d3cca06ff..77f074ccd5 100644 --- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -31,6 +31,7 @@ import com.annimon.stream.Stream; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.MasterCipher; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.model.DisplayRecord; @@ -44,6 +45,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.InvalidMessageException; +import org.whispersystems.libsignal.util.guava.Optional; import java.util.LinkedList; import java.util.List; @@ -95,9 +97,10 @@ public class ThreadDatabase extends Database { .map(columnName -> TABLE_NAME + "." + columnName) .toList(); - private static final List COMBINED_THREAD_RECIPIENT_PROJECTION = Stream.concat(Stream.of(TYPED_THREAD_PROJECTION), - Stream.of(RecipientPreferenceDatabase.TYPED_RECIPIENT_PROJECTION)) - .toList(); + private static final List COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION = Stream.concat(Stream.concat(Stream.of(TYPED_THREAD_PROJECTION), + Stream.of(RecipientPreferenceDatabase.TYPED_RECIPIENT_PROJECTION)), + Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION)) + .toList(); public ThreadDatabase(Context context, SQLiteOpenHelper databaseHelper) { super(context, databaseHelper); @@ -340,11 +343,13 @@ public class ThreadDatabase extends Database { } private Cursor getConversationList(String archived) { - String projection = Util.join(COMBINED_THREAD_RECIPIENT_PROJECTION, ","); + String projection = Util.join(COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION, ","); SQLiteDatabase db = databaseHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("SELECT " + projection + " FROM " + TABLE_NAME + " LEFT OUTER JOIN " + RecipientPreferenceDatabase.TABLE_NAME + - " ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientPreferenceDatabase.TABLE_NAME + "." + ADDRESS + + " ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientPreferenceDatabase.TABLE_NAME + "." + RecipientPreferenceDatabase.ADDRESS + + " LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME + + " ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GroupDatabase.GROUP_ID + " WHERE " + ARCHIVED + " = ? AND " + MESSAGE_COUNT + " != 0" + " ORDER BY " + TABLE_NAME + "." + DATE + " DESC", new String[] {archived}); @@ -356,7 +361,7 @@ public class ThreadDatabase extends Database { public Cursor getDirectShareList() { SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String projection = Util.join(COMBINED_THREAD_RECIPIENT_PROJECTION, ","); + String projection = Util.join(COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION, ","); return db.rawQuery("SELECT " + projection + " FROM " + TABLE_NAME + " LEFT OUTER JOIN " + RecipientPreferenceDatabase.TABLE_NAME + @@ -601,10 +606,11 @@ public class ThreadDatabase extends Database { } public ThreadRecord getCurrent() { - long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); - Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS))); - RecipientsPreferences preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientPreferences(cursor); - Recipient recipient = RecipientFactory.getRecipientFor(context, address, preferences, true); + long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); + Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS))); + Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientPreferences(cursor); + Optional groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(cursor); + Recipient recipient = RecipientFactory.getRecipientFor(context, address, preferences, groupRecord, true); DisplayRecord.Body body = getPlaintextBody(cursor); long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java index c3529dc0ba..48817c41c7 100644 --- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java +++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java @@ -58,19 +58,19 @@ public class GroupMessageProcessor { return null; } - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - SignalServiceGroup group = message.getGroupInfo().get(); - String id = GroupUtil.getEncodedId(group.getGroupId(), false); - GroupRecord record = database.getGroup(id); + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + SignalServiceGroup group = message.getGroupInfo().get(); + String id = GroupUtil.getEncodedId(group.getGroupId(), false); + Optional record = database.getGroup(id); - if (record != null && group.getType() == Type.UPDATE) { - return handleGroupUpdate(context, masterSecret, envelope, group, record, outgoing); - } else if (record == null && group.getType() == Type.UPDATE) { + if (record.isPresent() && group.getType() == Type.UPDATE) { + return handleGroupUpdate(context, masterSecret, envelope, group, record.get(), outgoing); + } else if (record.isPresent() && group.getType() == Type.UPDATE) { return handleGroupCreate(context, masterSecret, envelope, group, outgoing); - } else if (record != null && group.getType() == Type.QUIT) { - return handleGroupLeave(context, masterSecret, envelope, group, record, outgoing); - } else if (record != null && group.getType() == Type.REQUEST_INFO) { - return handleGroupInfoRequest(context, envelope, group, record); + } else if (record.isPresent() && group.getType() == Type.QUIT) { + return handleGroupLeave(context, masterSecret, envelope, group, record.get(), outgoing); + } else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) { + return handleGroupInfoRequest(context, envelope, group, record.get()); } else { Log.w(TAG, "Received unknown type, ignoring..."); return null; diff --git a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java index 9e9b8bc839..bcf7a200d6 100644 --- a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java @@ -8,6 +8,7 @@ import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel; @@ -55,18 +56,18 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType @Override public void onRun(MasterSecret masterSecret) throws IOException { - String encodeId = GroupUtil.getEncodedId(groupId, false); - GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - GroupDatabase.GroupRecord record = database.getGroup(encodeId); - File attachment = null; + String encodeId = GroupUtil.getEncodedId(groupId, false); + GroupDatabase database = DatabaseFactory.getGroupDatabase(context); + Optional record = database.getGroup(encodeId); + File attachment = null; try { - if (record != null) { - long avatarId = record.getAvatarId(); - String contentType = record.getAvatarContentType(); - byte[] key = record.getAvatarKey(); - String relay = record.getRelay(); - Optional digest = Optional.fromNullable(record.getAvatarDigest()); + if (record.isPresent()) { + long avatarId = record.get().getAvatarId(); + String contentType = record.get().getAvatarContentType(); + byte[] key = record.get().getAvatarKey(); + String relay = record.get().getRelay(); + Optional digest = Optional.fromNullable(record.get().getAvatarDigest()); Optional fileName = Optional.absent(); if (avatarId == -1 || key == null) { diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java index 3a2e9ef1ad..cfb463b497 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule.SignalM import org.thoughtcrime.securesms.util.GroupUtil; import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.requirements.NetworkRequirement; +import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; @@ -60,7 +61,7 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType { public void onRun() throws IOException, UntrustedIdentityException { SignalServiceMessageSender messageSender = messageSenderFactory.create(); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - GroupRecord record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false)); + Optional record = groupDatabase.getGroup(GroupUtil.getEncodedId(groupId, false)); SignalServiceAttachment avatar = null; if (record == null) { @@ -68,17 +69,17 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType { return; } - if (record.getAvatar() != null) { + if (record.get().getAvatar() != null) { avatar = SignalServiceAttachmentStream.newStreamBuilder() .withContentType("image/jpeg") - .withStream(new ByteArrayInputStream(record.getAvatar())) - .withLength(record.getAvatar().length) + .withStream(new ByteArrayInputStream(record.get().getAvatar())) + .withLength(record.get().getAvatar().length) .build(); } List members = new LinkedList<>(); - for (Address member : record.getMembers()) { + for (Address member : record.get().getMembers()) { members.add(member.serialize()); } @@ -86,7 +87,7 @@ public class PushGroupUpdateJob extends ContextJob implements InjectableType { .withAvatar(avatar) .withId(groupId) .withMembers(members) - .withName(record.getTitle()) + .withName(record.get().getTitle()) .build(); SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index b7fca83afe..d0590efa9f 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -69,7 +69,7 @@ public class Recipient implements RecipientModifiedListener { Recipient(@NonNull Address address, @Nullable Recipient stale, - @NonNull Optional preferences, + @NonNull Optional details, @NonNull ListenableFutureTask future) { this.address = address; @@ -90,17 +90,17 @@ public class Recipient implements RecipientModifiedListener { this.expireMessages = stale.expireMessages; } - if (preferences.isPresent()) { - if (!TextUtils.isEmpty(preferences.get().getSystemDisplayName())) { - this.name = preferences.get().getSystemDisplayName(); - } - - this.color = preferences.get().getColor(); - this.ringtone = preferences.get().getRingtone(); - this.mutedUntil = preferences.get().getMuteUntil(); - this.blocked = preferences.get().isBlocked(); - this.vibrate = preferences.get().getVibrateState(); - this.expireMessages = preferences.get().getExpireMessages(); + if (details.isPresent()) { + this.name = details.get().name; + this.contactPhoto = details.get().avatar; + this.color = details.get().color; + this.ringtone = details.get().ringtone; + this.mutedUntil = details.get().mutedUntil; + this.blocked = details.get().blocked; + this.vibrate = details.get().vibrateState; + this.expireMessages = details.get().expireMessages; + this.participants.clear(); + this.participants.addAll(details.get().participants); } future.addListener(new FutureTaskListener() { @@ -118,6 +118,8 @@ public class Recipient implements RecipientModifiedListener { Recipient.this.blocked = result.blocked; Recipient.this.vibrate = result.vibrateState; Recipient.this.expireMessages = result.expireMessages; + + Recipient.this.participants.clear(); Recipient.this.participants.addAll(result.participants); Recipient.this.resolving = false; diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java index f1eb2e0fd1..bc01001839 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientFactory.java @@ -21,7 +21,7 @@ import android.content.Intent; import android.support.annotation.NonNull; import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.whispersystems.libsignal.util.guava.Optional; @@ -33,12 +33,12 @@ public class RecipientFactory { public static @NonNull Recipient getRecipientFor(@NonNull Context context, @NonNull Address address, boolean asynchronous) { if (address == null) throw new AssertionError(address); - return provider.getRecipient(context, address, Optional.absent(), asynchronous); + return provider.getRecipient(context, address, Optional.absent(), Optional.absent(), asynchronous); } - public static @NonNull Recipient getRecipientFor(@NonNull Context context, @NonNull Address address, @NonNull RecipientsPreferences preferences, boolean asynchronous) { + public static @NonNull Recipient getRecipientFor(@NonNull Context context, @NonNull Address address, @NonNull Optional preferences, @NonNull Optional groupRecord, boolean asynchronous) { if (address == null) throw new AssertionError(address); - return provider.getRecipient(context, address, Optional.of(preferences), asynchronous); + return provider.getRecipient(context, address, preferences, groupRecord, asynchronous); } public static void clearCache(Context context) { diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java index 1c53a5fb4a..089ff53ec7 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java @@ -32,7 +32,7 @@ import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto; import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState; import org.thoughtcrime.securesms.util.LRUCache; @@ -68,16 +68,18 @@ class RecipientProvider { null, null)); }}; - @NonNull Recipient getRecipient(Context context, Address address, Optional preferences, boolean asynchronous) { + @NonNull Recipient getRecipient(Context context, Address address, Optional preferences, Optional groupRecord, boolean asynchronous) { Recipient cachedRecipient = recipientCache.get(address); if (cachedRecipient != null && !cachedRecipient.isStale() && (asynchronous || !cachedRecipient.isResolving())) { return cachedRecipient; } + Optional prefetchedRecipientDetails = createPrefetchedRecipientDetails(context, address, preferences, groupRecord); + if (asynchronous) { - cachedRecipient = new Recipient(address, cachedRecipient, preferences, getRecipientDetailsAsync(context, address, preferences)); + cachedRecipient = new Recipient(address, cachedRecipient, prefetchedRecipientDetails, getRecipientDetailsAsync(context, address, preferences, groupRecord)); } else { - cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address, preferences, false)); + cachedRecipient = new Recipient(address, getRecipientDetailsSync(context, address, preferences, groupRecord, false)); } recipientCache.set(address, cachedRecipient); @@ -88,12 +90,25 @@ class RecipientProvider { recipientCache.reset(); } - private @NonNull ListenableFutureTask getRecipientDetailsAsync(final Context context, final @NonNull Address address, final @NonNull Optional preferences) + private @NonNull Optional createPrefetchedRecipientDetails(@NonNull Context context, @NonNull Address address, + @NonNull Optional preferences, + @NonNull Optional groupRecord) + { + if (address.isGroup() && preferences.isPresent() && groupRecord.isPresent()) { + return Optional.of(getGroupRecipientDetails(context, address, groupRecord, preferences, true)); + } else if (!address.isGroup() && preferences.isPresent()) { + return Optional.of(new RecipientDetails(null, null, null, ContactPhotoFactory.getLoadingPhoto(), preferences.get(), null)); + } + + return Optional.absent(); + } + + private @NonNull ListenableFutureTask getRecipientDetailsAsync(final Context context, final @NonNull Address address, final @NonNull Optional preferences, final @NonNull Optional groupRecord) { Callable task = new Callable() { @Override public RecipientDetails call() throws Exception { - return getRecipientDetailsSync(context, address, preferences, true); + return getRecipientDetailsSync(context, address, preferences, groupRecord, true); } }; @@ -102,8 +117,8 @@ class RecipientProvider { return future; } - private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address, Optional preferences, boolean nestedAsynchronous) { - if (address.isGroup()) return getGroupRecipientDetails(context, address, nestedAsynchronous); + private @NonNull RecipientDetails getRecipientDetailsSync(Context context, @NonNull Address address, Optional preferences, Optional groupRecord, boolean nestedAsynchronous) { + if (address.isGroup()) return getGroupRecipientDetails(context, address, groupRecord, preferences, nestedAsynchronous); else return getIndividualRecipientDetails(context, address, preferences); } @@ -141,27 +156,33 @@ class RecipientProvider { else return new RecipientDetails(null, null, null, ContactPhotoFactory.getDefaultContactPhoto(null), preferences.orNull(), null); } - private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId, boolean asynchronous) { - GroupDatabase.GroupRecord record = DatabaseFactory.getGroupDatabase(context).getGroup(groupId.toGroupString()); + private @NonNull RecipientDetails getGroupRecipientDetails(Context context, Address groupId, Optional groupRecord, Optional preferences, boolean asynchronous) { + if (!groupRecord.isPresent()) { + groupRecord = DatabaseFactory.getGroupDatabase(context).getGroup(groupId.toGroupString()); + } - if (record != null) { - ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(record.getAvatar()); - String title = record.getTitle(); - List
memberAddresses = record.getMembers(); + if (!preferences.isPresent()) { + preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(groupId); + } + + if (groupRecord.isPresent()) { + ContactPhoto contactPhoto = ContactPhotoFactory.getGroupContactPhoto(groupRecord.get().getAvatar()); + String title = groupRecord.get().getTitle(); + List
memberAddresses = groupRecord.get().getMembers(); List members = new LinkedList<>(); for (Address memberAddress : memberAddresses) { - members.add(getRecipient(context, memberAddress, Optional.absent(), asynchronous)); + members.add(getRecipient(context, memberAddress, Optional.absent(), Optional.absent(), asynchronous)); } if (!groupId.isMmsGroup() && title == null) { title = context.getString(R.string.RecipientProvider_unnamed_group);; } - return new RecipientDetails(title, null, null, contactPhoto, null, members); + return new RecipientDetails(title, null, null, contactPhoto, preferences.orNull(), members); } - return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), null, null); + return new RecipientDetails(context.getString(R.string.RecipientProvider_unnamed_group), null, null, ContactPhotoFactory.getDefaultGroupPhoto(), preferences.orNull(), null); } static class RecipientDetails { @@ -182,7 +203,6 @@ class RecipientProvider { @Nullable RecipientsPreferences preferences, @Nullable List participants) { - this.name = name; this.customLabel = customLabel; this.avatar = avatar; this.contactUri = contactUri; @@ -193,6 +213,9 @@ class RecipientProvider { this.blocked = preferences != null && preferences.isBlocked(); this.expireMessages = preferences != null ? preferences.getExpireMessages() : 0; this.participants = participants == null ? new LinkedList() : participants; + + if (name == null && preferences != null) this.name = preferences.getSystemDisplayName(); + else this.name = name; } }