mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Improve UI send latency
// FREEBIE
This commit is contained in:
parent
4d889a45e2
commit
cb670d6783
@ -102,6 +102,7 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientPreferenceEvent;
|
||||
import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
||||
@ -274,7 +275,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
if (!Util.isEmpty(composeText) || attachmentManager.isAttachmentPresent()) {
|
||||
saveDraft();
|
||||
attachmentManager.clear();
|
||||
attachmentManager.clear(false);
|
||||
composeText.setText("");
|
||||
}
|
||||
|
||||
@ -566,7 +567,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
recipients.setExpireMessages(expirationTime);
|
||||
|
||||
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;
|
||||
}
|
||||
@ -692,7 +693,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
|
||||
@Override
|
||||
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
|
||||
@ -740,7 +741,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
.build();
|
||||
|
||||
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));
|
||||
initializeEnabledCheck();
|
||||
} catch (IOException e) {
|
||||
@ -1511,13 +1512,19 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessage);
|
||||
}
|
||||
|
||||
attachmentManager.clear();
|
||||
attachmentManager.clear(false);
|
||||
composeText.setText("");
|
||||
final long id = fragment.stageOutgoingMessage(outgoingMessage);
|
||||
|
||||
new AsyncTask<OutgoingMediaMessage, Void, Long>() {
|
||||
@Override
|
||||
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
|
||||
@ -1543,11 +1550,17 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
this.composeText.setText("");
|
||||
final long id = fragment.stageOutgoingMessage(message);
|
||||
|
||||
new AsyncTask<OutgoingTextMessage, Void, Long>() {
|
||||
@Override
|
||||
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
|
||||
|
@ -33,12 +33,14 @@ import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
|
||||
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.FastCursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.util.Conversions;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
@ -67,7 +69,7 @@ import java.util.Set;
|
||||
*
|
||||
*/
|
||||
public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
extends CursorRecyclerViewAdapter<ConversationAdapter.ViewHolder>
|
||||
extends FastCursorRecyclerViewAdapter<ConversationAdapter.ViewHolder, MessageRecord>
|
||||
implements StickyHeaderDecoration.StickyHeaderAdapter<HeaderViewHolder>
|
||||
{
|
||||
|
||||
@ -179,14 +181,13 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
@Override
|
||||
public void changeCursor(Cursor cursor) {
|
||||
messageRecordCache.clear();
|
||||
super.cleanFastRecords();
|
||||
super.changeCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
||||
long start = System.currentTimeMillis();
|
||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
||||
|
||||
protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) {
|
||||
long start = System.currentTimeMillis();
|
||||
viewHolder.getView().bind(masterSecret, messageRecord, locale, batchSelected, recipients);
|
||||
Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start));
|
||||
}
|
||||
@ -237,9 +238,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(@NonNull Cursor cursor) {
|
||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
||||
|
||||
public int getItemViewType(@NonNull MessageRecord messageRecord) {
|
||||
if (messageRecord.isGroupAction() || messageRecord.isCallLog() || messageRecord.isJoined() ||
|
||||
messageRecord.isExpirationTimerUpdate() || messageRecord.isEndSession() || messageRecord.isIdentityUpdate()) {
|
||||
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
|
||||
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 byte[] bytes = digest.digest(unique.getBytes());
|
||||
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));
|
||||
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
|
||||
|
||||
@ -293,8 +317,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
int count = getItemCount();
|
||||
|
||||
for (int i=0;i<count;i++) {
|
||||
Cursor cursor = getCursorAtPositionOrThrow(i);
|
||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
||||
MessageRecord messageRecord = getRecordForPositionOrThrow(i);
|
||||
|
||||
if (messageRecord.isOutgoing() || messageRecord.getDateReceived() <= lastSeen) {
|
||||
return i;
|
||||
@ -339,8 +362,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
if (position >= getItemCount()) return -1;
|
||||
if (position < 0) return -1;
|
||||
|
||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||
MessageRecord record = getMessageRecord(cursor);
|
||||
MessageRecord record = getRecordForPositionOrThrow(position);
|
||||
|
||||
calendar.setTime(new Date(record.getDateSent()));
|
||||
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 < 0) return 0;
|
||||
|
||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||
MessageRecord messageRecord = getMessageRecord(cursor);
|
||||
MessageRecord messageRecord = getRecordForPositionOrThrow(position);
|
||||
|
||||
if (messageRecord.isOutgoing()) return 0;
|
||||
else return messageRecord.getDateReceived();
|
||||
@ -371,8 +392,8 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
|
||||
|
||||
@Override
|
||||
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
|
||||
Cursor cursor = getCursorAtPositionOrThrow(position);
|
||||
viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, getMessageRecord(cursor).getDateReceived()));
|
||||
MessageRecord messageRecord = getRecordForPositionOrThrow(position);
|
||||
viewHolder.setText(DateUtils.getRelativeDate(getContext(), locale, messageRecord.getDateReceived()));
|
||||
}
|
||||
|
||||
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.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||
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.model.DisplayRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||
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.SlideDeck;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
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) {
|
||||
if (lastSeenPosition > 0) {
|
||||
list.post(new Runnable() {
|
||||
|
@ -236,7 +236,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
Optional<RecipientsPreferences> preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipients.getIds());
|
||||
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) {
|
||||
DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true);
|
||||
|
@ -28,18 +28,22 @@ public abstract class Attachment {
|
||||
@Nullable
|
||||
private final byte[] digest;
|
||||
|
||||
@Nullable
|
||||
private final String fastPreflightId;
|
||||
|
||||
public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName,
|
||||
@Nullable String location, @Nullable String key, @Nullable String relay,
|
||||
@Nullable byte[] digest)
|
||||
@Nullable byte[] digest, @Nullable String fastPreflightId)
|
||||
{
|
||||
this.contentType = contentType;
|
||||
this.transferState = transferState;
|
||||
this.size = size;
|
||||
this.fileName = fileName;
|
||||
this.location = location;
|
||||
this.key = key;
|
||||
this.relay = relay;
|
||||
this.digest = digest;
|
||||
this.contentType = contentType;
|
||||
this.transferState = transferState;
|
||||
this.size = size;
|
||||
this.fileName = fileName;
|
||||
this.location = location;
|
||||
this.key = key;
|
||||
this.relay = relay;
|
||||
this.digest = digest;
|
||||
this.fastPreflightId = fastPreflightId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -90,4 +94,9 @@ public abstract class Attachment {
|
||||
public byte[] getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFastPreflightId() {
|
||||
return fastPreflightId;
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,9 @@ public class DatabaseAttachment extends Attachment {
|
||||
boolean hasData, boolean hasThumbnail,
|
||||
String contentType, int transferProgress, long size,
|
||||
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.hasData = hasData;
|
||||
this.hasThumbnail = hasThumbnail;
|
||||
|
@ -10,7 +10,7 @@ import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
public class MmsNotificationAttachment extends Attachment {
|
||||
|
||||
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
|
||||
|
@ -20,7 +20,7 @@ public class PointerAttachment extends Attachment {
|
||||
@NonNull String key, @NonNull String relay,
|
||||
@Nullable byte[] digest)
|
||||
{
|
||||
super(contentType, transferState, size, fileName, location, key, relay, digest);
|
||||
super(contentType, transferState, size, fileName, location, key, relay, digest, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -12,14 +12,14 @@ public class UriAttachment extends Attachment {
|
||||
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
|
||||
@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,
|
||||
@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.thumbnailUri = thumbnailUri;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class ThumbnailView extends FrameLayout {
|
||||
|
||||
if (attrs != null) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -120,13 +120,22 @@ public class ThumbnailView extends FrameLayout {
|
||||
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()) {
|
||||
Log.w(TAG, "Not loading slide, context is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -86,6 +86,7 @@ public class AttachmentDatabase extends Database {
|
||||
static final String THUMBNAIL_ASPECT_RATIO = "aspect_ratio";
|
||||
static final String UNIQUE_ID = "unique_id";
|
||||
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_STARTED = 1;
|
||||
@ -98,7 +99,7 @@ public class AttachmentDatabase extends Database {
|
||||
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
|
||||
CONTENT_LOCATION, DATA, THUMBNAIL, TRANSFER_STATE,
|
||||
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, " +
|
||||
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
|
||||
@ -108,7 +109,7 @@ public class AttachmentDatabase extends Database {
|
||||
"ctt_t" + " TEXT, " + "encrypted" + " INTEGER, " +
|
||||
TRANSFER_STATE + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " +
|
||||
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 = {
|
||||
"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(DIGEST, (byte[])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) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
@ -334,7 +336,8 @@ public class AttachmentDatabase extends Database {
|
||||
databaseAttachment.getLocation(),
|
||||
databaseAttachment.getKey(),
|
||||
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_DISPOSITION)),
|
||||
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(FILE_NAME, fileName);
|
||||
contentValues.put(SIZE, attachment.getSize());
|
||||
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
|
||||
|
||||
if (partData != null) {
|
||||
contentValues.put(DATA, partData.first.getAbsolutePath());
|
||||
|
@ -115,6 +115,7 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||
if (!isActiveCursor()) return 0;
|
||||
|
||||
return cursor.getCount()
|
||||
+ getFastAccessSize()
|
||||
+ (hasHeaderView() ? 1 : 0)
|
||||
+ (hasFooterView() ? 1 : 0);
|
||||
}
|
||||
@ -144,16 +145,22 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||
@Override
|
||||
public final void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int 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);
|
||||
|
||||
protected void onBindFastAccessItemViewHolder(VH viewHolder, int position) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getItemViewType(int position) {
|
||||
if (isHeaderPosition(position)) return HEADER_TYPE;
|
||||
if (isFooterPosition(position)) return FOOTER_TYPE;
|
||||
if (isHeaderPosition(position)) return HEADER_TYPE;
|
||||
if (isFooterPosition(position)) return FOOTER_TYPE;
|
||||
if (isFastAccessPosition(position)) return getFastAccessItemViewType(position);
|
||||
return getItemViewType(getCursorAtPositionOrThrow(position));
|
||||
}
|
||||
|
||||
@ -163,8 +170,9 @@ public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHold
|
||||
|
||||
@Override
|
||||
public final long getItemId(int position) {
|
||||
if (isHeaderPosition(position)) return HEADER_ID;
|
||||
if (isFooterPosition(position)) return FOOTER_ID;
|
||||
if (isHeaderPosition(position)) return HEADER_ID;
|
||||
if (isFooterPosition(position)) return FOOTER_ID;
|
||||
if (isFastAccessPosition(position)) return getFastAccessItemId(position);
|
||||
long itemId = getItemId(getCursorAtPositionOrThrow(position));
|
||||
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) {
|
||||
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 {
|
||||
|
@ -77,7 +77,8 @@ public class DatabaseFactory {
|
||||
private static final int INTRODUCED_DIGEST = 30;
|
||||
private static final int INTRODUCED_NOTIFIED = 31;
|
||||
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 Object lock = new Object();
|
||||
@ -858,6 +859,10 @@ public class DatabaseFactory {
|
||||
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.endTransaction();
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public class EncryptingSmsDatabase extends SmsDatabase {
|
||||
|
||||
public long insertMessageOutbox(MasterSecretUnion masterSecret, long threadId,
|
||||
OutgoingTextMessage message, boolean forceSms,
|
||||
long timestamp)
|
||||
long timestamp, InsertListener insertListener)
|
||||
{
|
||||
long type = Types.BASE_SENDING_TYPE;
|
||||
|
||||
@ -76,7 +76,7 @@ public class EncryptingSmsDatabase extends SmsDatabase {
|
||||
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,
|
||||
|
@ -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_DISPOSITION + ", "
|
||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DIGEST + ", "
|
||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + ", "
|
||||
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.NAME + ", "
|
||||
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", "
|
||||
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", "
|
||||
|
@ -19,6 +19,7 @@ package org.thoughtcrime.securesms.database;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.net.Uri;
|
||||
@ -69,6 +70,8 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -144,6 +147,7 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
AttachmentDatabase.CONTENT_TYPE,
|
||||
AttachmentDatabase.CONTENT_LOCATION,
|
||||
AttachmentDatabase.DIGEST,
|
||||
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||
AttachmentDatabase.NAME,
|
||||
AttachmentDatabase.TRANSFER_STATE
|
||||
@ -694,7 +698,8 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
databaseAttachment.getLocation(),
|
||||
databaseAttachment.getKey(),
|
||||
databaseAttachment.getRelay(),
|
||||
databaseAttachment.getDigest()));
|
||||
databaseAttachment.getDigest(),
|
||||
databaseAttachment.getFastPreflightId()));
|
||||
}
|
||||
|
||||
return insertMediaMessage(new MasterSecretUnion(masterSecret),
|
||||
@ -867,7 +872,8 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
|
||||
public long insertMessageOutbox(@NonNull MasterSecretUnion masterSecret,
|
||||
@NonNull OutgoingMediaMessage message,
|
||||
long threadId, boolean forceSms)
|
||||
long threadId, boolean forceSms,
|
||||
SmsDatabase.InsertListener insertListener)
|
||||
throws MmsException
|
||||
{
|
||||
long type = Types.BASE_SENDING_TYPE;
|
||||
@ -924,6 +930,10 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
long messageId = insertMediaMessage(masterSecret, addresses, message.getBody(),
|
||||
message.getAttachments(), contentValues);
|
||||
|
||||
if (insertListener != null) {
|
||||
insertListener.onComplete();
|
||||
}
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
|
||||
jobManager.add(new TrimThreadJob(context, threadId));
|
||||
|
||||
@ -1106,6 +1116,10 @@ public class MmsDatabase extends MessagingDatabase {
|
||||
return new Reader(masterSecret, cursor);
|
||||
}
|
||||
|
||||
public OutgoingMessageReader readerFor(OutgoingMediaMessage message, long threadId) {
|
||||
return new OutgoingMessageReader(message, threadId);
|
||||
}
|
||||
|
||||
public static class Status {
|
||||
public static final int DOWNLOAD_INITIALIZED = 1;
|
||||
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 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 {
|
||||
|
||||
private final Cursor cursor;
|
||||
|
@ -97,6 +97,14 @@ public interface MmsSmsColumns {
|
||||
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) {
|
||||
return (type & MESSAGE_FORCE_SMS_BIT) != 0;
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ public class MmsSmsDatabase extends Database {
|
||||
AttachmentDatabase.CONTENT_TYPE,
|
||||
AttachmentDatabase.CONTENT_LOCATION,
|
||||
AttachmentDatabase.DIGEST,
|
||||
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||
AttachmentDatabase.NAME,
|
||||
AttachmentDatabase.TRANSFER_STATE};
|
||||
@ -165,6 +166,7 @@ public class MmsSmsDatabase extends Database {
|
||||
AttachmentDatabase.CONTENT_TYPE,
|
||||
AttachmentDatabase.CONTENT_LOCATION,
|
||||
AttachmentDatabase.DIGEST,
|
||||
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||
AttachmentDatabase.NAME,
|
||||
AttachmentDatabase.TRANSFER_STATE};
|
||||
@ -194,6 +196,7 @@ public class MmsSmsDatabase extends Database {
|
||||
AttachmentDatabase.CONTENT_TYPE,
|
||||
AttachmentDatabase.CONTENT_LOCATION,
|
||||
AttachmentDatabase.DIGEST,
|
||||
AttachmentDatabase.FAST_PREFLIGHT_ID,
|
||||
AttachmentDatabase.CONTENT_DISPOSITION,
|
||||
AttachmentDatabase.NAME,
|
||||
AttachmentDatabase.TRANSFER_STATE};
|
||||
@ -249,6 +252,7 @@ public class MmsSmsDatabase extends Database {
|
||||
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_TYPE);
|
||||
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_LOCATION);
|
||||
mmsColumnsPresent.add(AttachmentDatabase.DIGEST);
|
||||
mmsColumnsPresent.add(AttachmentDatabase.FAST_PREFLIGHT_ID);
|
||||
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_DISPOSITION);
|
||||
mmsColumnsPresent.add(AttachmentDatabase.NAME);
|
||||
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.IdentityKeyMismatchList;
|
||||
import org.thoughtcrime.securesms.database.model.DisplayRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@ -46,6 +47,8 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -594,7 +597,8 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
}
|
||||
|
||||
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;
|
||||
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();
|
||||
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
|
||||
|
||||
if (insertListener != null) {
|
||||
insertListener.onComplete();
|
||||
}
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
DatabaseFactory.getThreadDatabase(context).setLastSeen(threadId);
|
||||
notifyConversationListeners(threadId);
|
||||
@ -768,6 +776,37 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
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 {
|
||||
|
||||
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);
|
||||
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false);
|
||||
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null);
|
||||
|
||||
return new GroupActionResult(groupRecipient, threadId);
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ public class GroupMessageProcessor {
|
||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(group.getGroupId()), false);
|
||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipients, storage, null, envelope.getTimestamp(), 0);
|
||||
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);
|
||||
|
||||
|
@ -321,7 +321,7 @@ public class PushDecryptJob extends ContextJob {
|
||||
SecurityEvent.broadcastSecurityUpdateEvent(context);
|
||||
|
||||
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingEndSessionMessage,
|
||||
false, message.getTimestamp());
|
||||
false, message.getTimestamp(), null);
|
||||
database.markAsSent(messageId, true);
|
||||
}
|
||||
|
||||
@ -521,7 +521,7 @@ public class PushDecryptJob extends ContextJob {
|
||||
message.getMessage().getExpiresInSeconds() * 1000);
|
||||
|
||||
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);
|
||||
|
||||
@ -554,7 +554,7 @@ public class PushDecryptJob extends ContextJob {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -635,7 +635,7 @@ public class PushDecryptJob extends ContextJob {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -35,6 +35,7 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
|
||||
import com.google.android.gms.common.GooglePlayServicesRepairableException;
|
||||
import com.google.android.gms.location.places.ui.PlacePicker;
|
||||
@ -109,20 +110,27 @@ public class AttachmentManager {
|
||||
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
public void clear(boolean animate) {
|
||||
if (attachmentViewStub.resolved()) {
|
||||
ViewUtil.fadeOut(attachmentViewStub.get(), 200).addListener(new Listener<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
thumbnail.clear();
|
||||
attachmentViewStub.get().setVisibility(View.GONE);
|
||||
attachmentListener.onAttachmentChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ExecutionException e) {
|
||||
}
|
||||
});
|
||||
if (animate) {
|
||||
ViewUtil.fadeOut(attachmentViewStub.get(), 200).addListener(new Listener<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
thumbnail.clear();
|
||||
attachmentViewStub.get().setVisibility(View.GONE);
|
||||
attachmentListener.onAttachmentChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ExecutionException e) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
thumbnail.clear();
|
||||
attachmentViewStub.get().setVisibility(View.GONE);
|
||||
attachmentListener.onAttachmentChanged();
|
||||
}
|
||||
|
||||
markGarbage(getSlideUri());
|
||||
slide = Optional.absent();
|
||||
@ -413,7 +421,7 @@ public class AttachmentManager {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
cleanup();
|
||||
clear();
|
||||
clear(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ public class AudioSlide extends Slide {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -30,6 +30,9 @@ import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public abstract class Slide {
|
||||
|
||||
protected final Attachment attachment;
|
||||
@ -65,6 +68,11 @@ public abstract class Slide {
|
||||
return Optional.fromNullable(attachment.getFileName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFastPreflightId() {
|
||||
return attachment.getFastPreflightId();
|
||||
}
|
||||
|
||||
public long getFileSize() {
|
||||
return attachment.getSize();
|
||||
}
|
||||
@ -127,12 +135,18 @@ public abstract class Slide {
|
||||
boolean hasThumbnail,
|
||||
@Nullable String fileName)
|
||||
{
|
||||
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);
|
||||
try {
|
||||
Optional<String> resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri));
|
||||
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
|
||||
public boolean equals(Object other) {
|
||||
if (other == null) return false;
|
||||
if (!(other instanceof Slide)) return false;
|
||||
|
||||
Slide that = (Slide)other;
|
||||
|
@ -81,13 +81,13 @@ public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
|
||||
long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
|
||||
|
||||
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);
|
||||
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false);
|
||||
replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false, null);
|
||||
} else {
|
||||
Log.i("AndroidAutoReplyReceiver", "Sending regular message ");
|
||||
Log.w("AndroidAutoReplyReceiver", "Sending regular message ");
|
||||
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);
|
||||
|
@ -74,10 +74,10 @@ public class RemoteReplyReceiver extends MasterSecretBroadcastReceiver {
|
||||
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false);
|
||||
if (recipients.isGroupRecipient()) {
|
||||
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 {
|
||||
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);
|
||||
|
@ -60,10 +60,10 @@ public class QuickResponseService extends MasterSecretIntentService {
|
||||
|
||||
if (!TextUtils.isEmpty(content)) {
|
||||
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 {
|
||||
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) {
|
||||
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
@ -61,7 +62,8 @@ public class MessageSender {
|
||||
final MasterSecret masterSecret,
|
||||
final OutgoingTextMessage message,
|
||||
final long threadId,
|
||||
final boolean forceSms)
|
||||
final boolean forceSms,
|
||||
final SmsDatabase.InsertListener insertListener)
|
||||
{
|
||||
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||
Recipients recipients = message.getRecipients();
|
||||
@ -76,7 +78,7 @@ public class MessageSender {
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
@ -87,7 +89,8 @@ public class MessageSender {
|
||||
final MasterSecret masterSecret,
|
||||
final OutgoingMediaMessage message,
|
||||
final long threadId,
|
||||
final boolean forceSms)
|
||||
final boolean forceSms,
|
||||
final SmsDatabase.InsertListener insertListener)
|
||||
{
|
||||
try {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
@ -102,7 +105,7 @@ public class MessageSender {
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user