Support for generating video thumbnails

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2016-12-11 13:37:27 -08:00
parent 6308e6491a
commit a4c17e5325
30 changed files with 268 additions and 108 deletions

View File

@@ -21,7 +21,10 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
@@ -42,6 +45,7 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
import java.io.File;
import java.io.FileNotFoundException;
@@ -54,6 +58,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
public class AttachmentDatabase extends Database {
@@ -71,7 +76,7 @@ public class AttachmentDatabase extends Database {
static final String DATA = "_data";
static final String TRANSFER_STATE = "pending_push";
static final String SIZE = "data_size";
private static final String THUMBNAIL = "thumbnail";
static final String THUMBNAIL = "thumbnail";
static final String THUMBNAIL_ASPECT_RATIO = "aspect_ratio";
static final String UNIQUE_ID = "unique_id";
@@ -84,7 +89,7 @@ public class AttachmentDatabase extends Database {
private static final String[] PROJECTION = new String[] {ROW_ID + " AS " + ATTACHMENT_ID_ALIAS,
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
CONTENT_LOCATION, DATA, TRANSFER_STATE,
CONTENT_LOCATION, DATA, THUMBNAIL, TRANSFER_STATE,
SIZE, THUMBNAIL, THUMBNAIL_ASPECT_RATIO,
UNIQUE_ID};
@@ -313,6 +318,7 @@ public class AttachmentDatabase extends Database {
return new DatabaseAttachment(databaseAttachment.getAttachmentId(),
databaseAttachment.getMmsId(),
databaseAttachment.hasData(),
databaseAttachment.hasThumbnail(),
mediaStream.getMimeType(),
databaseAttachment.getTransferState(),
dataSize,
@@ -434,6 +440,7 @@ public class AttachmentDatabase extends Database {
cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))),
cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)),
!cursor.isNull(cursor.getColumnIndexOrThrow(DATA)),
!cursor.isNull(cursor.getColumnIndexOrThrow(THUMBNAIL)),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_TYPE)),
cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)),
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
@@ -474,11 +481,8 @@ public class AttachmentDatabase extends Database {
long rowId = database.insert(TABLE_NAME, null, contentValues);
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
if (attachment.getThumbnail() != null && masterSecret.getMasterSecret().isPresent()) {
Log.w(TAG, "inserting pre-generated thumbnail");
ThumbnailData data = new ThumbnailData(attachment.getThumbnail());
updateAttachmentThumbnail(masterSecret.getMasterSecret().get(), attachmentId, data.toDataStream(), data.getAspectRatio());
} else if (!attachment.isInProgress()) {
if (partData != null) {
Log.w(TAG, "Submitting thumbnail generation job...");
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), attachmentId));
}
@@ -501,21 +505,33 @@ public class AttachmentDatabase extends Database {
values.put(THUMBNAIL_ASPECT_RATIO, aspectRatio);
database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings());
Cursor cursor = database.query(TABLE_NAME, new String[] {MMS_ID}, PART_ID_WHERE, attachmentId.toStrings(), null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID))));
}
} finally {
if (cursor != null) cursor.close();
}
}
@VisibleForTesting
class ThumbnailFetchCallable implements Callable<InputStream> {
private final MasterSecret masterSecret;
private final AttachmentId attachmentId;
public ThumbnailFetchCallable(MasterSecret masterSecret, AttachmentId attachmentId) {
ThumbnailFetchCallable(MasterSecret masterSecret, AttachmentId attachmentId) {
this.masterSecret = masterSecret;
this.attachmentId = attachmentId;
}
@Override
public @Nullable InputStream call() throws Exception {
Log.w(TAG, "Executing thumbnail job...");
final InputStream stream = getDataStream(masterSecret, attachmentId, THUMBNAIL);
if (stream != null) {
@@ -528,7 +544,13 @@ public class AttachmentDatabase extends Database {
return null;
}
ThumbnailData data = MediaUtil.generateThumbnail(context, masterSecret, attachment.getContentType(), attachment.getDataUri());
ThumbnailData data;
if (ContentType.isVideoType(attachment.getContentType())) {
data = generateVideoThumbnail(masterSecret, attachmentId);
} else{
data = MediaUtil.generateThumbnail(context, masterSecret, attachment.getContentType(), attachment.getDataUri());
}
if (data == null) {
return null;
@@ -538,5 +560,28 @@ public class AttachmentDatabase extends Database {
return getDataStream(masterSecret, attachmentId, THUMBNAIL);
}
private ThumbnailData generateVideoThumbnail(MasterSecret masterSecret, AttachmentId attachmentId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Log.w(TAG, "Video thumbnails not supported...");
return null;
}
File mediaFile = getAttachmentDataFile(attachmentId, DATA);
if (mediaFile == null) {
Log.w(TAG, "No data file found for video thumbnail...");
return null;
}
EncryptedMediaDataSource dataSource = new EncryptedMediaDataSource(masterSecret, mediaFile);
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(dataSource);
Bitmap bitmap = retriever.getFrameAtTime(1000);
Log.w(TAG, "Generated video thumbnail...");
return new ThumbnailData(bitmap);
}
}
}

View File

@@ -19,6 +19,7 @@ public class ImageDatabase extends Database {
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.TRANSFER_STATE + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.SIZE + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DATA + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.THUMBNAIL + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.MESSAGE_BOX + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + ", "
+ MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + ", "
@@ -47,19 +48,22 @@ public class ImageDatabase extends Database {
private final AttachmentId attachmentId;
private final long mmsId;
private final boolean hasData;
private final boolean hasThumbnail;
private final String contentType;
private final String address;
private final long date;
private final int transferState;
private final long size;
private ImageRecord(AttachmentId attachmentId, long mmsId, boolean hasData,
private ImageRecord(AttachmentId attachmentId, long mmsId,
boolean hasData, boolean hasThumbnail,
String contentType, String address, long date,
int transferState, long size)
{
this.attachmentId = attachmentId;
this.mmsId = mmsId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;
this.contentType = contentType;
this.address = address;
this.date = date;
@@ -82,6 +86,7 @@ public class ImageDatabase extends Database {
return new ImageRecord(attachmentId,
cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID)),
!cursor.isNull(cursor.getColumnIndexOrThrow(AttachmentDatabase.DATA)),
!cursor.isNull(cursor.getColumnIndexOrThrow(AttachmentDatabase.THUMBNAIL)),
cursor.getString(cursor.getColumnIndexOrThrow(AttachmentDatabase.CONTENT_TYPE)),
cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)),
date,
@@ -90,7 +95,7 @@ public class ImageDatabase extends Database {
}
public Attachment getAttachment() {
return new DatabaseAttachment(attachmentId, mmsId, hasData, contentType, transferState, size, null, null, null);
return new DatabaseAttachment(attachmentId, mmsId, hasData, hasThumbnail, contentType, transferState, size, null, null, null);
}
public String getContentType() {

View File

@@ -138,6 +138,7 @@ public class MmsDatabase extends MessagingDatabase {
AttachmentDatabase.MMS_ID,
AttachmentDatabase.SIZE,
AttachmentDatabase.DATA,
AttachmentDatabase.THUMBNAIL,
AttachmentDatabase.CONTENT_TYPE,
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.CONTENT_DISPOSITION,
@@ -672,6 +673,7 @@ public class MmsDatabase extends MessagingDatabase {
attachments.add(new DatabaseAttachment(databaseAttachment.getAttachmentId(),
databaseAttachment.getMmsId(),
databaseAttachment.hasData(),
databaseAttachment.hasThumbnail(),
databaseAttachment.getContentType(),
AttachmentDatabase.TRANSFER_PROGRESS_DONE,
databaseAttachment.getSize(),

View File

@@ -62,6 +62,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.MMS_ID,
AttachmentDatabase.SIZE,
AttachmentDatabase.DATA,
AttachmentDatabase.THUMBNAIL,
AttachmentDatabase.CONTENT_TYPE,
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.CONTENT_DISPOSITION,
@@ -153,6 +154,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.MMS_ID,
AttachmentDatabase.SIZE,
AttachmentDatabase.DATA,
AttachmentDatabase.THUMBNAIL,
AttachmentDatabase.CONTENT_TYPE,
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.CONTENT_DISPOSITION,
@@ -178,6 +180,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.MMS_ID,
AttachmentDatabase.SIZE,
AttachmentDatabase.DATA,
AttachmentDatabase.THUMBNAIL,
AttachmentDatabase.CONTENT_TYPE,
AttachmentDatabase.CONTENT_LOCATION,
AttachmentDatabase.CONTENT_DISPOSITION,
@@ -226,7 +229,10 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(AttachmentDatabase.ROW_ID);
mmsColumnsPresent.add(AttachmentDatabase.UNIQUE_ID);
mmsColumnsPresent.add(AttachmentDatabase.MMS_ID);
mmsColumnsPresent.add(AttachmentDatabase.SIZE);
mmsColumnsPresent.add(AttachmentDatabase.DATA);
mmsColumnsPresent.add(AttachmentDatabase.THUMBNAIL);
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_TYPE);
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_LOCATION);
mmsColumnsPresent.add(AttachmentDatabase.CONTENT_DISPOSITION);