diff --git a/res/layout/media_preview_activity.xml b/res/layout/media_preview_activity.xml
index e5fa237f1c..f55556e06a 100644
--- a/res/layout/media_preview_activity.xml
+++ b/res/layout/media_preview_activity.xml
@@ -11,4 +11,10 @@
android:layout_height="match_parent"
android:contentDescription="@string/media_preview_activity__image_content_description" />
+
+
diff --git a/res/layout/video_player.xml b/res/layout/video_player.xml
new file mode 100644
index 0000000000..144e0f51ec
--- /dev/null
+++ b/res/layout/video_player.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java
index a228331c1f..db4e4e147c 100644
--- a/src/org/thoughtcrime/securesms/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationItem.java
@@ -535,14 +535,13 @@ public class ConversationItem extends LinearLayout
public void onClick(final View v, final Slide slide) {
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
performClick();
- } else if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) &&
- slide.getThumbnailUri() != null)
- {
+ } else 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());
if (!messageRecord.isOutgoing()) intent.putExtra(MediaPreviewActivity.RECIPIENT_EXTRA, recipient.getRecipientId());
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
+ intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
context.startActivity(intent);
} else {
diff --git a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java
index 8d6831f37d..933fd395af 100644
--- a/src/org/thoughtcrime/securesms/MediaPreviewActivity.java
+++ b/src/org/thoughtcrime/securesms/MediaPreviewActivity.java
@@ -34,6 +34,7 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.ZoomingImageView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@@ -41,6 +42,9 @@ import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
+import org.thoughtcrime.securesms.video.VideoPlayer;
+
+import java.io.IOException;
/**
* Activity for displaying media attachments in-app
@@ -50,16 +54,19 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
public static final String RECIPIENT_EXTRA = "recipient";
public static final String DATE_EXTRA = "date";
+ public static final String SIZE_EXTRA = "size";
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private MasterSecret masterSecret;
private ZoomingImageView image;
+ private VideoPlayer video;
private Uri mediaUri;
private String mediaType;
private Recipient recipient;
private long date;
+ private long size;
@Override
protected void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) {
@@ -131,6 +138,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private void initializeViews() {
image = (ZoomingImageView)findViewById(R.id.image);
+ video = (VideoPlayer)findViewById(R.id.video_player);
}
private void initializeResources() {
@@ -139,6 +147,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
mediaUri = getIntent().getData();
mediaType = getIntent().getType();
date = getIntent().getLongExtra(DATE_EXTRA, System.currentTimeMillis());
+ size = getIntent().getLongExtra(SIZE_EXTRA, 0);
if (recipientId > -1) {
recipient = RecipientFactory.getRecipientForId(this, recipientId, true);
@@ -157,13 +166,26 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
Log.w(TAG, "Loading Part URI: " + mediaUri);
- if (mediaType != null && mediaType.startsWith("image/")) {
- image.setImageUri(masterSecret, mediaUri);
+ try {
+ if (mediaType != null && mediaType.startsWith("image/")) {
+ image.setVisibility(View.VISIBLE);
+ video.setVisibility(View.GONE);
+ image.setImageUri(masterSecret, mediaUri);
+ } else if (mediaType != null && mediaType.startsWith("video/")) {
+ image.setVisibility(View.GONE);
+ video.setVisibility(View.VISIBLE);
+ video.setVideoSource(masterSecret, new VideoSlide(this, mediaUri, size));
+ }
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ Toast.makeText(getApplicationContext(), R.string.MediaPreviewActivity_unssuported_media_type, Toast.LENGTH_LONG).show();
+ finish();
}
}
private void cleanupMedia() {
image.setImageDrawable(null);
+ video.cleanup();
}
private void forward() {
@@ -208,6 +230,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
}
public static boolean isContentTypeSupported(final String contentType) {
- return contentType != null && contentType.startsWith("image/");
+ return contentType != null && (contentType.startsWith("image/") || contentType.startsWith("video/"));
}
}
diff --git a/src/org/thoughtcrime/securesms/audio/AudioAttachmentServer.java b/src/org/thoughtcrime/securesms/attachments/AttachmentServer.java
similarity index 97%
rename from src/org/thoughtcrime/securesms/audio/AudioAttachmentServer.java
rename to src/org/thoughtcrime/securesms/attachments/AttachmentServer.java
index dd96d91933..39c0d4e8fd 100644
--- a/src/org/thoughtcrime/securesms/audio/AudioAttachmentServer.java
+++ b/src/org/thoughtcrime/securesms/attachments/AttachmentServer.java
@@ -1,4 +1,4 @@
-package org.thoughtcrime.securesms.audio;
+package org.thoughtcrime.securesms.attachments;
import android.content.Context;
@@ -7,7 +7,6 @@ import android.support.annotation.NonNull;
import android.util.Log;
import org.spongycastle.util.encoders.Hex;
-import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.Util;
@@ -33,9 +32,9 @@ import java.util.StringTokenizer;
/**
* @author Stefan "frostymarvelous" Froelich
*/
-public class AudioAttachmentServer implements Runnable {
+public class AttachmentServer implements Runnable {
- private static final String TAG = AudioAttachmentServer.class.getSimpleName();
+ private static final String TAG = AttachmentServer.class.getSimpleName();
private final Context context;
private final MasterSecret masterSecret;
@@ -46,7 +45,7 @@ public class AudioAttachmentServer implements Runnable {
private volatile boolean isRunning;
- public AudioAttachmentServer(Context context, MasterSecret masterSecret, Attachment attachment)
+ public AttachmentServer(Context context, MasterSecret masterSecret, Attachment attachment)
throws IOException
{
try {
diff --git a/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java b/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java
index fe4d29b6e1..03b2dc01a3 100644
--- a/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java
+++ b/src/org/thoughtcrime/securesms/audio/AudioSlidePlayer.java
@@ -12,6 +12,7 @@ import android.util.Pair;
import android.widget.Toast;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.attachments.AttachmentServer;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.AudioSlide;
import org.thoughtcrime.securesms.util.Util;
@@ -33,7 +34,7 @@ public class AudioSlidePlayer {
private @NonNull WeakReference listener;
private @Nullable MediaPlayer mediaPlayer;
- private @Nullable AudioAttachmentServer audioAttachmentServer;
+ private @Nullable AttachmentServer audioAttachmentServer;
public synchronized static AudioSlidePlayer createFor(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@@ -64,7 +65,7 @@ public class AudioSlidePlayer {
if (this.mediaPlayer != null) return;
this.mediaPlayer = new MediaPlayer();
- this.audioAttachmentServer = new AudioAttachmentServer(context, masterSecret, slide.asAttachment());
+ this.audioAttachmentServer = new AttachmentServer(context, masterSecret, slide.asAttachment());
audioAttachmentServer.start();
diff --git a/src/org/thoughtcrime/securesms/video/VideoPlayer.java b/src/org/thoughtcrime/securesms/video/VideoPlayer.java
new file mode 100644
index 0000000000..6cb3bbeddd
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/video/VideoPlayer.java
@@ -0,0 +1,68 @@
+package org.thoughtcrime.securesms.video;
+
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+import android.widget.MediaController;
+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.VideoSlide;
+import org.thoughtcrime.securesms.util.ViewUtil;
+
+import java.io.IOException;
+
+public class VideoPlayer extends FrameLayout {
+
+ @NonNull private final VideoView videoView;
+ @Nullable private AttachmentServer attachmentServer;
+
+ public VideoPlayer(Context context) {
+ this(context, null);
+ }
+
+ public VideoPlayer(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public VideoPlayer(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ inflate(context, R.layout.video_player, this);
+
+ this.videoView = ViewUtil.findById(this, R.id.video_view);
+
+ initializeVideoViewControls(videoView);
+ }
+
+ public void setVideoSource(@NonNull MasterSecret masterSecret, @NonNull VideoSlide videoSource) throws IOException {
+ if (this.attachmentServer != null) {
+ this.attachmentServer.stop();
+ }
+
+ this.attachmentServer = new AttachmentServer(getContext(), masterSecret, videoSource.asAttachment());
+ this.attachmentServer.start();
+
+ this.videoView.setVideoURI(this.attachmentServer.getUri());
+ this.videoView.start();
+ }
+
+ public void cleanup() {
+ if (this.attachmentServer != null) {
+ this.attachmentServer.stop();
+ }
+ }
+
+ private void initializeVideoViewControls(@NonNull VideoView videoView) {
+ MediaController mediaController = new MediaController(getContext());
+ mediaController.setAnchorView(videoView);
+ mediaController.setMediaPlayer(videoView);
+
+ videoView.setMediaController(mediaController);
+ }
+}