Improve UI send latency
// FREEBIE
This commit is contained in:
parent
4d889a45e2
commit
cb670d6783
30 changed files with 459 additions and 94 deletions
|
@ -102,6 +102,7 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
|
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
|
||||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientPreferenceEvent;
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientPreferenceEvent;
|
||||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||||
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||||
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
||||||
|
@ -274,7 +275,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
|
|
||||||
if (!Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent()) {
|
if (!Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent()) {
|
||||||
saveDraft();
|
saveDraft();
|
||||||
attachmentManager.clear();
|
attachmentManager.clear(false);
|
||||||
composeText.setText("");
|
composeText.setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,7 +567,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
recipients.setExpireMessages(expirationTime);
|
recipients.setExpireMessages(expirationTime);
|
||||||
|
|
||||||
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipients(), System.currentTimeMillis(), expirationTime * 1000);
|
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipients(), System.currentTimeMillis(), expirationTime * 1000);
|
||||||
MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false);
|
MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false, null);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -692,7 +693,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
|
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
|
||||||
@Override
|
@Override
|
||||||
protected Long doInBackground(OutgoingEndSessionMessage... messages) {
|
protected Long doInBackground(OutgoingEndSessionMessage... messages) {
|
||||||
return MessageSender.send(context, masterSecret, messages[0], threadId, false);
|
return MessageSender.send(context, masterSecret, messages[0], threadId, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -740,7 +741,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipients(), context, null, System.currentTimeMillis(), 0);
|
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipients(), context, null, System.currentTimeMillis(), 0);
|
||||||
MessageSender.send(self, masterSecret, outgoingMessage, threadId, false);
|
MessageSender.send(self, masterSecret, outgoingMessage, threadId, false, null);
|
||||||
DatabaseFactory.getGroupDatabase(self).remove(groupId, TextSecurePreferences.getLocalNumber(self));
|
DatabaseFactory.getGroupDatabase(self).remove(groupId, TextSecurePreferences.getLocalNumber(self));
|
||||||
initializeEnabledCheck();
|
initializeEnabledCheck();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -1511,13 +1512,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessage);
|
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentManager.clear();
|
attachmentManager.clear(false);
|
||||||
composeText.setText("");
|
composeText.setText("");
|
||||||
|
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
||||||
|
|
||||||
new AsyncTask<OutgoingMediaMessage, Void, Long>() {
|
new AsyncTask<OutgoingMediaMessage, Void, Long>() {
|
||||||
@Override
|
@Override
|
||||||
protected Long doInBackground(OutgoingMediaMessage... messages) {
|
protected Long doInBackground(OutgoingMediaMessage... messages) {
|
||||||
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms);
|
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() {
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
fragment.releaseOutgoingMessage(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1543,11 +1550,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
this.composeText.setText("");
|
this.composeText.setText("");
|
||||||
|
final long id = fragment.stageOutgoingMessage(message);
|
||||||
|
|
||||||
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
||||||
@Override
|
@Override
|
||||||
protected Long doInBackground(OutgoingTextMessage... messages) {
|
protected Long doInBackground(OutgoingTextMessage... messages) {
|
||||||
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms);
|
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() {
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
fragment.releaseOutgoingMessage(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -33,12 +33,14 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
|
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.FastCursorRecyclerViewAdapter;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
import org.thoughtcrime.securesms.util.DateUtils;
|
import org.thoughtcrime.securesms.util.DateUtils;
|
||||||
|
@ -67,7 +69,7 @@ import java.util.Set;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ConversationAdapter <V extends View & BindableConversationItem>
|
public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
extends CursorRecyclerViewAdapter<ConversationAdapter.ViewHolder>
|
extends FastCursorRecyclerViewAdapter<ConversationAdapter.ViewHolder, MessageRecord>
|
||||||
implements StickyHeaderDecoration.StickyHeaderAdapter<HeaderViewHolder>
|
implements StickyHeaderDecoration.StickyHeaderAdapter<HeaderViewHolder>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -179,14 +181,13 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
@Override
|
@Override
|
||||||
public void changeCursor(Cursor cursor) {
|
public void changeCursor(Cursor cursor) {
|
||||||
messageRecordCache.clear();
|
messageRecordCache.clear();
|
||||||
|
super.cleanFastRecords();
|
||||||
super.changeCursor(cursor);
|
super.changeCursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
|
||||||
|
|
||||||
viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, recipients);
|
viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, recipients);
|
||||||
Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start));
|
Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start));
|
||||||
}
|
}
|
||||||
|
@ -237,9 +238,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(@NonNull Cursor cursor) {
|
public int getItemViewType(@NonNull MessageRecord messageRecord) {
|
||||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
|
||||||
|
|
||||||
if (messageRecord.isGroupAction() || messageRecord.isCallLog() || messageRecord.isJoined() ||
|
if (messageRecord.isGroupAction() || messageRecord.isCallLog() || messageRecord.isJoined() ||
|
||||||
messageRecord.isExpirationTimerUpdate() || messageRecord.isEndSession() || messageRecord.isIdentityUpdate()) {
|
messageRecord.isExpirationTimerUpdate() || messageRecord.isEndSession() || messageRecord.isIdentityUpdate()) {
|
||||||
return MESSAGE_TYPE_UPDATE;
|
return MESSAGE_TYPE_UPDATE;
|
||||||
|
@ -259,14 +258,39 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isRecordForId(@NonNull MessageRecord record, long id) {
|
||||||
|
return record.getId() == id;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getItemId(@NonNull Cursor cursor) {
|
public long getItemId(@NonNull Cursor cursor) {
|
||||||
|
String fastPreflightId = cursor.getString(cursor.getColumnIndexOrThrow(AttachmentDatabase.FAST_PREFLIGHT_ID));
|
||||||
|
|
||||||
|
if (fastPreflightId != null) {
|
||||||
|
return Long.valueOf(fastPreflightId);
|
||||||
|
}
|
||||||
|
|
||||||
final String unique = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsColumns.UNIQUE_ROW_ID));
|
final String unique = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsColumns.UNIQUE_ROW_ID));
|
||||||
final byte[] bytes = digest.digest(unique.getBytes());
|
final byte[] bytes = digest.digest(unique.getBytes());
|
||||||
return Conversions.byteArrayToLong(bytes);
|
return Conversions.byteArrayToLong(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageRecord getMessageRecord(Cursor cursor) {
|
@Override
|
||||||
|
protected long getItemId(@NonNull MessageRecord record) {
|
||||||
|
if (record.isOutgoing() && record.isMms()) {
|
||||||
|
SlideDeck slideDeck = ((MmsMessageRecord)record).getSlideDeck();
|
||||||
|
|
||||||
|
if (slideDeck.getThumbnailSlide() != null && slideDeck.getThumbnailSlide().getFastPreflightId() != null) {
|
||||||
|
return Long.valueOf(slideDeck.getThumbnailSlide().getFastPreflightId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return record.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MessageRecord getRecordFromCursor(@NonNull Cursor cursor) {
|
||||||
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID));
|
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID));
|
||||||
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
|
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
|
||||||
|
|
||||||
|
@ -293,8 +317,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
int count = getItemCount();
|
int count = getItemCount();
|
||||||
|
|
||||||
for (int i=0;i<count;i++) {
|
for (int i=0;i<count;i++) {
|
||||||
Cursor cursor = getCursorAtPositionOrThrow(i);
|
MessageRecord messageRecord = getRecordForPositionOrThrow(i);
|
||||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
|
||||||
|
|
||||||
if (messageRecord.isOutgoing() || messageRecord.getDateReceived() <= lastSeen) {
|
if (messageRecord.isOutgoing() || messageRecord.getDateReceived() <= lastSeen) {
|
||||||
return i;
|
return i;
|
||||||
|
@ -339,8 +362,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
if (position >= getItemCount()) return -1;
|
if (position >= getItemCount()) return -1;
|
||||||
if (position < 0) return -1;
|
if (position < 0) return -1;
|
||||||
|
|
||||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
MessageRecord record = getRecordForPositionOrThrow(position);
|
||||||
MessageRecord record = getMessageRecord(cursor);
|
|
||||||
|
|
||||||
calendar.setTime(new Date(record.getDateSent()));
|
calendar.setTime(new Date(record.getDateSent()));
|
||||||
return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
|
return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
|
||||||
|
@ -353,8 +375,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
if (position >= getItemCount()) return 0;
|
if (position >= getItemCount()) return 0;
|
||||||
if (position < 0) return 0;
|
if (position < 0) return 0;
|
||||||
|
|
||||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
MessageRecord messageRecord = getRecordForPositionOrThrow(position);
|
||||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
|
||||||
|
|
||||||
if (messageRecord.isOutgoing()) return 0;
|
if (messageRecord.isOutgoing()) return 0;
|
||||||
else return messageRecord.getDateReceived();
|
else return messageRecord.getDateReceived();
|
||||||
|
@ -371,8 +392,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
MessageRecord messageRecord = getRecordForPositionOrThrow(position);
|
||||||
viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, getMessageRecord(cursor).getDateReceived()));
|
viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, messageRecord.getDateReceived()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onBindLastSeenViewHolder(HeaderViewHolder viewHolder, int position) {
|
public void onBindLastSeenViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||||
|
|
|
@ -54,20 +54,30 @@ import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
|
||||||
import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener;
|
import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||||
|
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||||
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
import org.thoughtcrime.securesms.database.loaders.ConversationLoader;
|
||||||
|
import org.thoughtcrime.securesms.database.model.DisplayRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||||
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.mms.Slide;
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
|
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
||||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -448,6 +458,32 @@ public class ConversationFragment extends Fragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long stageOutgoingMessage(OutgoingMediaMessage message) {
|
||||||
|
MessageRecord messageRecord = DatabaseFactory.getMmsDatabase(getContext()).readerFor(message, threadId).getCurrent();
|
||||||
|
|
||||||
|
if (getListAdapter() != null) {
|
||||||
|
getListAdapter().addFastRecord(messageRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
return messageRecord.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long stageOutgoingMessage(OutgoingTextMessage message) {
|
||||||
|
MessageRecord messageRecord = DatabaseFactory.getSmsDatabase(getContext()).readerFor(message, threadId).getCurrent();
|
||||||
|
|
||||||
|
if (getListAdapter() != null) {
|
||||||
|
getListAdapter().addFastRecord(messageRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
return messageRecord.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releaseOutgoingMessage(long id) {
|
||||||
|
if (getListAdapter() != null) {
|
||||||
|
getListAdapter().releaseFastRecord(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void scrollToLastSeenPosition(final int lastSeenPosition) {
|
private void scrollToLastSeenPosition(final int lastSeenPosition) {
|
||||||
if (lastSeenPosition > 0) {
|
if (lastSeenPosition > 0) {
|
||||||
list.post(new Runnable() {
|
list.post(new Runnable() {
|
||||||
|
|
|
@ -236,7 +236,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||||
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getIds());
|
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getIds());
|
||||||
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
|
int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
|
||||||
|
|
||||||
MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipients, message, subscriptionId), -1L, true);
|
MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipients, message, subscriptionId), -1L, true, null);
|
||||||
|
|
||||||
if (recipients.getPrimaryRecipient().getContactUri() != null) {
|
if (recipients.getPrimaryRecipient().getContactUri() != null) {
|
||||||
DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true);
|
DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true);
|
||||||
|
|
|
@ -28,9 +28,12 @@ public abstract class Attachment {
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] digest;
|
private final byte[] digest;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final String fastPreflightId;
|
||||||
|
|
||||||
public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName,
|
public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName,
|
||||||
@Nullable String location, @Nullable String key, @Nullable String relay,
|
@Nullable String location, @Nullable String key, @Nullable String relay,
|
||||||
@Nullable byte[] digest)
|
@Nullable byte[] digest, @Nullable String fastPreflightId)
|
||||||
{
|
{
|
||||||
this.contentType = contentType;
|
this.contentType = contentType;
|
||||||
this.transferState = transferState;
|
this.transferState = transferState;
|
||||||
|
@ -40,6 +43,7 @@ public abstract class Attachment {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.relay = relay;
|
this.relay = relay;
|
||||||
this.digest = digest;
|
this.digest = digest;
|
||||||
|
this.fastPreflightId = fastPreflightId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -90,4 +94,9 @@ public abstract class Attachment {
|
||||||
public byte[] getDigest() {
|
public byte[] getDigest() {
|
||||||
return digest;
|
return digest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getFastPreflightId() {
|
||||||
|
return fastPreflightId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@ public class DatabaseAttachment extends Attachment {
|
||||||
boolean hasData, boolean hasThumbnail,
|
boolean hasData, boolean hasThumbnail,
|
||||||
String contentType, int transferProgress, long size,
|
String contentType, int transferProgress, long size,
|
||||||
String fileName, String location, String key, String relay,
|
String fileName, String location, String key, String relay,
|
||||||
byte[] digest)
|
byte[] digest, String fastPreflightId)
|
||||||
{
|
{
|
||||||
super(contentType, transferProgress, size, fileName, location, key, relay, digest);
|
super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId);
|
||||||
this.attachmentId = attachmentId;
|
this.attachmentId = attachmentId;
|
||||||
this.hasData = hasData;
|
this.hasData = hasData;
|
||||||
this.hasThumbnail = hasThumbnail;
|
this.hasThumbnail = hasThumbnail;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
public class MmsNotificationAttachment extends Attachment {
|
public class MmsNotificationAttachment extends Attachment {
|
||||||
|
|
||||||
public MmsNotificationAttachment(int status, long size) {
|
public MmsNotificationAttachment(int status, long size) {
|
||||||
super("application/mms", getTransferStateFromStatus(status), size, null, null, null, null, null);
|
super("application/mms", getTransferStateFromStatus(status), size, null, null, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class PointerAttachment extends Attachment {
|
||||||
@NonNull String key, @NonNull String relay,
|
@NonNull String key, @NonNull String relay,
|
||||||
@Nullable byte[] digest)
|
@Nullable byte[] digest)
|
||||||
{
|
{
|
||||||
super(contentType, transferState, size, fileName, location, key, relay, digest);
|
super(contentType, transferState, size, fileName, location, key, relay, digest, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -12,14 +12,14 @@ public class UriAttachment extends Attachment {
|
||||||
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
|
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
|
||||||
@Nullable String fileName)
|
@Nullable String fileName)
|
||||||
{
|
{
|
||||||
this(uri, uri, contentType, transferState, size, fileName);
|
this(uri, uri, contentType, transferState, size, fileName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
|
public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
|
||||||
@NonNull String contentType, int transferState, long size,
|
@NonNull String contentType, int transferState, long size,
|
||||||
@Nullable String fileName)
|
@Nullable String fileName, @Nullable String fastPreflightId)
|
||||||
{
|
{
|
||||||
super(contentType, transferState, size, fileName, null, null, null, null);
|
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId);
|
||||||
this.dataUri = dataUri;
|
this.dataUri = dataUri;
|
||||||
this.thumbnailUri = thumbnailUri;
|
this.thumbnailUri = thumbnailUri;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class ThumbnailView extends FrameLayout {
|
||||||
|
|
||||||
if (attrs != null) {
|
if (attrs != null) {
|
||||||
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0);
|
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThumbnailView, 0, 0);
|
||||||
backgroundColorHint = typedArray.getColor(0, Color.BLACK);
|
backgroundColorHint = typedArray.getColor(R.styleable.ThumbnailView_backgroundColorHint, Color.BLACK);
|
||||||
typedArray.recycle();
|
typedArray.recycle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,13 +120,22 @@ public class ThumbnailView extends FrameLayout {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.slide != null && this.slide.getFastPreflightId() != null &&
|
||||||
|
this.slide.getFastPreflightId().equals(slide.getFastPreflightId()))
|
||||||
|
{
|
||||||
|
Log.w(TAG, "Not re-loading slide for fast preflight: " + slide.getFastPreflightId());
|
||||||
|
this.slide = slide;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isContextValid()) {
|
if (!isContextValid()) {
|
||||||
Log.w(TAG, "Not loading slide, context is invalid");
|
Log.w(TAG, "Not loading slide, context is invalid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.w(TAG, "loading part with id " + slide.asAttachment().getDataUri()
|
Log.w(TAG, "loading part with id " + slide.asAttachment().getDataUri()
|
||||||
+ ", progress " + slide.getTransferState());
|
+ ", progress " + slide.getTransferState() + ", fast preflight id: " +
|
||||||
|
slide.asAttachment().getFastPreflightId());
|
||||||
|
|
||||||
this.slide = slide;
|
this.slide = slide;
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ public class AttachmentDatabase extends Database {
|
||||||
static final String THUMBNAIL_ASPECT_RATIO = "aspect_ratio";
|
static final String THUMBNAIL_ASPECT_RATIO = "aspect_ratio";
|
||||||
static final String UNIQUE_ID = "unique_id";
|
static final String UNIQUE_ID = "unique_id";
|
||||||
static final String DIGEST = "digest";
|
static final String DIGEST = "digest";
|
||||||
|
public static final String FAST_PREFLIGHT_ID = "fast_preflight_id";
|
||||||
|
|
||||||
public static final int TRANSFER_PROGRESS_DONE = 0;
|
public static final int TRANSFER_PROGRESS_DONE = 0;
|
||||||
public static final int TRANSFER_PROGRESS_STARTED = 1;
|
public static final int TRANSFER_PROGRESS_STARTED = 1;
|
||||||
|
@ -98,7 +99,7 @@ public class AttachmentDatabase extends Database {
|
||||||
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
|
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
|
||||||
CONTENT_LOCATION, DATA, THUMBNAIL, TRANSFER_STATE,
|
CONTENT_LOCATION, DATA, THUMBNAIL, TRANSFER_STATE,
|
||||||
SIZE, FILE_NAME, THUMBNAIL, THUMBNAIL_ASPECT_RATIO,
|
SIZE, FILE_NAME, THUMBNAIL, THUMBNAIL_ASPECT_RATIO,
|
||||||
UNIQUE_ID, DIGEST};
|
UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID};
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " +
|
||||||
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
|
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
|
||||||
|
@ -108,7 +109,7 @@ public class AttachmentDatabase extends Database {
|
||||||
"ctt_t" + " TEXT, " + "encrypted" + " INTEGER, " +
|
"ctt_t" + " TEXT, " + "encrypted" + " INTEGER, " +
|
||||||
TRANSFER_STATE + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " +
|
TRANSFER_STATE + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " +
|
||||||
FILE_NAME + " TEXT, " + THUMBNAIL + " TEXT, " + THUMBNAIL_ASPECT_RATIO + " REAL, " +
|
FILE_NAME + " TEXT, " + THUMBNAIL + " TEXT, " + THUMBNAIL_ASPECT_RATIO + " REAL, " +
|
||||||
UNIQUE_ID + " INTEGER NOT NULL, " + DIGEST + " BLOB);";
|
UNIQUE_ID + " INTEGER NOT NULL, " + DIGEST + " BLOB, " + FAST_PREFLIGHT_ID + " TEXT);";
|
||||||
|
|
||||||
public static final String[] CREATE_INDEXS = {
|
public static final String[] CREATE_INDEXS = {
|
||||||
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
|
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
|
||||||
|
@ -276,6 +277,7 @@ public class AttachmentDatabase extends Database {
|
||||||
values.put(CONTENT_DISPOSITION, (String)null);
|
values.put(CONTENT_DISPOSITION, (String)null);
|
||||||
values.put(DIGEST, (byte[])null);
|
values.put(DIGEST, (byte[])null);
|
||||||
values.put(NAME, (String) null);
|
values.put(NAME, (String) null);
|
||||||
|
values.put(FAST_PREFLIGHT_ID, (String)null);
|
||||||
|
|
||||||
if (database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()) == 0) {
|
if (database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()) == 0) {
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
@ -334,7 +336,8 @@ public class AttachmentDatabase extends Database {
|
||||||
databaseAttachment.getLocation(),
|
databaseAttachment.getLocation(),
|
||||||
databaseAttachment.getKey(),
|
databaseAttachment.getKey(),
|
||||||
databaseAttachment.getRelay(),
|
databaseAttachment.getRelay(),
|
||||||
databaseAttachment.getDigest());
|
databaseAttachment.getDigest(),
|
||||||
|
databaseAttachment.getFastPreflightId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -485,7 +488,8 @@ public class AttachmentDatabase extends Database {
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
|
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)),
|
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)),
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(NAME)),
|
cursor.getString(cursor.getColumnIndexOrThrow(NAME)),
|
||||||
cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)));
|
cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)),
|
||||||
|
cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -519,6 +523,7 @@ public class AttachmentDatabase extends Database {
|
||||||
contentValues.put(NAME, attachment.getRelay());
|
contentValues.put(NAME, attachment.getRelay());
|
||||||
contentValues.put(FILE_NAME, fileName);
|
contentValues.put(FILE_NAME, fileName);
|
||||||
contentValues.put(SIZE, attachment.getSize());
|
contentValues.put(SIZE, attachment.getSize());
|
||||||
|
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
|
||||||
|
|
||||||
if (partData != null) {
|
if (partData != null) {
|
||||||
contentValues.put(DATA, partData.first.getAbsolutePath());
|
contentValues.put(DATA, partData.first.getAbsolutePath());
|
||||||
|
|
|
@ -115,6 +115,7 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||||
if (!isActiveCursor()) return 0;
|
if (!isActiveCursor()) return 0;
|
||||||
|
|
||||||
return cursor.getCount()
|
return cursor.getCount()
|
||||||
|
+ getFastAccessSize()
|
||||||
+ (hasHeaderView() ? 1 : 0)
|
+ (hasHeaderView() ? 1 : 0)
|
||||||
+ (hasFooterView() ? 1 : 0);
|
+ (hasFooterView() ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
@ -144,16 +145,22 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||||
@Override
|
@Override
|
||||||
public final void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
|
public final void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
|
||||||
if (!isHeaderPosition(position) && !isFooterPosition(position)) {
|
if (!isHeaderPosition(position) && !isFooterPosition(position)) {
|
||||||
onBindItemViewHolder((VH)viewHolder, getCursorAtPositionOrThrow(position));
|
if (isFastAccessPosition(position)) onBindFastAccessItemViewHolder((VH)viewHolder, position);
|
||||||
|
else onBindItemViewHolder((VH)viewHolder, getCursorAtPositionOrThrow(position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void onBindItemViewHolder(VH viewHolder, @NonNull Cursor cursor);
|
public abstract void onBindItemViewHolder(VH viewHolder, @NonNull Cursor cursor);
|
||||||
|
|
||||||
|
protected void onBindFastAccessItemViewHolder(VH viewHolder, int position) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getItemViewType(int position) {
|
public final int getItemViewType(int position) {
|
||||||
if (isHeaderPosition(position)) return HEADER_TYPE;
|
if (isHeaderPosition(position)) return HEADER_TYPE;
|
||||||
if (isFooterPosition(position)) return FOOTER_TYPE;
|
if (isFooterPosition(position)) return FOOTER_TYPE;
|
||||||
|
if (isFastAccessPosition(position)) return getFastAccessItemViewType(position);
|
||||||
return getItemViewType(getCursorAtPositionOrThrow(position));
|
return getItemViewType(getCursorAtPositionOrThrow(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +172,7 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||||
public final long getItemId(int position) {
|
public final long getItemId(int position) {
|
||||||
if (isHeaderPosition(position)) return HEADER_ID;
|
if (isHeaderPosition(position)) return HEADER_ID;
|
||||||
if (isFooterPosition(position)) return FOOTER_ID;
|
if (isFooterPosition(position)) return FOOTER_ID;
|
||||||
|
if (isFastAccessPosition(position)) return getFastAccessItemId(position);
|
||||||
long itemId = getItemId(getCursorAtPositionOrThrow(position));
|
long itemId = getItemId(getCursorAtPositionOrThrow(position));
|
||||||
return itemId <= Long.MIN_VALUE + 1 ? itemId + 2 : itemId;
|
return itemId <= Long.MIN_VALUE + 1 ? itemId + 2 : itemId;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +204,27 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getCursorPosition(int position) {
|
private int getCursorPosition(int position) {
|
||||||
return hasHeaderView() ? position - 1 : position;
|
if (hasHeaderView()) {
|
||||||
|
position -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return position - getFastAccessSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getFastAccessItemViewType(int position) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isFastAccessPosition(int position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getFastAccessItemId(int position) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getFastAccessSize() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AdapterDataSetObserver extends DataSetObserver {
|
private class AdapterDataSetObserver extends DataSetObserver {
|
||||||
|
|
|
@ -77,7 +77,8 @@ public class DatabaseFactory {
|
||||||
private static final int INTRODUCED_DIGEST = 30;
|
private static final int INTRODUCED_DIGEST = 30;
|
||||||
private static final int INTRODUCED_NOTIFIED = 31;
|
private static final int INTRODUCED_NOTIFIED = 31;
|
||||||
private static final int INTRODUCED_DOCUMENTS = 32;
|
private static final int INTRODUCED_DOCUMENTS = 32;
|
||||||
private static final int DATABASE_VERSION = 32;
|
private static final int INTRODUCED_FAST_PREFLIGHT = 33;
|
||||||
|
private static final int DATABASE_VERSION = 33;
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "messages.db";
|
private static final String DATABASE_NAME = "messages.db";
|
||||||
private static final Object lock = new Object();
|
private static final Object lock = new Object();
|
||||||
|
@ -858,6 +859,10 @@ public class DatabaseFactory {
|
||||||
db.execSQL("ALTER TABLE part ADD COLUMN file_name TEXT");
|
db.execSQL("ALTER TABLE part ADD COLUMN file_name TEXT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < INTRODUCED_FAST_PREFLIGHT) {
|
||||||
|
db.execSQL("ALTER TABLE part ADD COLUMN fast_preflight_id TEXT");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class EncryptingSmsDatabase extends SmsDatabase {
|
||||||
|
|
||||||
public long insertMessageOutbox(MasterSecretUnion masterSecret, long threadId,
|
public long insertMessageOutbox(MasterSecretUnion masterSecret, long threadId,
|
||||||
OutgoingTextMessage message, boolean forceSms,
|
OutgoingTextMessage message, boolean forceSms,
|
||||||
long timestamp)
|
long timestamp, InsertListener insertListener)
|
||||||
{
|
{
|
||||||
long type = Types.BASE_SENDING_TYPE;
|
long type = Types.BASE_SENDING_TYPE;
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ public class EncryptingSmsDatabase extends SmsDatabase {
|
||||||
type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
|
type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return insertMessageOutbox(threadId, message, type, forceSms, timestamp);
|
return insertMessageOutbox(threadId, message, type, forceSms, timestamp, insertListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<InsertResult> insertMessageInbox(@NonNull MasterSecretUnion masterSecret,
|
public Optional<InsertResult> insertMessageInbox(@NonNull MasterSecretUnion masterSecret,
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class FastCursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder, T>
|
||||||
|
extends CursorRecyclerViewAdapter<VH>
|
||||||
|
{
|
||||||
|
private static final String TAG = FastCursorRecyclerViewAdapter.class.getSimpleName();
|
||||||
|
|
||||||
|
private final LinkedList<T> fastRecords = new LinkedList<>();
|
||||||
|
private final List<Long> releasedRecordIds = new LinkedList<>();
|
||||||
|
|
||||||
|
protected FastCursorRecyclerViewAdapter(Context context, Cursor cursor) {
|
||||||
|
super(context, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFastRecord(@NonNull T record) {
|
||||||
|
fastRecords.addFirst(record);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releaseFastRecord(long id) {
|
||||||
|
synchronized (releasedRecordIds) {
|
||||||
|
releasedRecordIds.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void cleanFastRecords() {
|
||||||
|
synchronized (releasedRecordIds) {
|
||||||
|
Iterator<Long> releaseIdIterator = releasedRecordIds.iterator();
|
||||||
|
|
||||||
|
while (releaseIdIterator.hasNext()) {
|
||||||
|
long releasedId = releaseIdIterator.next();
|
||||||
|
Iterator<T> fastRecordIterator = fastRecords.iterator();
|
||||||
|
|
||||||
|
while (fastRecordIterator.hasNext()) {
|
||||||
|
if (isRecordForId(fastRecordIterator.next(), releasedId)) {
|
||||||
|
fastRecordIterator.remove();
|
||||||
|
releaseIdIterator.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T getRecordFromCursor(@NonNull Cursor cursor);
|
||||||
|
protected abstract void onBindItemViewHolder(VH viewHolder, @NonNull T record);
|
||||||
|
protected abstract long getItemId(@NonNull T record);
|
||||||
|
protected abstract int getItemViewType(@NonNull T record);
|
||||||
|
protected abstract boolean isRecordForId(@NonNull T record, long id);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(@NonNull Cursor cursor) {
|
||||||
|
T record = getRecordFromCursor(cursor);
|
||||||
|
return getItemViewType(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindItemViewHolder(VH viewHolder, @NonNull Cursor cursor) {
|
||||||
|
T record = getRecordFromCursor(cursor);
|
||||||
|
onBindItemViewHolder(viewHolder, record);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindFastAccessItemViewHolder(VH viewHolder, int position) {
|
||||||
|
onBindItemViewHolder(viewHolder, fastRecords.get(getCalculatedPosition(position)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getFastAccessSize() {
|
||||||
|
return fastRecords.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected T getRecordForPositionOrThrow(int position) {
|
||||||
|
if (isFastAccessPosition(position)) {
|
||||||
|
return fastRecords.get(getCalculatedPosition(position));
|
||||||
|
} else {
|
||||||
|
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||||
|
return getRecordFromCursor(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getFastAccessItemViewType(int position) {
|
||||||
|
return getItemViewType(fastRecords.get(getCalculatedPosition(position)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isFastAccessPosition(int position) {
|
||||||
|
position = getCalculatedPosition(position);
|
||||||
|
return position >= 0 && position < fastRecords.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getFastAccessItemId(int position) {
|
||||||
|
return getItemId(fastRecords.get(getCalculatedPosition(position)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCalculatedPosition(int position) {
|
||||||
|
return hasHeaderView() ? position - 1 : position;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ public class MediaDatabase extends Database {
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DIGEST + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DIGEST + ", "
|
||||||
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + ", "
|
||||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.NAME + ", "
|
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.NAME + ", "
|
||||||
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", "
|
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", "
|
||||||
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", "
|
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", "
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.thoughtcrime.securesms.database;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -69,6 +70,8 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -144,6 +147,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||||
AttachmentDatabase.CONTENT_TYPE,
|
AttachmentDatabase.CONTENT_TYPE,
|
||||||
AttachmentDatabase.CONTENT_LOCATION,
|
AttachmentDatabase.CONTENT_LOCATION,
|
||||||
AttachmentDatabase.DIGEST,
|
AttachmentDatabase.DIGEST,
|
||||||
|
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||||
AttachmentDatabase.NAME,
|
AttachmentDatabase.NAME,
|
||||||
AttachmentDatabase.TRANSFER_STATE
|
AttachmentDatabase.TRANSFER_STATE
|
||||||
|
@ -694,7 +698,8 @@ public class MmsDatabase extends MessagingDatabase {
|
||||||
databaseAttachment.getLocation(),
|
databaseAttachment.getLocation(),
|
||||||
databaseAttachment.getKey(),
|
databaseAttachment.getKey(),
|
||||||
databaseAttachment.getRelay(),
|
databaseAttachment.getRelay(),
|
||||||
databaseAttachment.getDigest()));
|
databaseAttachment.getDigest(),
|
||||||
|
databaseAttachment.getFastPreflightId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return insertMediaMessage(new MasterSecretUnion(masterSecret),
|
return insertMediaMessage(new MasterSecretUnion(masterSecret),
|
||||||
|
@ -867,7 +872,8 @@ public class MmsDatabase extends MessagingDatabase {
|
||||||
|
|
||||||
public long insertMessageOutbox(@NonNull MasterSecretUnion masterSecret,
|
public long insertMessageOutbox(@NonNull MasterSecretUnion masterSecret,
|
||||||
@NonNull OutgoingMediaMessage message,
|
@NonNull OutgoingMediaMessage message,
|
||||||
long threadId, boolean forceSms)
|
long threadId, boolean forceSms,
|
||||||
|
SmsDatabase.InsertListener insertListener)
|
||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
long type = Types.BASE_SENDING_TYPE;
|
long type = Types.BASE_SENDING_TYPE;
|
||||||
|
@ -924,6 +930,10 @@ public class MmsDatabase extends MessagingDatabase {
|
||||||
long messageId = insertMediaMessage(masterSecret, addresses, message.getBody(),
|
long messageId = insertMediaMessage(masterSecret, addresses, message.getBody(),
|
||||||
message.getAttachments(), contentValues);
|
message.getAttachments(), contentValues);
|
||||||
|
|
||||||
|
if (insertListener != null) {
|
||||||
|
insertListener.onComplete();
|
||||||
|
}
|
||||||
|
|
||||||
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
|
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
|
||||||
jobManager.add(new TrimThreadJob(context, threadId));
|
jobManager.add(new TrimThreadJob(context, threadId));
|
||||||
|
|
||||||
|
@ -1106,6 +1116,10 @@ public class MmsDatabase extends MessagingDatabase {
|
||||||
return new Reader(masterSecret, cursor);
|
return new Reader(masterSecret, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OutgoingMessageReader readerFor(OutgoingMediaMessage message, long threadId) {
|
||||||
|
return new OutgoingMessageReader(message, threadId);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Status {
|
public static class Status {
|
||||||
public static final int DOWNLOAD_INITIALIZED = 1;
|
public static final int DOWNLOAD_INITIALIZED = 1;
|
||||||
public static final int DOWNLOAD_NO_CONNECTIVITY = 2;
|
public static final int DOWNLOAD_NO_CONNECTIVITY = 2;
|
||||||
|
@ -1115,6 +1129,39 @@ public class MmsDatabase extends MessagingDatabase {
|
||||||
public static final int DOWNLOAD_APN_UNAVAILABLE = 6;
|
public static final int DOWNLOAD_APN_UNAVAILABLE = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class OutgoingMessageReader {
|
||||||
|
|
||||||
|
private final OutgoingMediaMessage message;
|
||||||
|
private final long id;
|
||||||
|
private final long threadId;
|
||||||
|
|
||||||
|
public OutgoingMessageReader(OutgoingMediaMessage message, long threadId) {
|
||||||
|
try {
|
||||||
|
this.message = message;
|
||||||
|
this.id = SecureRandom.getInstance("SHA1PRNG").nextLong();
|
||||||
|
this.threadId = threadId;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageRecord getCurrent() {
|
||||||
|
SlideDeck slideDeck = new SlideDeck(context, message.getAttachments());
|
||||||
|
|
||||||
|
return new MediaMmsMessageRecord(context, id, message.getRecipients(),
|
||||||
|
message.getRecipients().getPrimaryRecipient(),
|
||||||
|
1, System.currentTimeMillis(), System.currentTimeMillis(),
|
||||||
|
0, threadId, new DisplayRecord.Body(message.getBody(), true),
|
||||||
|
slideDeck, slideDeck.getSlides().size(),
|
||||||
|
message.isSecure() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
||||||
|
new LinkedList<IdentityKeyMismatch>(),
|
||||||
|
new LinkedList<NetworkFailure>(),
|
||||||
|
message.getSubscriptionId(),
|
||||||
|
message.getExpiresIn(),
|
||||||
|
System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class Reader {
|
public class Reader {
|
||||||
|
|
||||||
private final Cursor cursor;
|
private final Cursor cursor;
|
||||||
|
|
|
@ -97,6 +97,14 @@ public interface MmsSmsColumns {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long getOutgoingEncryptedMessageType() {
|
||||||
|
return Types.BASE_SENDING_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getOutgoingSmsMessageType() {
|
||||||
|
return Types.BASE_SENDING_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isForcedSms(long type) {
|
public static boolean isForcedSms(long type) {
|
||||||
return (type & MESSAGE_FORCE_SMS_BIT) != 0;
|
return (type & MESSAGE_FORCE_SMS_BIT) != 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class MmsSmsDatabase extends Database {
|
||||||
AttachmentDatabase.CONTENT_TYPE,
|
AttachmentDatabase.CONTENT_TYPE,
|
||||||
AttachmentDatabase.CONTENT_LOCATION,
|
AttachmentDatabase.CONTENT_LOCATION,
|
||||||
AttachmentDatabase.DIGEST,
|
AttachmentDatabase.DIGEST,
|
||||||
|
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||||
AttachmentDatabase.NAME,
|
AttachmentDatabase.NAME,
|
||||||
AttachmentDatabase.TRANSFER_STATE};
|
AttachmentDatabase.TRANSFER_STATE};
|
||||||
|
@ -165,6 +166,7 @@ public class MmsSmsDatabase extends Database {
|
||||||
AttachmentDatabase.CONTENT_TYPE,
|
AttachmentDatabase.CONTENT_TYPE,
|
||||||
AttachmentDatabase.CONTENT_LOCATION,
|
AttachmentDatabase.CONTENT_LOCATION,
|
||||||
AttachmentDatabase.DIGEST,
|
AttachmentDatabase.DIGEST,
|
||||||
|
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||||
AttachmentDatabase.NAME,
|
AttachmentDatabase.NAME,
|
||||||
AttachmentDatabase.TRANSFER_STATE};
|
AttachmentDatabase.TRANSFER_STATE};
|
||||||
|
@ -194,6 +196,7 @@ public class MmsSmsDatabase extends Database {
|
||||||
AttachmentDatabase.CONTENT_TYPE,
|
AttachmentDatabase.CONTENT_TYPE,
|
||||||
AttachmentDatabase.CONTENT_LOCATION,
|
AttachmentDatabase.CONTENT_LOCATION,
|
||||||
AttachmentDatabase.DIGEST,
|
AttachmentDatabase.DIGEST,
|
||||||
|
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||||
AttachmentDatabase.NAME,
|
AttachmentDatabase.NAME,
|
||||||
AttachmentDatabase.TRANSFER_STATE};
|
AttachmentDatabase.TRANSFER_STATE};
|
||||||
|
@ -249,6 +252,7 @@ public class MmsSmsDatabase extends Database {
|
||||||
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_TYPE);
|
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_TYPE);
|
||||||
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_LOCATION);
|
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_LOCATION);
|
||||||
mmsColumnsPresent.add(AttachmentDatabase.DIGEST);
|
mmsColumnsPresent.add(AttachmentDatabase.DIGEST);
|
||||||
|
mmsColumnsPresent.add(AttachmentDatabase.FAST_PREFLIGHT_ID);
|
||||||
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_DISPOSITION);
|
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_DISPOSITION);
|
||||||
mmsColumnsPresent.add(AttachmentDatabase.NAME);
|
mmsColumnsPresent.add(AttachmentDatabase.NAME);
|
||||||
mmsColumnsPresent.add(AttachmentDatabase.TRANSFER_STATE);
|
mmsColumnsPresent.add(AttachmentDatabase.TRANSFER_STATE);
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
|
||||||
import org.thoughtcrime.securesms.database.model.DisplayRecord;
|
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.database.model.SmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
@ -46,6 +47,8 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -594,7 +597,8 @@ public class SmsDatabase extends MessagingDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long insertMessageOutbox(long threadId, OutgoingTextMessage message,
|
protected long insertMessageOutbox(long threadId, OutgoingTextMessage message,
|
||||||
long type, boolean forceSms, long date)
|
long type, boolean forceSms, long date,
|
||||||
|
InsertListener insertListener)
|
||||||
{
|
{
|
||||||
if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT;
|
if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT;
|
||||||
else if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
|
else if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
|
||||||
|
@ -623,6 +627,10 @@ public class SmsDatabase extends MessagingDatabase {
|
||||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
|
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
|
||||||
|
|
||||||
|
if (insertListener != null) {
|
||||||
|
insertListener.onComplete();
|
||||||
|
}
|
||||||
|
|
||||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||||
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
|
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
|
||||||
notifyConversationListeners(threadId);
|
notifyConversationListeners(threadId);
|
||||||
|
@ -768,6 +776,37 @@ public class SmsDatabase extends MessagingDatabase {
|
||||||
return new Reader(cursor);
|
return new Reader(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OutgoingMessageReader readerFor(OutgoingTextMessage message, long threadId) {
|
||||||
|
return new OutgoingMessageReader(message, threadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OutgoingMessageReader {
|
||||||
|
|
||||||
|
private final OutgoingTextMessage message;
|
||||||
|
private final long id;
|
||||||
|
private final long threadId;
|
||||||
|
|
||||||
|
public OutgoingMessageReader(OutgoingTextMessage message, long threadId) {
|
||||||
|
try {
|
||||||
|
this.message = message;
|
||||||
|
this.threadId = threadId;
|
||||||
|
this.id = SecureRandom.getInstance("SHA1PRNG").nextLong();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageRecord getCurrent() {
|
||||||
|
return new SmsMessageRecord(context, id, new DisplayRecord.Body(message.getMessageBody(), true),
|
||||||
|
message.getRecipients(), message.getRecipients().getPrimaryRecipient(),
|
||||||
|
1, System.currentTimeMillis(), System.currentTimeMillis(),
|
||||||
|
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
|
||||||
|
threadId, 0, new LinkedList<IdentityKeyMismatch>(),
|
||||||
|
message.getSubscriptionId(), message.getExpiresIn(),
|
||||||
|
System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class Reader {
|
public class Reader {
|
||||||
|
|
||||||
private final Cursor cursor;
|
private final Cursor cursor;
|
||||||
|
@ -858,4 +897,8 @@ public class SmsDatabase extends MessagingDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface InsertListener {
|
||||||
|
public void onComplete();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ public class GroupManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);
|
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);
|
||||||
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false);
|
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null);
|
||||||
|
|
||||||
return new GroupActionResult(groupRecipient, threadId);
|
return new GroupActionResult(groupRecipient, threadId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,7 +205,7 @@ public class GroupMessageProcessor {
|
||||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(group.getGroupId()), false);
|
Recipients recipients = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(group.getGroupId()), false);
|
||||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp(), 0);
|
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp(), 0);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||||
long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false);
|
long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false, null);
|
||||||
|
|
||||||
mmsDatabase.markAsSent(messageId, true);
|
mmsDatabase.markAsSent(messageId, true);
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,7 @@ public class PushDecryptJob extends ContextJob {
|
||||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||||
|
|
||||||
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingEndSessionMessage,
|
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingEndSessionMessage,
|
||||||
false, message.getTimestamp());
|
false, message.getTimestamp(), null);
|
||||||
database.markAsSent(messageId, true);
|
database.markAsSent(messageId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +521,7 @@ public class PushDecryptJob extends ContextJob {
|
||||||
message.getMessage().getExpiresInSeconds() * 1000);
|
message.getMessage().getExpiresInSeconds() * 1000);
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||||
long messageId = database.insertMessageOutbox(masterSecret, expirationUpdateMessage, threadId, false);
|
long messageId = database.insertMessageOutbox(masterSecret, expirationUpdateMessage, threadId, false, null);
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
database.markAsSent(messageId, true);
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ public class PushDecryptJob extends ContextJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||||
long messageId = database.insertMessageOutbox(masterSecret, mediaMessage, threadId, false);
|
long messageId = database.insertMessageOutbox(masterSecret, mediaMessage, threadId, false, null);
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
database.markAsSent(messageId, true);
|
||||||
|
|
||||||
|
@ -635,7 +635,7 @@ public class PushDecryptJob extends ContextJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||||
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingTextMessage, false, message.getTimestamp());
|
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingTextMessage, false, message.getTimestamp(), null);
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
database.markAsSent(messageId, true);
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
|
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
|
||||||
import com.google.android.gms.common.GooglePlayServicesRepairableException;
|
import com.google.android.gms.common.GooglePlayServicesRepairableException;
|
||||||
import com.google.android.gms.location.places.ui.PlacePicker;
|
import com.google.android.gms.location.places.ui.PlacePicker;
|
||||||
|
@ -109,8 +110,10 @@ public class AttachmentManager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear(boolean animate) {
|
||||||
if (attachmentViewStub.resolved()) {
|
if (attachmentViewStub.resolved()) {
|
||||||
|
|
||||||
|
if (animate) {
|
||||||
ViewUtil.fadeOut(attachmentViewStub.get(), 200).addListener(new Listener<Boolean>() {
|
ViewUtil.fadeOut(attachmentViewStub.get(), 200).addListener(new Listener<Boolean>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Boolean result) {
|
public void onSuccess(Boolean result) {
|
||||||
|
@ -123,6 +126,11 @@ public class AttachmentManager {
|
||||||
public void onFailure(ExecutionException e) {
|
public void onFailure(ExecutionException e) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
thumbnail.clear();
|
||||||
|
attachmentViewStub.get().setVisibility(View.GONE);
|
||||||
|
attachmentListener.onAttachmentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
markGarbage(getSlideUri());
|
markGarbage(getSlideUri());
|
||||||
slide = Optional.absent();
|
slide = Optional.absent();
|
||||||
|
@ -413,7 +421,7 @@ public class AttachmentManager {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
cleanup();
|
cleanup();
|
||||||
clear();
|
clear(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class AudioSlide extends Slide {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioSlide(Context context, Uri uri, long dataSize, String contentType) {
|
public AudioSlide(Context context, Uri uri, long dataSize, String contentType) {
|
||||||
super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, null));
|
super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioSlide(Context context, Attachment attachment) {
|
public AudioSlide(Context context, Attachment attachment) {
|
||||||
|
|
|
@ -30,6 +30,9 @@ import org.thoughtcrime.securesms.util.MediaUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
public abstract class Slide {
|
public abstract class Slide {
|
||||||
|
|
||||||
protected final Attachment attachment;
|
protected final Attachment attachment;
|
||||||
|
@ -65,6 +68,11 @@ public abstract class Slide {
|
||||||
return Optional.fromNullable(attachment.getFileName());
|
return Optional.fromNullable(attachment.getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getFastPreflightId() {
|
||||||
|
return attachment.getFastPreflightId();
|
||||||
|
}
|
||||||
|
|
||||||
public long getFileSize() {
|
public long getFileSize() {
|
||||||
return attachment.getSize();
|
return attachment.getSize();
|
||||||
}
|
}
|
||||||
|
@ -127,12 +135,18 @@ public abstract class Slide {
|
||||||
boolean hasThumbnail,
|
boolean hasThumbnail,
|
||||||
@Nullable String fileName)
|
@Nullable String fileName)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
Optional<String> resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri));
|
Optional<String> resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri));
|
||||||
return new UriAttachment(uri, hasThumbnail ? uri : null, resolvedType.or(defaultMime), AttachmentDatabase.TRANSFER_PROGRESS_STARTED, size, fileName);
|
String fastPreflightId = String.valueOf(SecureRandom.getInstance("SHA1PRNG").nextLong());
|
||||||
|
return new UriAttachment(uri, hasThumbnail ? uri : null, resolvedType.or(defaultMime), AttachmentDatabase.TRANSFER_PROGRESS_STARTED, size, fileName, fastPreflightId);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
|
if (other == null) return false;
|
||||||
if (!(other instanceof Slide)) return false;
|
if (!(other instanceof Slide)) return false;
|
||||||
|
|
||||||
Slide that = (Slide)other;
|
Slide that = (Slide)other;
|
||||||
|
|
|
@ -81,13 +81,13 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
|
||||||
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
|
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
|
||||||
|
|
||||||
if (recipients.isGroupRecipient()) {
|
if (recipients.isGroupRecipient()) {
|
||||||
Log.i("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
|
Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
|
||||||
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
|
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
|
||||||
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false);
|
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false, null);
|
||||||
} else {
|
} else {
|
||||||
Log.i("AndroidAutoReplyReceiver", "Sending regular message ");
|
Log.w("AndroidAutoReplyReceiver", "Sending regular message ");
|
||||||
OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), expiresIn, subscriptionId);
|
OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), expiresIn, subscriptionId);
|
||||||
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false);
|
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(replyThreadId, true);
|
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(replyThreadId, true);
|
||||||
|
|
|
@ -74,10 +74,10 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
|
||||||
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false);
|
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false);
|
||||||
if (recipients.isGroupRecipient()) {
|
if (recipients.isGroupRecipient()) {
|
||||||
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
|
OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList<Attachment>(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
|
||||||
threadId = MessageSender.send(context, masterSecret, reply, -1, false);
|
threadId = MessageSender.send(context, masterSecret, reply, -1, false, null);
|
||||||
} else {
|
} else {
|
||||||
OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), expiresIn, subscriptionId);
|
OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), expiresIn, subscriptionId);
|
||||||
threadId = MessageSender.send(context, masterSecret, reply, -1, false);
|
threadId = MessageSender.send(context, masterSecret, reply, -1, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, true);
|
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId, true);
|
||||||
|
|
|
@ -60,10 +60,10 @@ public class QuickResponseService extends MasterSecretIntentService {
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(content)) {
|
if (!TextUtils.isEmpty(content)) {
|
||||||
if (recipients.isSingleRecipient()) {
|
if (recipients.isSingleRecipient()) {
|
||||||
MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipients, content, expiresIn, subscriptionId), -1, false);
|
MessageSender.send(this, masterSecret, new OutgoingTextMessage(recipients, content, expiresIn, subscriptionId), -1, false, null);
|
||||||
} else {
|
} else {
|
||||||
MessageSender.send(this, masterSecret, new OutgoingMediaMessage(recipients, new SlideDeck(), content, System.currentTimeMillis(),
|
MessageSender.send(this, masterSecret, new OutgoingMediaMessage(recipients, new SlideDeck(), content, System.currentTimeMillis(),
|
||||||
subscriptionId, expiresIn, ThreadDatabase.DistributionTypes.DEFAULT), -1, false);
|
subscriptionId, expiresIn, ThreadDatabase.DistributionTypes.DEFAULT), -1, false, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
||||||
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
|
@ -61,7 +62,8 @@ public class MessageSender {
|
||||||
final MasterSecret masterSecret,
|
final MasterSecret masterSecret,
|
||||||
final OutgoingTextMessage message,
|
final OutgoingTextMessage message,
|
||||||
final long threadId,
|
final long threadId,
|
||||||
final boolean forceSms)
|
final boolean forceSms,
|
||||||
|
final SmsDatabase.InsertListener insertListener)
|
||||||
{
|
{
|
||||||
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||||
Recipients recipients = message.getRecipients();
|
Recipients recipients = message.getRecipients();
|
||||||
|
@ -76,7 +78,7 @@ public class MessageSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), allocatedThreadId,
|
long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), allocatedThreadId,
|
||||||
message, forceSms, System.currentTimeMillis());
|
message, forceSms, System.currentTimeMillis(), insertListener);
|
||||||
|
|
||||||
sendTextMessage(context, recipients, forceSms, keyExchange, messageId, message.getExpiresIn());
|
sendTextMessage(context, recipients, forceSms, keyExchange, messageId, message.getExpiresIn());
|
||||||
|
|
||||||
|
@ -87,7 +89,8 @@ public class MessageSender {
|
||||||
final MasterSecret masterSecret,
|
final MasterSecret masterSecret,
|
||||||
final OutgoingMediaMessage message,
|
final OutgoingMediaMessage message,
|
||||||
final long threadId,
|
final long threadId,
|
||||||
final boolean forceSms)
|
final boolean forceSms,
|
||||||
|
final SmsDatabase.InsertListener insertListener)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
|
@ -102,7 +105,7 @@ public class MessageSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipients recipients = message.getRecipients();
|
Recipients recipients = message.getRecipients();
|
||||||
long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), message, allocatedThreadId, forceSms);
|
long messageId = database.insertMessageOutbox(new MasterSecretUnion(masterSecret), message, allocatedThreadId, forceSms, insertListener);
|
||||||
|
|
||||||
sendMediaMessage(context, masterSecret, recipients, forceSms, messageId, message.getExpiresIn());
|
sendMediaMessage(context, masterSecret, recipients, forceSms, messageId, message.getExpiresIn());
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue