From 32fbbf2b557d60d320212bebc0a704b783ae8b7a Mon Sep 17 00:00:00 2001 From: Nicholas Date: Tue, 18 Oct 2022 16:00:12 -0400 Subject: [PATCH] Add seek buttons for videos longer than 30s. --- .../mediapreview/MediaPreviewFragment.java | 4 +++- .../mediapreview/MediaPreviewV2Fragment.kt | 22 ++++++++++--------- .../VideoMediaPreviewFragment.java | 18 ++++++++++++++- .../securesms/video/VideoPlayer.java | 2 +- .../video/exo/SimpleExoPlayerPool.kt | 3 +++ 5 files changed, 36 insertions(+), 13 deletions(-) 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 e24b69dec..b90686c46 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewFragment.java @@ -9,6 +9,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import com.google.android.exoplayer2.ui.PlayerControlView; + import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.database.SignalDatabase; @@ -85,7 +87,7 @@ public abstract class MediaPreviewFragment extends Fragment { public void pause() { } - public @Nullable View getPlaybackControls() { + public @Nullable PlayerControlView getPlaybackControls() { return 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 f2c413e51..3b818a8a7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewV2Fragment.kt @@ -20,6 +20,7 @@ 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 @@ -199,8 +200,7 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med */ private fun bindLoadedState(currentState: MediaPreviewV2State) { val currentItem: MediaDatabase.MediaRecord = currentState.mediaRecords[currentState.position] - val currentFragment: Fragment? = childFragmentManager.findFragmentByTag("f${currentState.position}") - val playbackControls = (currentFragment as? MediaPreviewFragment)?.playbackControls + val albumThumbnailMedia = if (currentState.allMediaInAlbumRail) { currentState.mediaRecords.map { it.toMedia() } } else { @@ -209,11 +209,7 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med .map { it.toMedia() } } val caption = currentItem.attachment?.caption - if (albumThumbnailMedia.size <= 1 && caption == null && playbackControls == null) { - binding.mediaPreviewDetailsContainer.visibility = View.GONE - } else { - binding.mediaPreviewDetailsContainer.visibility = View.VISIBLE - } + binding.mediaPreviewAlbumRail.visibility = if (albumThumbnailMedia.size <= 1) View.GONE else View.VISIBLE (binding.mediaPreviewAlbumRail.adapter as MediaRailAdapter).setMedia(albumThumbnailMedia, currentState.position) binding.mediaPreviewAlbumRail.smoothScrollToPosition(currentState.position) @@ -221,13 +217,19 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med binding.mediaPreviewCaptionContainer.visibility = if (caption == null) View.GONE else View.VISIBLE binding.mediaPreviewCaption.text = caption + val fragmentTag = "f${currentState.position}" + val currentFragment: Fragment? = childFragmentManager.findFragmentByTag(fragmentTag) + val playbackControls: PlayerControlView? = (currentFragment as? MediaPreviewFragment)?.playbackControls + if (albumThumbnailMedia.size <= 1 && caption == null && playbackControls == null) { + binding.mediaPreviewDetailsContainer.visibility = View.GONE + } else { + binding.mediaPreviewDetailsContainer.visibility = View.VISIBLE + } + binding.mediaPreviewPlaybackControlsContainer.removeAllViews() if (playbackControls != null) { val params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) playbackControls.layoutParams = params - binding.mediaPreviewPlaybackControlsContainer.removeAllViews() binding.mediaPreviewPlaybackControlsContainer.addView(playbackControls) - } else { - binding.mediaPreviewPlaybackControlsContainer.removeAllViews() } } 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 466d75c47..4adef0639 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/VideoMediaPreviewFragment.java @@ -10,6 +10,8 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.ui.PlayerControlView; + import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner; @@ -17,10 +19,14 @@ import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.video.VideoPlayer; +import java.util.concurrent.TimeUnit; + public final class VideoMediaPreviewFragment extends MediaPreviewFragment { private static final String TAG = Log.tag(VideoMediaPreviewFragment.class); + private static final Long MINIMUM_DURATION_FOR_SKIP_MS = TimeUnit.MILLISECONDS.convert(30, TimeUnit.SECONDS); + private VideoPlayer videoView; private boolean isVideoGif; @@ -90,6 +96,15 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { return itemView; } + private void updateSkipButtonState() { + final PlayerControlView playbackControls = getPlaybackControls(); + if (playbackControls != null) { + boolean shouldShowSkipButtons = videoView.getDuration() > MINIMUM_DURATION_FOR_SKIP_MS; + playbackControls.setShowFastForwardButton(shouldShowSkipButtons); + playbackControls.setShowRewindButton(shouldShowSkipButtons); + } + } + @Override public void onDestroyView() { super.onDestroyView(); @@ -127,8 +142,9 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment { } } + @Nullable @Override - public View getPlaybackControls() { + public PlayerControlView getPlaybackControls() { return videoView != null && !isVideoGif ? videoView.getControlView() : null; } 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 5f8305ba9..6367a9a3c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/video/VideoPlayer.java @@ -222,7 +222,7 @@ public class VideoPlayer extends FrameLayout { super.setOnClickListener(l); } - public @Nullable View getControlView() { + public @Nullable PlayerControlView getControlView() { return this.exoControls; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/video/exo/SimpleExoPlayerPool.kt b/app/src/main/java/org/thoughtcrime/securesms/video/exo/SimpleExoPlayerPool.kt index 7268e2a43..1737f4e0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/video/exo/SimpleExoPlayerPool.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/video/exo/SimpleExoPlayerPool.kt @@ -61,6 +61,8 @@ class SimpleExoPlayerPool(context: Context) : ExoPlayerPool(MAXIMUM_R override fun createPlayer(): ExoPlayer { return ExoPlayer.Builder(context) .setMediaSourceFactory(mediaSourceFactory) + .setSeekBackIncrementMs(SEEK_INTERVAL.inWholeMilliseconds) + .setSeekForwardIncrementMs(SEEK_INTERVAL.inWholeMilliseconds) .build() } @@ -68,6 +70,7 @@ class SimpleExoPlayerPool(context: Context) : ExoPlayerPool(MAXIMUM_R private const val MAXIMUM_RESERVED_PLAYERS = 1 private const val MAXIMUM_SUPPORTED_PLAYBACK_PRE_23 = 6 private const val MAXIMUM_SUPPORTED_PLAYBACK_PRE_23_LOW_MEM = 3 + private val SEEK_INTERVAL = 15.seconds } }