diff --git a/build.gradle b/build.gradle
index 1d5da530a6..060bda4eb8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -50,10 +50,10 @@ dependencies {
compile 'de.greenrobot:eventbus:2.4.0'
compile 'pl.tajchert:waitingdots:0.1.0'
compile 'com.soundcloud.android:android-crop:0.9.10@aar'
- compile 'com.android.support:appcompat-v7:25.0.1'
- compile 'com.android.support:recyclerview-v7:25.0.1'
- compile 'com.android.support:design:25.0.1'
- compile 'com.android.support:cardview-v7:25.0.1'
+ compile 'com.android.support:appcompat-v7:24.2.1'
+ compile 'com.android.support:recyclerview-v7:24.2.1'
+ compile 'com.android.support:design:24.2.1'
+ compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.melnykov:floatingactionbutton:1.3.0'
compile 'com.google.zxing:android-integration:3.1.0'
compile ('com.android.support:support-v4-preferencefragment:1.0.0@aar'){
@@ -117,10 +117,10 @@ dependencyVerification {
'de.greenrobot:eventbus:61d743a748156a372024d083de763b9e91ac2dcb3f6a1cbc74995c7ddab6e968',
'pl.tajchert:waitingdots:2835d49e0787dbcb606c5a60021ced66578503b1e9fddcd7a5ef0cd5f095ba2c',
'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177',
- 'com.android.support:appcompat-v7:7fead560a22ea4b15848ce3000f312ef611fac0953bf90ca8710a72a1f6e36ea',
- 'com.android.support:recyclerview-v7:803baba7be537ace8c5cb8a775e37547c22a04c4b028833796c45c26ec1deca2',
- 'com.android.support:design:07a72eb68c888b38d7b78e450e1af8a84e571406e0cf911889e0645d5a41f1e4',
- 'com.android.support:cardview-v7:50d88fae8cd1076cb90504d36ca5ee9df4018555c8f041bd28f43274c0fc9e1f',
+ 'com.android.support:appcompat-v7:ead7ac8011fb40676df8adc2856cae934edab55fc4444654c0ac6ea443736088',
+ 'com.android.support:recyclerview-v7:9077766a1a0f4e89528fbf9dcdf6d5880a8686f0266fa852d58d803beeef18fa',
+ 'com.android.support:design:89842bb1243507fe3079066ea4ea58795effe69cdf9a819e05274d21760adfc2',
+ 'com.android.support:cardview-v7:2303b351686d1db060b5fcf1a9c709c79b4a54a85bfda0fb3c4849e244606ee1',
'com.melnykov:floatingactionbutton:15d58d4fac0f7a288d0e5301bbaf501a146f5b3f5921277811bf99bd3b397263',
'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4',
'com.android.support:support-v4-preferencefragment:5470f5872514a6226fa1fc6f4e000991f38805691c534cf0bd2778911fc773ad',
@@ -136,10 +136,9 @@ dependencyVerification {
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'cn.carbswang.android:NumberPickerView:18b3c316d62c7c277978a8d4ed57a5b8f4e943762264960f579a8a549c756729',
'com.google.android.gms:play-services-base:ef36e50fa5c0415ed41f74dd399a889efd2fa327c449036e140c7c3786aa0e1f',
- 'com.android.support:support-annotations:bd94ab42c841db16fb480f4c65d33d297e544655ecc498b37c5cf33a0c5f1968',
- 'com.android.support:support-compat:d04f15aa5f2ae9e8cb7d025bf02dfd4fd6f6800628ceb107e0589634c9e4e537',
- 'com.android.support:support-core-ui:29205ac978a1839d92be3d32db2385dac10f8688bba649e51650023c76de2f00',
- 'com.android.support:transition:9fd1e6d27cb70b3c5cd19f842b48bbb05cb4e5c93a22372769c342523393e8ea',
+ 'com.android.support:support-annotations:1e4d471c5378b283d95abfb128e7ed3c6b3cb19bb6f0c317a9b75e48e99365ff',
+ 'com.android.support:support-compat:8e4fe0078b68073e8f5bcb52aa5b6407fd456d47c51aa0f8e8d1e23c69da06c1',
+ 'com.android.support:support-core-ui:ecc9184b7f438980e1c4a08b089d62dbc53ff90091f442d91fec27322a02c73c',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
@@ -156,18 +155,17 @@ dependencyVerification {
'com.squareup.okio:okio:5e1098bd3fdee4c3347f5ab815b40ba851e4ab1b348c5e49a5b0362f0ce6e978',
'com.fasterxml.jackson.core:jackson-annotations:0ca408c24202a7626ec8b861e99d85eca5e38b73311dd6dd12e3e9deecc3fe94',
'com.fasterxml.jackson.core:jackson-core:cbf4604784b4de226262845447a1ad3bb38a6728cebe86562e2c5afada8be2c0',
- 'com.android.support:support-v4:50da261acc4ca3d2dea9a43106bf65488711ca97b20a4daa095dba381c205c98',
- 'com.android.support:support-media-compat:01cac57af687bed9a6cb4ce803bebd1b7e6b8469c14f1f9ac6b4596637ff73d6',
- 'com.android.support:support-core-utils:632c3750bd991da8b591f24a8916e74ca6063ae7f525f005c96981725c9bf491',
- 'com.android.support:support-fragment:da47261a1d7c3d33e6e911335a7f4ce01135923bb221d3ab84625d005fa1969f',
- 'com.android.support:support-vector-drawable:071ae3695bf8427d3cbfc8791492a3d9c804a4b111aa2a72fbfe7790ea268e5d',
- 'com.android.support:animated-vector-drawable:70443a2857f9968c4e2c27c107657ce2291d774f8a50f03444e12ab637451175',
+ 'com.android.support:support-v4:cac2956f5c4bb363cc0ba824ac16ea2a687d1c305d434416a34772a5f9375ed7',
+ 'com.android.support:support-media-compat:fa29a23eadd685631584b2c0c624a36e3bb79a33e257b00304501ad682fa2be3',
+ 'com.android.support:support-core-utils:0fbc508e41dd6e8c634f310ee88452aaf8f48b6a843a369b115130b80d2fc05f',
+ 'com.android.support:support-fragment:d8030f0bf0f64214a29dc4e14d5ccd225e59f66ed15eb37f3a5022e773dd1fda',
+ 'com.android.support:support-vector-drawable:6ee37a7f7b93c1df1294e6f6f97df3724ac989fcda0549faf677001085330548',
+ 'com.android.support:animated-vector-drawable:5aa30f578e1daefb26bef0ce06414266fbb4cdf5d4259f42a92c7bd83dcd81b4',
]
}
-
android {
- compileSdkVersion 25
+ compileSdkVersion 24
buildToolsVersion '23.0.3'
useLibrary 'org.apache.http.legacy'
diff --git a/res/drawable-hdpi/ic_play_circle_outline_white_48dp.png b/res/drawable-hdpi/ic_play_circle_outline_white_48dp.png
new file mode 100644
index 0000000000..6e1b578c54
Binary files /dev/null and b/res/drawable-hdpi/ic_play_circle_outline_white_48dp.png differ
diff --git a/res/drawable-mdpi/ic_play_circle_outline_white_48dp.png b/res/drawable-mdpi/ic_play_circle_outline_white_48dp.png
new file mode 100644
index 0000000000..615b80d085
Binary files /dev/null and b/res/drawable-mdpi/ic_play_circle_outline_white_48dp.png differ
diff --git a/res/drawable-xhdpi/ic_play_circle_outline_white_48dp.png b/res/drawable-xhdpi/ic_play_circle_outline_white_48dp.png
new file mode 100644
index 0000000000..516f643269
Binary files /dev/null and b/res/drawable-xhdpi/ic_play_circle_outline_white_48dp.png differ
diff --git a/res/drawable-xxhdpi/ic_play_circle_outline_white_48dp.png b/res/drawable-xxhdpi/ic_play_circle_outline_white_48dp.png
new file mode 100644
index 0000000000..0311f899dd
Binary files /dev/null and b/res/drawable-xxhdpi/ic_play_circle_outline_white_48dp.png differ
diff --git a/res/drawable-xxxhdpi/ic_play_circle_outline_white_48dp.png b/res/drawable-xxxhdpi/ic_play_circle_outline_white_48dp.png
new file mode 100644
index 0000000000..7a5a16858b
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_play_circle_outline_white_48dp.png differ
diff --git a/res/layout/thumbnail_view.xml b/res/layout/thumbnail_view.xml
index 1e446b0552..24851538c3 100644
--- a/res/layout/thumbnail_view.xml
+++ b/res/layout/thumbnail_view.xml
@@ -10,6 +10,18 @@
android:scaleType="fitCenter"
android:contentDescription="@string/conversation_item__mms_image_description" />
+
+
{
@Override
public void onClick(View v) {
- Intent intent = new Intent(getContext(), MediaPreviewActivity.class);
- intent.putExtra(MediaPreviewActivity.DATE_EXTRA, imageRecord.getDate());
- intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, threadId);
+ if (imageRecord.getAttachment().getDataUri() != null) {
+ Intent intent = new Intent(getContext(), MediaPreviewActivity.class);
+ intent.putExtra(MediaPreviewActivity.DATE_EXTRA, imageRecord.getDate());
+ intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, threadId);
- if (!TextUtils.isEmpty(imageRecord.getAddress())) {
- Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(),
- imageRecord.getAddress(),
- true);
- if (recipients != null && recipients.getPrimaryRecipient() != null) {
- intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
+ if (!TextUtils.isEmpty(imageRecord.getAddress())) {
+ Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(),
+ imageRecord.getAddress(),
+ true);
+ if (recipients != null && recipients.getPrimaryRecipient() != null) {
+ intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipients.getPrimaryRecipient().getRecipientId());
+ }
}
+ intent.setDataAndType(imageRecord.getAttachment().getDataUri(), imageRecord.getContentType());
+ getContext().startActivity(intent);
}
- intent.setDataAndType(imageRecord.getAttachment().getDataUri(), imageRecord.getContentType());
- getContext().startActivity(intent);
-
}
}
}
diff --git a/src/org/thoughtcrime/securesms/attachments/Attachment.java b/src/org/thoughtcrime/securesms/attachments/Attachment.java
index ce579d81e8..c872cc1b25 100644
--- a/src/org/thoughtcrime/securesms/attachments/Attachment.java
+++ b/src/org/thoughtcrime/securesms/attachments/Attachment.java
@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.attachments;
-import android.graphics.Bitmap;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -23,10 +22,6 @@ public abstract class Attachment {
@Nullable
private final String relay;
- // XXX - This shouldn't be here.
- @Nullable
- private Bitmap thumbnail;
-
public Attachment(@NonNull String contentType, int transferState, long size,
@Nullable String location, @Nullable String key, @Nullable String relay)
{
@@ -76,13 +71,4 @@ public abstract class Attachment {
public String getRelay() {
return relay;
}
-
- public void setThumbnail(@Nullable Bitmap thumbnail) {
- this.thumbnail = thumbnail;
- }
-
- @Nullable
- public Bitmap getThumbnail() {
- return thumbnail;
- }
}
diff --git a/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java b/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java
index a8f47be164..84412c66b5 100644
--- a/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java
+++ b/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java
@@ -1,7 +1,7 @@
package org.thoughtcrime.securesms.attachments;
import android.net.Uri;
-import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.mms.PartAuthority;
@@ -10,27 +10,38 @@ public class DatabaseAttachment extends Attachment {
private final AttachmentId attachmentId;
private final long mmsId;
private final boolean hasData;
+ private final boolean hasThumbnail;
- public DatabaseAttachment(AttachmentId attachmentId, long mmsId, boolean hasData,
+ public DatabaseAttachment(AttachmentId attachmentId, long mmsId,
+ boolean hasData, boolean hasThumbnail,
String contentType, int transferProgress, long size,
String location, String key, String relay)
{
super(contentType, transferProgress, size, location, key, relay);
this.attachmentId = attachmentId;
this.hasData = hasData;
+ this.hasThumbnail = hasThumbnail;
this.mmsId = mmsId;
}
@Override
- @NonNull
+ @Nullable
public Uri getDataUri() {
- return PartAuthority.getAttachmentDataUri(attachmentId);
+ if (hasData) {
+ return PartAuthority.getAttachmentDataUri(attachmentId);
+ } else {
+ return null;
+ }
}
@Override
- @NonNull
+ @Nullable
public Uri getThumbnailUri() {
- return PartAuthority.getAttachmentThumbnailUri(attachmentId);
+ if (hasThumbnail) {
+ return PartAuthority.getAttachmentThumbnailUri(attachmentId);
+ } else {
+ return null;
+ }
}
public AttachmentId getAttachmentId() {
@@ -56,4 +67,8 @@ public class DatabaseAttachment extends Attachment {
public boolean hasData() {
return hasData;
}
+
+ public boolean hasThumbnail() {
+ return hasThumbnail;
+ }
}
diff --git a/src/org/thoughtcrime/securesms/attachments/UriAttachment.java b/src/org/thoughtcrime/securesms/attachments/UriAttachment.java
index ea6828bc76..fdef4178b1 100644
--- a/src/org/thoughtcrime/securesms/attachments/UriAttachment.java
+++ b/src/org/thoughtcrime/securesms/attachments/UriAttachment.java
@@ -1,26 +1,19 @@
package org.thoughtcrime.securesms.attachments;
-import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
-
-import org.thoughtcrime.securesms.crypto.MasterSecret;
-import org.thoughtcrime.securesms.util.MediaUtil;
-import org.whispersystems.libsignal.util.guava.Optional;
-
-import java.io.IOException;
-import java.io.InputStream;
+import android.support.annotation.Nullable;
public class UriAttachment extends Attachment {
- private final @NonNull Uri dataUri;
- private final @NonNull Uri thumbnailUri;
+ private final @NonNull Uri dataUri;
+ private final @Nullable Uri thumbnailUri;
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size) {
this(uri, uri, contentType, transferState, size);
}
- public UriAttachment(@NonNull Uri dataUri, @NonNull Uri thumbnailUri,
+ public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
@NonNull String contentType, int transferState, long size)
{
super(contentType, transferState, size, null, null, null);
@@ -35,7 +28,7 @@ public class UriAttachment extends Attachment {
}
@Override
- @NonNull
+ @Nullable
public Uri getThumbnailUri() {
return thumbnailUri;
}
diff --git a/src/org/thoughtcrime/securesms/components/ThumbnailView.java b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
index 29799b548e..ec30896f0c 100644
--- a/src/org/thoughtcrime/securesms/components/ThumbnailView.java
+++ b/src/org/thoughtcrime/securesms/components/ThumbnailView.java
@@ -36,6 +36,7 @@ public class ThumbnailView extends FrameLayout {
private static final String TAG = ThumbnailView.class.getSimpleName();
private ImageView image;
+ private ImageView playOverlay;
private int backgroundColorHint;
private int radius;
private OnClickListener parentClickListener;
@@ -58,8 +59,9 @@ public class ThumbnailView extends FrameLayout {
inflate(context, R.layout.thumbnail_view, this);
- this.radius = getResources().getDimensionPixelSize(R.dimen.message_bubble_corner_radius);
- this.image = (ImageView) findViewById(R.id.thumbnail_image);
+ this.radius = getResources().getDimensionPixelSize(R.dimen.message_bubble_corner_radius);
+ this.image = (ImageView) findViewById(R.id.thumbnail_image);
+ this.playOverlay = (ImageView) findViewById(R.id.play_overlay);
super.setOnClickListener(new ThumbnailClickDispatcher());
if (attrs != null) {
@@ -105,6 +107,12 @@ public class ThumbnailView extends FrameLayout {
getTransferControls().setVisibility(View.GONE);
}
+ if (slide.getThumbnailUri() != null && slide.hasPlayOverlay() && slide.getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
+ this.playOverlay.setVisibility(View.VISIBLE);
+ } else {
+ this.playOverlay.setVisibility(View.GONE);
+ }
+
if (Util.equals(slide, this.slide)) {
Log.w(TAG, "Not re-loading slide " + slide.asAttachment().getDataUri());
return;
diff --git a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java
index f591765d1e..b78abc6031 100644
--- a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java
@@ -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 {
+
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);
+ }
}
}
diff --git a/src/org/thoughtcrime/securesms/database/ImageDatabase.java b/src/org/thoughtcrime/securesms/database/ImageDatabase.java
index 5e7bb25179..6532501a8d 100644
--- a/src/org/thoughtcrime/securesms/database/ImageDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/ImageDatabase.java
@@ -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() {
diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
index 0bcd958759..fdb5e5bb67 100644
--- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java
@@ -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(),
diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java
index 3ad42dec9f..106fd94093 100644
--- a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java
@@ -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);
diff --git a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
index a26e782ded..bb5941e14b 100644
--- a/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/src/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -328,7 +328,7 @@ public class AttachmentManager {
}
private void previewImageDraft(final @NonNull Slide slide) {
- if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getThumbnailUri() != null) {
+ if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) {
Intent intent = new Intent(context, MediaPreviewActivity.class);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(slide.getUri(), slide.getContentType());
diff --git a/src/org/thoughtcrime/securesms/mms/AudioSlide.java b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
index 46cb231079..805a61ec3c 100644
--- a/src/org/thoughtcrime/securesms/mms/AudioSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/AudioSlide.java
@@ -37,11 +37,11 @@ import ws.com.google.android.mms.pdu.PduPart;
public class AudioSlide extends Slide {
public AudioSlide(Context context, Uri uri, long dataSize) {
- super(context, constructAttachmentFromUri(context, uri, ContentType.AUDIO_UNSPECIFIED, dataSize));
+ super(context, constructAttachmentFromUri(context, uri, ContentType.AUDIO_UNSPECIFIED, dataSize, false));
}
public AudioSlide(Context context, Uri uri, long dataSize, String contentType) {
- super(context, new UriAttachment(uri, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize));
+ super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize));
}
public AudioSlide(Context context, Attachment attachment) {
diff --git a/src/org/thoughtcrime/securesms/mms/GifSlide.java b/src/org/thoughtcrime/securesms/mms/GifSlide.java
index 614e5ad557..5a2c614b8d 100644
--- a/src/org/thoughtcrime/securesms/mms/GifSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/GifSlide.java
@@ -20,7 +20,7 @@ public class GifSlide extends ImageSlide {
}
public GifSlide(Context context, Uri uri, long size) {
- super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_GIF, size));
+ super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_GIF, size, true));
}
@Override
diff --git a/src/org/thoughtcrime/securesms/mms/ImageSlide.java b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
index 2971086f39..2324309306 100644
--- a/src/org/thoughtcrime/securesms/mms/ImageSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/ImageSlide.java
@@ -25,8 +25,6 @@ import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
-import java.io.IOException;
-
import ws.com.google.android.mms.ContentType;
public class ImageSlide extends Slide {
@@ -38,7 +36,7 @@ public class ImageSlide extends Slide {
}
public ImageSlide(Context context, Uri uri, long size) {
- super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_JPEG, size));
+ super(context, constructAttachmentFromUri(context, uri, ContentType.IMAGE_JPEG, size, true));
}
@Override
@@ -51,7 +49,9 @@ public class ImageSlide extends Slide {
return true;
}
- @NonNull @Override public String getContentDescription() {
+ @NonNull
+ @Override
+ public String getContentDescription() {
return context.getString(R.string.Slide_image);
}
}
diff --git a/src/org/thoughtcrime/securesms/mms/Slide.java b/src/org/thoughtcrime/securesms/mms/Slide.java
index ba85a3d0a1..71550bee5f 100644
--- a/src/org/thoughtcrime/securesms/mms/Slide.java
+++ b/src/org/thoughtcrime/securesms/mms/Slide.java
@@ -103,13 +103,18 @@ public abstract class Slide {
return false;
}
+ public boolean hasPlayOverlay() {
+ return false;
+ }
+
protected static Attachment constructAttachmentFromUri(@NonNull Context context,
@NonNull Uri uri,
@NonNull String defaultMime,
- long size)
+ long size,
+ boolean hasThumbnail)
{
Optional resolvedType = Optional.fromNullable(MediaUtil.getMimeType(context, uri));
- return new UriAttachment(uri, resolvedType.or(defaultMime), AttachmentDatabase.TRANSFER_PROGRESS_STARTED, size);
+ return new UriAttachment(uri, hasThumbnail ? uri : null, resolvedType.or(defaultMime), AttachmentDatabase.TRANSFER_PROGRESS_STARTED, size);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/mms/VideoSlide.java b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
index 8c2000b42e..465e91ff34 100644
--- a/src/org/thoughtcrime/securesms/mms/VideoSlide.java
+++ b/src/org/thoughtcrime/securesms/mms/VideoSlide.java
@@ -21,21 +21,17 @@ import android.content.res.Resources.Theme;
import android.net.Uri;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.util.ResUtil;
-import java.io.IOException;
-
import ws.com.google.android.mms.ContentType;
-import ws.com.google.android.mms.pdu.PduPart;
public class VideoSlide extends Slide {
public VideoSlide(Context context, Uri uri, long dataSize) {
- super(context, constructAttachmentFromUri(context, uri, ContentType.VIDEO_UNSPECIFIED, dataSize));
+ super(context, constructAttachmentFromUri(context, uri, ContentType.VIDEO_UNSPECIFIED, dataSize, false));
}
public VideoSlide(Context context, Attachment attachment) {
@@ -43,13 +39,12 @@ public class VideoSlide extends Slide {
}
@Override
- @Nullable
- public Uri getThumbnailUri() {
- return null;
+ public boolean hasPlaceholder() {
+ return true;
}
@Override
- public boolean hasPlaceholder() {
+ public boolean hasPlayOverlay() {
return true;
}
@@ -68,7 +63,8 @@ public class VideoSlide extends Slide {
return true;
}
- @NonNull @Override public String getContentDescription() {
+ @NonNull @Override
+ public String getContentDescription() {
return context.getString(R.string.Slide_video);
}
}
diff --git a/src/org/thoughtcrime/securesms/util/MediaUtil.java b/src/org/thoughtcrime/securesms/util/MediaUtil.java
index 0d04f0b533..a9adbec6f3 100644
--- a/src/org/thoughtcrime/securesms/util/MediaUtil.java
+++ b/src/org/thoughtcrime/securesms/util/MediaUtil.java
@@ -23,11 +23,11 @@ import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import java.io.IOException;
import java.io.InputStream;
-import java.util.concurrent.ExecutionException;
import ws.com.google.android.mms.ContentType;
public class MediaUtil {
+
private static final String TAG = MediaUtil.class.getSimpleName();
public static @Nullable ThumbnailData generateThumbnail(Context context, MasterSecret masterSecret, String contentType, Uri uri)
diff --git a/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java b/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java
index 38500086bf..85bfa6f063 100644
--- a/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java
+++ b/src/org/thoughtcrime/securesms/util/SaveAttachmentTask.java
@@ -5,6 +5,7 @@ import android.content.DialogInterface.OnClickListener;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
+import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.webkit.MimeTypeMap;
@@ -158,7 +159,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask 0) {
+ int read = inputStream.read(buffer, 0, Util.toIntExact(Math.min((long)buffer.length, headerRemaining)));
+
+ if (read == -1) return -1;
+
+ headerRemaining -= read;
+ }
+
+ int returnValue = inputStream.read(bytes, offset, length);
+ inputStream.close();
+ return returnValue;
+ }
+
+ @Override
+ public long getSize() throws IOException {
+ DecryptingPartInputStream inputStream = new DecryptingPartInputStream(mediaFile, masterSecret);
+ byte[] buffer = new byte[4096];
+ long size = 0;
+
+ int read;
+
+ while ((read = inputStream.read(buffer)) != -1) {
+ size += read;
+ }
+
+ return size;
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/video/VideoPlayer.java b/src/org/thoughtcrime/securesms/video/VideoPlayer.java
index 6cb3bbeddd..47135bb5cb 100644
--- a/src/org/thoughtcrime/securesms/video/VideoPlayer.java
+++ b/src/org/thoughtcrime/securesms/video/VideoPlayer.java
@@ -5,13 +5,16 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
+import android.util.Log;
import android.widget.FrameLayout;
import android.widget.MediaController;
+import android.widget.Toast;
import android.widget.VideoView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.AttachmentServer;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.util.ViewUtil;
@@ -19,6 +22,8 @@ import java.io.IOException;
public class VideoPlayer extends FrameLayout {
+ private static final String TAG = VideoPlayer.class.getName();
+
@NonNull private final VideoView videoView;
@Nullable private AttachmentServer attachmentServer;
@@ -45,10 +50,20 @@ public class VideoPlayer extends FrameLayout {
this.attachmentServer.stop();
}
- this.attachmentServer = new AttachmentServer(getContext(), masterSecret, videoSource.asAttachment());
- this.attachmentServer.start();
+ if (videoSource.getUri() != null && PartAuthority.isLocalUri(videoSource.getUri())) {
+ Log.w(TAG, "Starting video attachment server for part provider Uri...");
+ this.attachmentServer = new AttachmentServer(getContext(), masterSecret, videoSource.asAttachment());
+ this.attachmentServer.start();
+
+ this.videoView.setVideoURI(this.attachmentServer.getUri());
+ } else if (videoSource.getUri() != null) {
+ Log.w(TAG, "Playing video directly from non-local Uri...");
+ this.videoView.setVideoURI(videoSource.getUri());
+ } else {
+ Toast.makeText(getContext(), "Error playing video...", Toast.LENGTH_LONG).show();
+ return;
+ }
- this.videoView.setVideoURI(this.attachmentServer.getUri());
this.videoView.start();
}