diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index eb7611d2c..55883783d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -686,6 +686,10 @@
android:screenOrientation="portrait"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
+
+
diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java
index 53fcd3836..ffb3c432d 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java
@@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity;
+import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory;
import org.thoughtcrime.securesms.mediapreview.MediaPreviewFragment;
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
@@ -98,18 +99,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private final static String TAG = Log.tag(MediaPreviewActivity.class);
- private static final int NOT_IN_A_THREAD = -2;
-
- public static final String THREAD_ID_EXTRA = "thread_id";
- public static final String DATE_EXTRA = "date";
- public static final String SIZE_EXTRA = "size";
- public static final String CAPTION_EXTRA = "caption";
- public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent";
- public static final String HIDE_ALL_MEDIA_EXTRA = "came_from_all_media";
- public static final String SHOW_THREAD_EXTRA = "show_thread";
- public static final String SORTING_EXTRA = "sorting";
- public static final String IS_VIDEO_GIF = "is_video_gif";
-
private ViewPager mediaPager;
private View detailsContainer;
private TextView caption;
@@ -127,7 +116,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private ViewPagerListener viewPagerListener;
private int restartItem = -1;
- private long threadId = NOT_IN_A_THREAD;
+ private long threadId = MediaIntentFactory.NOT_IN_A_THREAD;
private boolean cameFromAllMedia;
private boolean showThread;
private MediaDatabase.Sorting sorting;
@@ -143,12 +132,12 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
{
DatabaseAttachment attachment = Objects.requireNonNull(mediaRecord.getAttachment());
Intent intent = new Intent(context, MediaPreviewActivity.class);
- intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, mediaRecord.getThreadId());
- intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
- intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize());
- intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption());
- intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent);
- intent.putExtra(MediaPreviewActivity.IS_VIDEO_GIF, attachment.isVideoGif());
+ intent.putExtra(MediaIntentFactory.THREAD_ID_EXTRA, mediaRecord.getThreadId());
+ intent.putExtra(MediaIntentFactory.DATE_EXTRA, mediaRecord.getDate());
+ intent.putExtra(MediaIntentFactory.SIZE_EXTRA, attachment.getSize());
+ intent.putExtra(MediaIntentFactory.CAPTION_EXTRA, attachment.getCaption());
+ intent.putExtra(MediaIntentFactory.LEFT_IS_RECENT_EXTRA, leftIsRecent);
+ intent.putExtra(MediaIntentFactory.IS_VIDEO_GIF, attachment.isVideoGif());
intent.setDataAndType(attachment.getUri(), mediaRecord.getContentType());
return intent;
}
@@ -305,17 +294,17 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private void initializeResources() {
Intent intent = getIntent();
- threadId = intent.getLongExtra(THREAD_ID_EXTRA, NOT_IN_A_THREAD);
- cameFromAllMedia = intent.getBooleanExtra(HIDE_ALL_MEDIA_EXTRA, false);
- showThread = intent.getBooleanExtra(SHOW_THREAD_EXTRA, false);
- sorting = MediaDatabase.Sorting.values()[intent.getIntExtra(SORTING_EXTRA, 0)];
+ threadId = intent.getLongExtra(MediaIntentFactory.THREAD_ID_EXTRA, MediaIntentFactory.NOT_IN_A_THREAD);
+ cameFromAllMedia = intent.getBooleanExtra(MediaIntentFactory.HIDE_ALL_MEDIA_EXTRA, false);
+ showThread = intent.getBooleanExtra(MediaIntentFactory.SHOW_THREAD_EXTRA, false);
+ sorting = MediaDatabase.Sorting.values()[intent.getIntExtra(MediaIntentFactory.SORTING_EXTRA, 0)];
initialMediaUri = intent.getData();
initialMediaType = intent.getType();
- initialMediaSize = intent.getLongExtra(SIZE_EXTRA, 0);
- initialCaption = intent.getStringExtra(CAPTION_EXTRA);
- leftIsRecent = intent.getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
- initialMediaIsVideoGif = intent.getBooleanExtra(IS_VIDEO_GIF, false);
+ initialMediaSize = intent.getLongExtra(MediaIntentFactory.SIZE_EXTRA, 0);
+ initialCaption = intent.getStringExtra(MediaIntentFactory.CAPTION_EXTRA);
+ leftIsRecent = intent.getBooleanExtra(MediaIntentFactory.LEFT_IS_RECENT_EXTRA, false);
+ initialMediaIsVideoGif = intent.getBooleanExtra(MediaIntentFactory.IS_VIDEO_GIF, false);
restartItem = -1;
}
@@ -533,7 +522,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
}
private boolean isMediaInDb() {
- return threadId != NOT_IN_A_THREAD;
+ return threadId != MediaIntentFactory.NOT_IN_A_THREAD;
}
private @Nullable MediaItem getCurrentMediaItem() {
@@ -789,7 +778,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
cursor.moveToPosition(cursorPosition);
- MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(context, cursor);
+ MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(cursor);
DatabaseAttachment attachment = Objects.requireNonNull(mediaRecord.getAttachment());
MediaPreviewFragment fragment = MediaPreviewFragment.newInstance(attachment, autoPlay);
@@ -819,7 +808,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
cursor.moveToPosition(cursorPosition);
- MediaRecord mediaRecord = MediaRecord.from(context, cursor);
+ MediaRecord mediaRecord = MediaRecord.from(cursor);
DatabaseAttachment attachment = Objects.requireNonNull(mediaRecord.getAttachment());
RecipientId recipientId = mediaRecord.getRecipientId();
RecipientId threadRecipientId = mediaRecord.getThreadRecipientId();
@@ -890,4 +879,4 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
@Nullable View getPlaybackControls(int position);
boolean hasFragmentFor(int position);
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/animation/DepthPageTransformer.java b/app/src/main/java/org/thoughtcrime/securesms/animation/DepthPageTransformer.java
index da935281b..85ca770bf 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/animation/DepthPageTransformer.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/animation/DepthPageTransformer.java
@@ -4,11 +4,12 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.ViewPager;
+import androidx.viewpager2.widget.ViewPager2;
/**
* Based on https://developer.android.com/training/animation/screen-slide#depth-page
*/
-public final class DepthPageTransformer implements ViewPager.PageTransformer {
+public final class DepthPageTransformer implements ViewPager.PageTransformer, ViewPager2.PageTransformer {
private static final float MIN_SCALE = 0.75f;
public void transformPage(@NonNull View view, float position) {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java b/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java
index 05d1b56b5..4c09af37e 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/components/ThreadPhotoRailView.java
@@ -89,7 +89,7 @@ public class ThreadPhotoRailView extends FrameLayout {
@Override
public void onBindItemViewHolder(ThreadPhotoViewHolder viewHolder, @NonNull Cursor cursor) {
ThumbnailView imageView = viewHolder.imageView;
- MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor);
+ MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(cursor);
Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment());
if (slide != null) {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java
index 66e2dd01f..7272b35f6 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java
@@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.conversation;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
+import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -96,6 +97,7 @@ import org.thoughtcrime.securesms.conversation.colors.Colorizer;
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection;
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
+import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
@@ -111,6 +113,8 @@ import org.thoughtcrime.securesms.jobs.MmsSendJob;
import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
+import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory;
+import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority;
@@ -126,6 +130,7 @@ import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageView;
import org.thoughtcrime.securesms.util.DateUtils;
+import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.InterceptableLongClickCopyLinkSpan;
import org.thoughtcrime.securesms.util.LinkUtil;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
@@ -2312,17 +2317,19 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
} else if (!canPlayContent && mediaItem != null && eventListener != null) {
eventListener.onPlayInlineContent(conversationMessage);
} 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());
- intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, messageRecord.getThreadId());
- intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
- intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
- intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orElse(null));
- intent.putExtra(MediaPreviewActivity.IS_VIDEO_GIF, slide.isVideoGif());
- intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false);
-
- context.startActivity(intent);
+ MediaIntentFactory.MediaPreviewArgs args = new MediaIntentFactory.MediaPreviewArgs(
+ messageRecord.getThreadId(),
+ messageRecord.getTimestamp(),
+ slide.getUri(),
+ slide.getContentType(),
+ slide.asAttachment().getSize(),
+ slide.getCaption().orElse(null),
+ false,
+ false,
+ false,
+ MediaDatabase.Sorting.Newest.ordinal(),
+ slide.isVideoGif());
+ context.startActivity(MediaIntentFactory.create(context, args));
} else if (slide.getUri() != null) {
Log.i(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType());
Uri publicUri = PartAuthority.getAttachmentPublicUri(slide.getUri());
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java
index 7a4fd1463..ea5313c3b 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/MediaDatabase.java
@@ -192,7 +192,7 @@ public class MediaDatabase extends Database {
this.outgoing = outgoing;
}
- public static MediaRecord from(@NonNull Context context, @NonNull Cursor cursor) {
+ public static MediaRecord from(@NonNull Cursor cursor) {
AttachmentDatabase attachmentDatabase = SignalDatabase.attachments();
List attachments = attachmentDatabase.getAttachments(cursor);
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.RECIPIENT_ID)));
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java
index 26da50d08..8b2169ea8 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/loaders/GroupedThreadMediaLoader.java
@@ -75,7 +75,7 @@ public final class GroupedThreadMediaLoader extends AsyncTaskLoader> {
+ return Single.fromCallable {
+ val cursor = media.getGalleryMediaForThread(threadId, sorting)
+
+ val acc = mutableListOf()
+ var attachmentUri: Uri? = null
+ while (cursor.moveToNext()) {
+ val attachmentId = AttachmentId(cursor.requireLong(AttachmentDatabase.ROW_ID), cursor.requireLong(AttachmentDatabase.UNIQUE_ID))
+ attachmentUri = PartAuthority.getAttachmentDataUri(attachmentId)
+ if (attachmentUri == startingUri) {
+ break
+ }
+ }
+
+ if (attachmentUri == startingUri) {
+ for (i in 0..limit) {
+ val element = MediaDatabase.MediaRecord.from(cursor).attachment
+ if (element != null) {
+ acc.add(element)
+ }
+ if (!cursor.isLast) {
+ cursor.moveToNext()
+ } else {
+ break
+ }
+ }
+ acc.toList()
+ } else {
+ Log.e(TAG, "Could not find $startingUri in thread $threadId")
+ emptyList()
+ }
+ }.subscribeOn(Schedulers.io()).toFlowable()
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Activity.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Activity.kt
new file mode 100644
index 000000000..3735f8fc0
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Activity.kt
@@ -0,0 +1,28 @@
+package org.thoughtcrime.securesms.mediapreview
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.commit
+import org.thoughtcrime.securesms.R
+
+class MediaPreviewV2Activity : AppCompatActivity(R.layout.activity_mediapreview_v2) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (savedInstanceState == null) {
+ val bundle = Bundle()
+ val args = MediaIntentFactory.requireArguments(intent.extras!!)
+ bundle.putParcelable(MediaPreviewV2Fragment.ARGS_KEY, args)
+ supportFragmentManager.commit {
+ setReorderingAllowed(true)
+ add(R.id.fragment_container_view, MediaPreviewV2Fragment::class.java, bundle, FRAGMENT_TAG)
+ }
+ }
+ }
+
+ companion object {
+ private const val FRAGMENT_TAG = "media_preview_fragment_v2"
+ private const val NOT_IN_A_THREAD = -2
+
+ const val THREAD_ID_EXTRA = "thread_id"
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt
new file mode 100644
index 000000000..3a22843c4
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt
@@ -0,0 +1,57 @@
+package org.thoughtcrime.securesms.mediapreview
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import org.signal.core.util.logging.Log
+import org.thoughtcrime.securesms.R
+import org.thoughtcrime.securesms.animation.DepthPageTransformer
+import org.thoughtcrime.securesms.components.ViewBinderDelegate
+import org.thoughtcrime.securesms.database.MediaDatabase
+import org.thoughtcrime.securesms.databinding.FragmentMediaPreviewV2Binding
+import org.thoughtcrime.securesms.util.LifecycleDisposable
+
+class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), MediaPreviewFragment.Events {
+ private val TAG = Log.tag(MediaPreviewV2Fragment::class.java)
+
+ private val lifecycleDisposable = LifecycleDisposable()
+ private val binding by ViewBinderDelegate(FragmentMediaPreviewV2Binding::bind)
+ private val viewModel: MediaPreviewV2ViewModel by viewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.mediaPager.offscreenPageLimit = 1
+ binding.mediaPager.setPageTransformer(DepthPageTransformer())
+ lifecycleDisposable += viewModel.state.distinctUntilChanged().observeOn(AndroidSchedulers.mainThread()).subscribe {
+ if (it.loadState == MediaPreviewV2State.LoadState.READY) {
+ binding.mediaPager.adapter = PreviewMediaAdapter(this, it.attachments)
+ }
+ }
+ initializeViewModel()
+ }
+
+ private fun initializeViewModel() {
+ val args = MediaIntentFactory.requireArguments(requireArguments())
+ val sorting = MediaDatabase.Sorting.values()[args.sorting]
+ viewModel.fetchAttachments(args.initialMediaUri, args.threadId, sorting)
+ }
+
+ override fun singleTapOnMedia(): Boolean {
+ Log.d(TAG, "singleTapOnMedia()")
+ return true
+ }
+
+ override fun mediaNotAvailable() {
+ Log.d(TAG, "mediaNotAvailable()")
+ }
+
+ override fun onMediaReady() {
+ Log.d(TAG, "onMediaReady()")
+ }
+
+ companion object {
+ val ARGS_KEY: String = "args"
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2State.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2State.kt
new file mode 100644
index 000000000..ef873fc32
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2State.kt
@@ -0,0 +1,10 @@
+package org.thoughtcrime.securesms.mediapreview
+
+import org.thoughtcrime.securesms.attachments.Attachment
+
+data class MediaPreviewV2State(
+ val attachments: List = emptyList(),
+ val loadState: LoadState = LoadState.INIT
+) {
+ enum class LoadState { INIT, READY, }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt
new file mode 100644
index 000000000..6de2873d9
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2ViewModel.kt
@@ -0,0 +1,30 @@
+package org.thoughtcrime.securesms.mediapreview
+
+import android.net.Uri
+import androidx.lifecycle.ViewModel
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import io.reactivex.rxjava3.kotlin.plusAssign
+import org.signal.core.util.logging.Log
+import org.thoughtcrime.securesms.database.MediaDatabase
+import org.thoughtcrime.securesms.util.rx.RxStore
+
+class MediaPreviewV2ViewModel : ViewModel() {
+ private val TAG = Log.tag(MediaPreviewV2ViewModel::class.java)
+ private val store = RxStore(MediaPreviewV2State())
+ private val disposables = CompositeDisposable()
+ private val repository: MediaPreviewRepository = MediaPreviewRepository()
+
+ val state: Flowable = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
+
+ fun fetchAttachments(startingUri: Uri, threadId: Long, sorting: MediaDatabase.Sorting) {
+ disposables += store.update(repository.getAttachments(startingUri, threadId, sorting)) { attachments, oldState ->
+ oldState.copy(attachments = attachments, loadState = MediaPreviewV2State.LoadState.READY)
+ }
+ }
+
+ override fun onCleared() {
+ disposables.dispose()
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java
index 2ec38b000..70a986365 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewViewModel.java
@@ -47,14 +47,14 @@ public class MediaPreviewViewModel extends ViewModel {
cursor.moveToPosition(activePosition);
- MediaRecord activeRecord = MediaRecord.from(context, cursor);
+ MediaRecord activeRecord = MediaRecord.from(cursor);
LinkedList rail = new LinkedList<>();
Media activeMedia = toMedia(activeRecord);
if (activeMedia != null) rail.add(activeMedia);
while (cursor.moveToPrevious()) {
- MediaRecord record = MediaRecord.from(context, cursor);
+ MediaRecord record = MediaRecord.from(cursor);
if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) {
Media media = toMedia(record);
if (media != null) rail.addFirst(media);
@@ -66,7 +66,7 @@ public class MediaPreviewViewModel extends ViewModel {
cursor.moveToPosition(activePosition);
while (cursor.moveToNext()) {
- MediaRecord record = MediaRecord.from(context, cursor);
+ MediaRecord record = MediaRecord.from(cursor);
if (record.getAttachment().getMmsId() == activeRecord.getAttachment().getMmsId()) {
Media media = toMedia(record);
if (media != null) rail.addLast(media);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/PreviewMediaAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/PreviewMediaAdapter.kt
new file mode 100644
index 000000000..781a46153
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/PreviewMediaAdapter.kt
@@ -0,0 +1,39 @@
+package org.thoughtcrime.securesms.mediapreview
+
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import org.thoughtcrime.securesms.attachments.Attachment
+import org.thoughtcrime.securesms.util.MediaUtil
+
+class PreviewMediaAdapter(val fragment: Fragment, val items: List) : FragmentStateAdapter(fragment) {
+ var autoPlayPosition = -1
+
+ override fun getItemCount(): Int {
+ return items.count()
+ }
+
+ override fun createFragment(position: Int): Fragment {
+ val attachment: Attachment = items[position]
+
+ val contentType = attachment.contentType
+ val args = bundleOf(
+ MediaPreviewFragment.DATA_URI to attachment.uri,
+ MediaPreviewFragment.DATA_CONTENT_TYPE to contentType,
+ MediaPreviewFragment.DATA_SIZE to attachment.size,
+ MediaPreviewFragment.AUTO_PLAY to (position == autoPlayPosition),
+ MediaPreviewFragment.VIDEO_GIF to attachment.isVideoGif,
+ )
+ val fragment = if (MediaUtil.isVideo(contentType)) {
+ VideoMediaPreviewFragment()
+ } else if (MediaUtil.isImageType(contentType)) {
+ ImageMediaPreviewFragment()
+ } else {
+ throw AssertionError("Unexpected media type: $contentType")
+ }
+
+ fragment.arguments = args
+
+ return fragment
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java
index 61caa9e94..2e7adbea0 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/mms/AttachmentManager.java
@@ -52,8 +52,10 @@ import org.thoughtcrime.securesms.components.location.SignalMapView;
import org.thoughtcrime.securesms.components.location.SignalPlace;
import org.thoughtcrime.securesms.conversation.MessageSendType;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
+import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.maps.PlacePickerActivity;
+import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory;
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity;
import org.thoughtcrime.securesms.payments.create.CreatePaymentFragmentArgs;
import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity;
@@ -469,13 +471,19 @@ public class AttachmentManager {
private void previewImageDraft(final @NonNull Slide slide) {
if (MediaPreviewActivity.isContentTypeSupported(slide.getContentType()) && slide.getUri() != null) {
- Intent intent = new Intent(context, MediaPreviewActivity.class);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
- intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orElse(null));
- intent.setDataAndType(slide.getUri(), slide.getContentType());
-
- context.startActivity(intent);
+ MediaIntentFactory.MediaPreviewArgs args = new MediaIntentFactory.MediaPreviewArgs(
+ MediaIntentFactory.NOT_IN_A_THREAD,
+ MediaIntentFactory.UNKNOWN_TIMESTAMP,
+ slide.getUri(),
+ slide.getContentType(),
+ slide.asAttachment().getSize(),
+ slide.getCaption().orElse(null),
+ false,
+ false,
+ false,
+ MediaDatabase.Sorting.Newest.ordinal(),
+ slide.isVideoGif());
+ context.startActivity(MediaIntentFactory.create(context, args));
}
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java
index 107b917ae..431e127e0 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java
@@ -103,6 +103,7 @@ public final class FeatureFlags {
private static final String CDS_V2_COMPAT = "android.cdsV2Compat.4";
public static final String STORIES_LOCALE = "android.stories.locale";
private static final String HIDE_CONTACTS = "android.hide.contacts";
+ public static final String MEDIA_PREVIEW_V2 = "android.mediaPreviewV2";
/**
* We will only store remote values for flags in this set. If you want a flag to be controllable
@@ -157,7 +158,8 @@ public final class FeatureFlags {
SMS_EXPORTER,
CDS_V2_COMPAT,
STORIES_LOCALE,
- HIDE_CONTACTS
+ HIDE_CONTACTS,
+ MEDIA_PREVIEW_V2
);
@VisibleForTesting
@@ -220,7 +222,8 @@ public final class FeatureFlags {
RECIPIENT_MERGE_V2,
CDS_V2_LOAD_TEST,
CDS_V2_COMPAT,
- STORIES
+ STORIES,
+ MEDIA_PREVIEW_V2
);
/**
@@ -565,6 +568,13 @@ public final class FeatureFlags {
return getBoolean(HIDE_CONTACTS, false);
}
+ /**
+ * Whether or not we should use the new media preview fragment implementation.
+ */
+ public static boolean mediaPreviewV2() {
+ return getBoolean(MEDIA_PREVIEW_V2, false);
+ }
+
/** Only for rendering debug info. */
public static synchronized @NonNull Map getMemoryValues() {
return new TreeMap<>(REMOTE_VALUES);
diff --git a/app/src/main/res/layout/activity_mediapreview_v2.xml b/app/src/main/res/layout/activity_mediapreview_v2.xml
new file mode 100644
index 000000000..b6ed0ae4a
--- /dev/null
+++ b/app/src/main/res/layout/activity_mediapreview_v2.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_media_preview_v2.xml b/app/src/main/res/layout/fragment_media_preview_v2.xml
new file mode 100644
index 000000000..02f8c6def
--- /dev/null
+++ b/app/src/main/res/layout/fragment_media_preview_v2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
\ No newline at end of file