From 469cab284e485c2e1babf77d8559d15ddf8de78a Mon Sep 17 00:00:00 2001 From: Nicholas Date: Fri, 21 Oct 2022 17:31:50 -0400 Subject: [PATCH] Media Preview V2 Visual Redesign --- .../securesms/MediaPreviewActivity.java | 17 +-- .../ImageMediaPreviewFragment.java | 22 ++++ .../mediapreview/MediaPreviewFragment.java | 6 +- .../mediapreview/MediaPreviewV2Fragment.kt | 42 +++---- .../VideoMediaPreviewFragment.java | 23 +++- .../securesms/util/FeatureFlags.java | 9 -- .../securesms/video/VideoPlayer.java | 11 +- .../drawable/ic_share_24_outline_white.xml | 9 ++ .../res/layout/exo_player_control_view.xml | 119 ++++++++++++++++++ .../res/layout/fragment_media_preview_v2.xml | 28 ++--- .../layout/image_media_preview_bottom_bar.xml | 40 ++++++ app/src/main/res/menu/media_preview.xml | 13 +- app/src/main/res/values/strings.xml | 2 + 13 files changed, 254 insertions(+), 87 deletions(-) create mode 100644 app/src/main/res/drawable/ic_share_24_outline_white.xml create mode 100644 app/src/main/res/layout/exo_player_control_view.xml create mode 100644 app/src/main/res/layout/image_media_preview_bottom_bar.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 0dfb5a705..a1d6a2289 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; @@ -490,17 +489,8 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity @Override public boolean onPrepareOptionsMenu(Menu menu) { if (!isMediaInDb()) { - menu.findItem(R.id.media_preview__overview).setVisible(false); menu.findItem(R.id.delete).setVisible(false); } - - // Restricted to API26 because of MemoryFileUtil not supporting lower API levels well - menu.findItem(R.id.media_preview__share).setVisible(Build.VERSION.SDK_INT >= 26); - - if (cameFromAllMedia) { - menu.findItem(R.id.media_preview__overview).setVisible(false); - } - super.onPrepareOptionsMenu(menu); return true; } @@ -511,9 +501,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity int itemId = item.getItemId(); - if (itemId == R.id.media_preview__overview) { showOverview(); return true; } - if (itemId == R.id.media_preview__forward) { forward(); return true; } - if (itemId == R.id.media_preview__share) { share(); return true; } if (itemId == R.id.save) { saveToDisk(); return true; } if (itemId == R.id.delete) { deleteMedia(); return true; } if (itemId == android.R.id.home) { finish(); return true; } @@ -701,7 +688,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity @Override public @Nullable View getPlaybackControls(int position) { if (mediaPreviewFragment != null) { - return mediaPreviewFragment.getPlaybackControls(); + return mediaPreviewFragment.getBottomBarControls(); } return null; } @@ -831,7 +818,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity @Override public @Nullable View getPlaybackControls(int position) { MediaPreviewFragment mediaView = mediaFragments.get(position); - if (mediaView != null) return mediaView.getPlaybackControls(); + if (mediaView != null) return mediaView.getBottomBarControls(); return null; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java index 73467069e..2e93f72ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/ImageMediaPreviewFragment.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageButton; import androidx.annotation.Nullable; @@ -15,6 +16,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.util.MediaUtil; public final class ImageMediaPreviewFragment extends MediaPreviewFragment { + private View bottomBarControlView; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -39,6 +41,26 @@ public final class ImageMediaPreviewFragment extends MediaPreviewFragment { zoomingImageView.setOnClickListener(v -> events.singleTapOnMedia()); + bottomBarControlView = getLayoutInflater().inflate(R.layout.image_media_preview_bottom_bar, null); return zoomingImageView; } + + @Override + public void setShareButtonListener(View.OnClickListener listener) { + ImageButton forwardButton = bottomBarControlView.findViewById(R.id.image_preview_forward); + forwardButton.setOnClickListener(listener); + + } + + @Override + public void setForwardButtonListener(View.OnClickListener listener) { + ImageButton shareButton = bottomBarControlView.findViewById(R.id.image_preview_share); + shareButton.setOnClickListener(listener); + } + + @Nullable + @Override + public View getBottomBarControls() { + return bottomBarControlView; + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java index b90686c46..81d99a8c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java @@ -87,9 +87,9 @@ public abstract class MediaPreviewFragment extends Fragment { public void pause() { } - public @Nullable PlayerControlView getPlaybackControls() { - return null; - } + abstract public void setShareButtonListener(View.OnClickListener listener); + abstract public void setForwardButtonListener(View.OnClickListener listener); + abstract public @Nullable View getBottomBarControls(); private void checkMediaStillAvailable() { if (attachmentId == null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt index 3b818a8a7..53a901890 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt @@ -7,6 +7,7 @@ import android.content.DialogInterface import android.content.Intent import android.os.Build import android.os.Bundle +import android.view.LayoutInflater import android.view.Menu import android.view.View import android.view.ViewGroup @@ -20,7 +21,6 @@ import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewpager2.widget.ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback -import com.google.android.exoplayer2.ui.PlayerControlView import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar @@ -35,7 +35,6 @@ import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectFor import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs import org.thoughtcrime.securesms.database.MediaDatabase import org.thoughtcrime.securesms.databinding.FragmentMediaPreviewV2Binding -import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.PartAuthority @@ -64,13 +63,18 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med fullscreenHelper = FullscreenHelper(requireActivity()) } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + lifecycleDisposable.bindTo(viewLifecycleOwner) + return super.onCreateView(inflater, container, savedInstanceState) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val args = MediaIntentFactory.requireArguments(requireArguments()) initializeViewModel(args) - initializeToolbar(binding.toolbar, args) + initializeToolbar(binding.toolbar) initializeViewPager() initializeFullScreenUi() initializeAlbumRail() @@ -96,19 +100,12 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med viewModel.fetchAttachments(PartAuthority.requireAttachmentId(args.initialMediaUri), args.threadId, sorting) } - private fun initializeToolbar(toolbar: MaterialToolbar, args: MediaIntentFactory.MediaPreviewArgs) { + private fun initializeToolbar(toolbar: MaterialToolbar) { toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() } binding.toolbar.inflateMenu(R.menu.media_preview) - - // Restricted to API26 because of MemoryFileUtil not supporting lower API levels well - binding.toolbar.menu.findItem(R.id.media_preview__share).isVisible = Build.VERSION.SDK_INT >= 26 - - if (args.hideAllMedia) { - binding.toolbar.menu.findItem(R.id.media_preview__overview).isVisible = false - } } private fun initializeViewPager() { @@ -175,15 +172,11 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med val menu: Menu = binding.toolbar.menu if (currentItem.threadId == MediaIntentFactory.NOT_IN_A_THREAD.toLong()) { - menu.findItem(R.id.media_preview__overview).isVisible = false menu.findItem(R.id.delete).isVisible = false } binding.toolbar.setOnMenuItemClickListener { when (it.itemId) { - R.id.media_preview__overview -> showOverview(currentItem.threadId) - R.id.media_preview__forward -> forward(currentItem) - R.id.media_preview__share -> share(currentItem) R.id.save -> saveToDisk(currentItem) R.id.delete -> deleteMedia(currentItem) android.R.id.home -> requireActivity().finish() @@ -214,12 +207,16 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med (binding.mediaPreviewAlbumRail.adapter as MediaRailAdapter).setMedia(albumThumbnailMedia, currentState.position) binding.mediaPreviewAlbumRail.smoothScrollToPosition(currentState.position) - binding.mediaPreviewCaptionContainer.visibility = if (caption == null) View.GONE else View.VISIBLE - binding.mediaPreviewCaption.text = caption + if (caption != null) { + binding.mediaPreviewCaption.text = caption + binding.mediaPreviewCaption.visibility = View.VISIBLE + } else { + binding.mediaPreviewCaption.visibility = View.GONE + } val fragmentTag = "f${currentState.position}" - val currentFragment: Fragment? = childFragmentManager.findFragmentByTag(fragmentTag) - val playbackControls: PlayerControlView? = (currentFragment as? MediaPreviewFragment)?.playbackControls + val currentFragment: MediaPreviewFragment? = childFragmentManager.findFragmentByTag(fragmentTag) as? MediaPreviewFragment + val playbackControls: View? = currentFragment?.bottomBarControls if (albumThumbnailMedia.size <= 1 && caption == null && playbackControls == null) { binding.mediaPreviewDetailsContainer.visibility = View.GONE } else { @@ -231,6 +228,8 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med playbackControls.layoutParams = params binding.mediaPreviewPlaybackControlsContainer.addView(playbackControls) } + currentFragment?.setShareButtonListener { share(currentItem) } + currentFragment?.setForwardButtonListener { forward(currentItem) } } private fun getTitleText(mediaRecord: MediaDatabase.MediaRecord, showThread: Boolean): String { @@ -319,11 +318,6 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med Log.d(TAG, "onMediaReady()") } - private fun showOverview(threadId: Long) { - val context = requireContext() - context.startActivity(MediaOverviewActivity.forThread(context, threadId)) - } - private fun forward(mediaItem: MediaDatabase.MediaRecord) { val attachment = mediaItem.attachment val uri = attachment?.uri diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java index 4adef0639..8530cd686 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java @@ -6,6 +6,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageButton; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -29,6 +30,8 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { private VideoPlayer videoView; private boolean isVideoGif; + private ImageButton shareButton; + private ImageButton forwardButton; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -92,12 +95,16 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { } videoView.setOnClickListener(v -> events.singleTapOnMedia()); - + final PlayerControlView controlView = videoView.getControlView(); + if (controlView != null) { + shareButton = controlView.findViewById(R.id.exo_share); + forwardButton = controlView.findViewById(R.id.exo_forward); + } return itemView; } private void updateSkipButtonState() { - final PlayerControlView playbackControls = getPlaybackControls(); + final PlayerControlView playbackControls = getBottomBarControls(); if (playbackControls != null) { boolean shouldShowSkipButtons = videoView.getDuration() > MINIMUM_DURATION_FOR_SKIP_MS; playbackControls.setShowFastForwardButton(shouldShowSkipButtons); @@ -142,9 +149,19 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { } } + @Override + public void setShareButtonListener(View.OnClickListener listener) { + shareButton.setOnClickListener(listener); + } + + @Override + public void setForwardButtonListener(View.OnClickListener listener) { + forwardButton.setOnClickListener(listener); + } + @Nullable @Override - public PlayerControlView getPlaybackControls() { + public PlayerControlView getBottomBarControls() { return videoView != null && !isVideoGif ? videoView.getControlView() : null; } 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 7b9e84b47..5000d513c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -102,7 +102,6 @@ public final class FeatureFlags { private static final String SMS_EXPORTER = "android.sms.exporter.2"; public static final String STORIES_LOCALE = "android.stories.locale.1"; private static final String HIDE_CONTACTS = "android.hide.contacts"; - private static final String MEDIA_PREVIEW_V2 = "android.mediaPreviewV2"; private static final String SMS_EXPORT_MEGAPHONE_DELAY_DAYS = "android.smsExport.megaphoneDelayDays"; public static final String CREDIT_CARD_PAYMENTS = "android.credit.card.payments"; @@ -159,7 +158,6 @@ public final class FeatureFlags { SMS_EXPORTER, STORIES_LOCALE, HIDE_CONTACTS, - MEDIA_PREVIEW_V2, SMS_EXPORT_MEGAPHONE_DELAY_DAYS, CREDIT_CARD_PAYMENTS ); @@ -557,13 +555,6 @@ 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); - } - /** * Number of days to postpone the sms export megaphone and Phase 1 start. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java b/app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java index 6367a9a3c..3a5a57f0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java @@ -90,8 +90,7 @@ public class VideoPlayer extends FrameLayout { this.mediaSourceFactory = new DefaultMediaSourceFactory(context); this.exoView = findViewById(R.id.video_view); - this.exoControls = new PlayerControlView(getContext()); - this.exoControls.setShowTimeoutMs(-1); + this.exoControls = createPlayerControls(getContext()); this.exoPlayerListener = new ExoPlayerListener(); this.playerListener = new Player.Listener() { @@ -129,6 +128,14 @@ public class VideoPlayer extends FrameLayout { }; } + private PlayerControlView createPlayerControls(Context context) { + final PlayerControlView playerControlView = new PlayerControlView(context); + playerControlView.setShowTimeoutMs(-1); + playerControlView.setShowNextButton(false); + playerControlView.setShowPreviousButton(false); + return playerControlView; + } + private MediaItem mediaItem; public void setVideoSource(@NonNull VideoSlide videoSource, boolean autoplay, String poolTag) { diff --git a/app/src/main/res/drawable/ic_share_24_outline_white.xml b/app/src/main/res/drawable/ic_share_24_outline_white.xml new file mode 100644 index 000000000..a9ea18e34 --- /dev/null +++ b/app/src/main/res/drawable/ic_share_24_outline_white.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/exo_player_control_view.xml b/app/src/main/res/layout/exo_player_control_view.xml new file mode 100644 index 000000000..6e4721117 --- /dev/null +++ b/app/src/main/res/layout/exo_player_control_view.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_media_preview_v2.xml b/app/src/main/res/layout/fragment_media_preview_v2.xml index 83802f0b5..2fec133be 100644 --- a/app/src/main/res/layout/fragment_media_preview_v2.xml +++ b/app/src/main/res/layout/fragment_media_preview_v2.xml @@ -24,27 +24,17 @@ android:visibility="gone" tools:visibility="visible"> - - - - - + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp" + style="@style/Signal.Text.Body" + android:textColor="@color/core_white" + android:gravity="bottom" + tools:text="With great power comes great responsibility." /> + + + + + + + + + + + + diff --git a/app/src/main/res/menu/media_preview.xml b/app/src/main/res/menu/media_preview.xml index b727524b0..b44746460 100644 --- a/app/src/main/res/menu/media_preview.xml +++ b/app/src/main/res/menu/media_preview.xml @@ -1,23 +1,12 @@ - - - + app:showAsAction="never"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d2c4cffd9..9afcbb416 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3237,6 +3237,8 @@ Forward Share All media + Edit + Media preview