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); + } +}