Keep track of when audio attachments are voice notes

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2017-05-11 22:46:35 -07:00
parent e96bf2bdc7
commit b78c05e70b
26 changed files with 76 additions and 44 deletions

View File

@@ -1646,7 +1646,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipients.getExpireMessages() * 1000;
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC);
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true);
SlideDeck slideDeck = new SlideDeck();
slideDeck.addSlide(audioSlide);

View File

@@ -31,9 +31,11 @@ public abstract class Attachment {
@Nullable
private final String fastPreflightId;
private final boolean voiceNote;
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 String fastPreflightId)
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote)
{
this.contentType = contentType;
this.transferState = transferState;
@@ -44,6 +46,7 @@ public abstract class Attachment {
this.relay = relay;
this.digest = digest;
this.fastPreflightId = fastPreflightId;
this.voiceNote = voiceNote;
}
@Nullable
@@ -99,4 +102,8 @@ public abstract class Attachment {
public String getFastPreflightId() {
return fastPreflightId;
}
public boolean isVoiceNote() {
return voiceNote;
}
}

View File

@@ -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, String fastPreflightId)
byte[] digest, String fastPreflightId, boolean voiceNote)
{
super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId);
super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;

View File

@@ -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, null);
super("application/mms", getTransferStateFromStatus(status), size, null, null, null, null, null, null, false);
}
@Nullable

View File

@@ -18,9 +18,9 @@ public class PointerAttachment extends Attachment {
public PointerAttachment(@NonNull String contentType, int transferState, long size,
@Nullable String fileName, @NonNull String location,
@NonNull String key, @NonNull String relay,
@Nullable byte[] digest)
@Nullable byte[] digest, boolean voiceNote)
{
super(contentType, transferState, size, fileName, location, key, relay, digest, null);
super(contentType, transferState, size, fileName, location, key, relay, digest, null, voiceNote);
}
@Nullable
@@ -49,7 +49,8 @@ public class PointerAttachment extends Attachment {
pointer.asPointer().getFileName().orNull(),
String.valueOf(pointer.asPointer().getId()),
encryptedKey, pointer.asPointer().getRelay().orNull(),
pointer.asPointer().getDigest().orNull()));
pointer.asPointer().getDigest().orNull(),
pointer.asPointer().getVoiceNote()));
}
}
}

View File

@@ -10,16 +10,17 @@ public class UriAttachment extends Attachment {
private final @Nullable Uri thumbnailUri;
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
@Nullable String fileName)
@Nullable String fileName, boolean voiceNote)
{
this(uri, uri, contentType, transferState, size, fileName, null);
this(uri, uri, contentType, transferState, size, fileName, null, voiceNote);
}
public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
@NonNull String contentType, int transferState, long size,
@Nullable String fileName, @Nullable String fastPreflightId)
@Nullable String fileName, @Nullable String fastPreflightId,
boolean voiceNote)
{
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId);
super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote);
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
}

View File

@@ -81,6 +81,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";
static final String VOICE_NOTE = "voice_note";
public static final String FAST_PREFLIGHT_ID = "fast_preflight_id";
public static final int TRANSFER_PROGRESS_DONE = 0;
@@ -94,7 +95,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, FAST_PREFLIGHT_ID};
UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID, VOICE_NOTE};
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
@@ -104,7 +105,8 @@ 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, " + FAST_PREFLIGHT_ID + " TEXT);";
UNIQUE_ID + " INTEGER NOT NULL, " + DIGEST + " BLOB, " + FAST_PREFLIGHT_ID + " TEXT, " +
VOICE_NOTE + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
@@ -332,7 +334,8 @@ public class AttachmentDatabase extends Database {
databaseAttachment.getKey(),
databaseAttachment.getRelay(),
databaseAttachment.getDigest(),
databaseAttachment.getFastPreflightId());
databaseAttachment.getFastPreflightId(),
databaseAttachment.isVoiceNote());
}
@@ -484,7 +487,8 @@ public class AttachmentDatabase extends Database {
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)),
cursor.getString(cursor.getColumnIndexOrThrow(NAME)),
cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)),
cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)));
cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(VOICE_NOTE)) == 1);
}
@@ -519,6 +523,7 @@ public class AttachmentDatabase extends Database {
contentValues.put(FILE_NAME, fileName);
contentValues.put(SIZE, attachment.getSize());
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
if (partData != null) {
contentValues.put(DATA, partData.first.getAbsolutePath());

View File

@@ -77,7 +77,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_NOTIFIED = 31;
private static final int INTRODUCED_DOCUMENTS = 32;
private static final int INTRODUCED_FAST_PREFLIGHT = 33;
private static final int DATABASE_VERSION = 33;
private static final int INTRODUCED_VOICE_NOTES = 34;
private static final int DATABASE_VERSION = 34;
private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object();
@@ -862,6 +863,10 @@ public class DatabaseFactory {
db.execSQL("ALTER TABLE part ADD COLUMN fast_preflight_id TEXT");
}
if (oldVersion < INTRODUCED_VOICE_NOTES) {
db.execSQL("ALTER TABLE part ADD COLUMN voice_note INTEGER DEFAULT 0");
}
db.setTransactionSuccessful();
db.endTransaction();
}

View File

@@ -27,6 +27,7 @@ public class MediaDatabase extends Database {
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_DISPOSITION + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DIGEST + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.VOICE_NOTE + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.NAME + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", "

View File

@@ -147,6 +147,7 @@ public class MmsDatabase extends MessagingDatabase {
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.DIGEST,
AttachmentDatabase.FAST_PREFLIGHT_ID,
AttachmentDatabase.VOICE_NOTE,
AttachmentDatabase.CONTENT_DISPOSITION,
AttachmentDatabase.NAME,
AttachmentDatabase.TRANSFER_STATE
@@ -691,7 +692,8 @@ public class MmsDatabase extends MessagingDatabase {
databaseAttachment.getKey(),
databaseAttachment.getRelay(),
databaseAttachment.getDigest(),
databaseAttachment.getFastPreflightId()));
databaseAttachment.getFastPreflightId(),
databaseAttachment.isVoiceNote()));
}
return insertMediaMessage(new MasterSecretUnion(masterSecret),

View File

@@ -71,6 +71,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.DIGEST,
AttachmentDatabase.FAST_PREFLIGHT_ID,
AttachmentDatabase.VOICE_NOTE,
AttachmentDatabase.CONTENT_DISPOSITION,
AttachmentDatabase.NAME,
AttachmentDatabase.TRANSFER_STATE};
@@ -167,6 +168,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.DIGEST,
AttachmentDatabase.FAST_PREFLIGHT_ID,
AttachmentDatabase.VOICE_NOTE,
AttachmentDatabase.CONTENT_DISPOSITION,
AttachmentDatabase.NAME,
AttachmentDatabase.TRANSFER_STATE};
@@ -197,6 +199,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.DIGEST,
AttachmentDatabase.FAST_PREFLIGHT_ID,
AttachmentDatabase.VOICE_NOTE,
AttachmentDatabase.CONTENT_DISPOSITION,
AttachmentDatabase.NAME,
AttachmentDatabase.TRANSFER_STATE};
@@ -253,6 +256,7 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_LOCATION);
mmsColumnsPresent.add(AttachmentDatabase.DIGEST);
mmsColumnsPresent.add(AttachmentDatabase.FAST_PREFLIGHT_ID);
mmsColumnsPresent.add(AttachmentDatabase.VOICE_NOTE);
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_DISPOSITION);
mmsColumnsPresent.add(AttachmentDatabase.NAME);
mmsColumnsPresent.add(AttachmentDatabase.TRANSFER_STATE);

View File

@@ -103,7 +103,7 @@ public class GroupManager {
if (avatar != null) {
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false);
}
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);

View File

@@ -158,7 +158,7 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
Log.w(TAG, "Downloading attachment with no digest...");
}
return new SignalServiceAttachmentPointer(id, null, key, relay, Optional.fromNullable(attachment.getDigest()), Optional.fromNullable(attachment.getFileName()));
return new SignalServiceAttachmentPointer(id, null, key, relay, Optional.fromNullable(attachment.getDigest()), Optional.fromNullable(attachment.getFileName()), attachment.isVoiceNote());
} catch (InvalidMessageException | IOException e) {
Log.w(TAG, e);
throw new InvalidPartException(e);

View File

@@ -78,7 +78,7 @@ public class AvatarDownloadJob extends MasterSecretJob implements InjectableType
attachment = File.createTempFile("avatar", "tmp", context.getCacheDir());
attachment.deleteOnExit();
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, relay, digest, fileName);
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, relay, digest, fileName, false);
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE);
Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key), 500, 500);

View File

@@ -199,7 +199,7 @@ public class MmsDownloadJob extends MasterSecretJob {
attachments.add(new UriAttachment(uri, Util.toIsoString(part.getContentType()),
AttachmentDatabase.TRANSFER_PROGRESS_DONE,
part.getData().length, name));
part.getData().length, name, false));
}
}
}

View File

@@ -28,6 +28,7 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
@@ -97,7 +98,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
Optional.fromNullable(recipient.getColor().serialize())));
out.close();
sendUpdate(messageSender, contactDataFile);
sendUpdate(messageSender, contactDataFile, false);
} catch(InvalidNumberException e) {
Log.w(TAG, e);
@@ -126,7 +127,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
}
out.close();
sendUpdate(messageSender, contactDataFile);
sendUpdate(messageSender, contactDataFile, true);
} catch(InvalidNumberException e) {
Log.w(TAG, e);
} finally {
@@ -159,7 +160,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
}
}
private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile)
private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile, boolean complete)
throws IOException, UntrustedIdentityException, NetworkException
{
if (contactsFile.length() > 0) {
@@ -171,7 +172,7 @@ public class MultiDeviceContactUpdateJob extends MasterSecretJob implements Inje
.build();
try {
messageSender.sendMessage(SignalServiceSyncMessage.forContacts(attachmentStream));
messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)));
} catch (IOException ioe) {
throw new NetworkException(ioe);
}

View File

@@ -80,6 +80,7 @@ public abstract class PushSendJob extends SendJob {
.withContentType(attachment.getContentType())
.withLength(attachment.getSize())
.withFileName(attachment.getFileName())
.withVoiceNote(attachment.isVoiceNote())
.withListener(new ProgressListener() {
@Override
public void onAttachmentProgress(long total, long progress) {

View File

@@ -4,6 +4,7 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.attachments.Attachment;
@@ -93,7 +94,9 @@ public class MediaNetworkRequirement implements Requirement, ContextDependent {
boolean isAllowed;
if (isNonDocumentType(contentType)) {
if (attachment.isVoiceNote() || (MediaUtil.isAudio(attachment) && TextUtils.isEmpty(attachment.getFileName()))) {
isAllowed = isConnectedWifi() || isConnectedMobile();
} else if (isNonDocumentType(contentType)) {
isAllowed = allowedTypes.contains(MediaUtil.getDiscreteMimeType(contentType));
} else {
isAllowed = allowedTypes.contains("documents");

View File

@@ -465,7 +465,7 @@ public class AttachmentManager {
switch (this) {
case IMAGE: return new ImageSlide(context, uri, dataSize);
case GIF: return new GifSlide(context, uri, dataSize);
case AUDIO: return new AudioSlide(context, uri, dataSize);
case AUDIO: return new AudioSlide(context, uri, dataSize, false);
case VIDEO: return new VideoSlide(context, uri, dataSize);
case DOCUMENT: return new DocumentSlide(context, uri, mimeType, dataSize, fileName);
default: throw new AssertionError("unrecognized enum");

View File

@@ -33,12 +33,12 @@ import org.thoughtcrime.securesms.util.ResUtil;
public class AudioSlide extends Slide {
public AudioSlide(Context context, Uri uri, long dataSize) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.AUDIO_UNSPECIFIED, dataSize, false, null));
public AudioSlide(Context context, Uri uri, long dataSize, boolean voiceNote) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.AUDIO_UNSPECIFIED, dataSize, false, null, voiceNote));
}
public AudioSlide(Context context, Uri uri, long dataSize, String contentType) {
super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, null, null));
public AudioSlide(Context context, Uri uri, long dataSize, String contentType, boolean voiceNote) {
super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, null, null, voiceNote));
}
public AudioSlide(Context context, Attachment attachment) {

View File

@@ -18,7 +18,7 @@ public class DocumentSlide extends Slide {
@NonNull String contentType, long size,
@Nullable String fileName)
{
super(context, constructAttachmentFromUri(context, uri, contentType, size, true, fileName));
super(context, constructAttachmentFromUri(context, uri, contentType, size, true, fileName, false));
}
@Override

View File

@@ -14,7 +14,7 @@ public class GifSlide extends ImageSlide {
}
public GifSlide(Context context, Uri uri, long size) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_GIF, size, true, null));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_GIF, size, true, null, false));
}
@Override

View File

@@ -35,7 +35,7 @@ public class ImageSlide extends Slide {
}
public ImageSlide(Context context, Uri uri, long size) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_JPEG, size, true, null));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_JPEG, size, true, null, false));
}
@Override

View File

@@ -133,12 +133,13 @@ public abstract class Slide {
@NonNull String defaultMime,
long size,
boolean hasThumbnail,
@Nullable String fileName)
@Nullable String fileName,
boolean voiceNote)
{
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);
return new UriAttachment(uri, hasThumbnail ? uri : null, resolvedType.or(defaultMime), AttachmentDatabase.TRANSFER_PROGRESS_STARTED, size, fileName, fastPreflightId, voiceNote);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}

View File

@@ -30,7 +30,7 @@ import org.thoughtcrime.securesms.util.ResUtil;
public class VideoSlide extends Slide {
public VideoSlide(Context context, Uri uri, long dataSize) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, MediaUtil.hasVideoThumbnail(uri), null));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, MediaUtil.hasVideoThumbnail(uri), null, false));
}
public VideoSlide(Context context, Attachment attachment) {