mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 00:59:49 +01:00
Media Preview V2 Visual Redesign.
This commit is contained in:
@@ -5,7 +5,6 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -16,7 +15,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
|
||||
public final class ImageMediaPreviewFragment extends MediaPreviewFragment {
|
||||
private View bottomBarControlView;
|
||||
private MediaPreviewPlayerControlView bottomBarControlView;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@@ -41,26 +40,24 @@ 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);
|
||||
|
||||
public void cleanUp() {
|
||||
bottomBarControlView = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForwardButtonListener(View.OnClickListener listener) {
|
||||
ImageButton shareButton = bottomBarControlView.findViewById(R.id.image_preview_share);
|
||||
shareButton.setOnClickListener(listener);
|
||||
}
|
||||
public void pause() {}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View getBottomBarControls() {
|
||||
public ViewGroup getBottomBarControls() {
|
||||
return bottomBarControlView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBottomButtonControls(MediaPreviewPlayerControlView playerControlView) {
|
||||
bottomBarControlView = playerControlView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,12 @@ package org.thoughtcrime.securesms.mediapreview;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
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;
|
||||
@@ -81,15 +79,10 @@ public abstract class MediaPreviewFragment extends Fragment {
|
||||
checkMediaStillAvailable();
|
||||
}
|
||||
|
||||
public void cleanUp() {
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
}
|
||||
|
||||
abstract public void setShareButtonListener(View.OnClickListener listener);
|
||||
abstract public void setForwardButtonListener(View.OnClickListener listener);
|
||||
abstract public @Nullable View getBottomBarControls();
|
||||
public abstract void cleanUp();
|
||||
public abstract void pause();
|
||||
public abstract ViewGroup getBottomBarControls();
|
||||
public abstract void setBottomButtonControls(MediaPreviewPlayerControlView playerControlView);
|
||||
|
||||
private void checkMediaStillAvailable() {
|
||||
if (attachmentId == null) {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.thoughtcrime.securesms.mediapreview
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.exoplayer2.ui.PlayerControlView
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
|
||||
/**
|
||||
* The bottom bar for the media preview. This includes the standard seek bar as well as playback controls,
|
||||
* but adds forward and share buttons as well as a recyclerview that can be populated with a rail of thumbnails.
|
||||
*/
|
||||
class MediaPreviewPlayerControlView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
playbackAttrs: AttributeSet? = null
|
||||
) : PlayerControlView(context, attrs, defStyleAttr, playbackAttrs) {
|
||||
|
||||
val recyclerView: RecyclerView = findViewById(R.id.media_preview_album_rail)
|
||||
private val durationBar: LinearLayout = findViewById(R.id.exo_duration_viewgroup)
|
||||
private val videoControls: LinearLayout = findViewById(R.id.exo_button_viewgroup)
|
||||
private val shareButton: ImageButton = findViewById(R.id.exo_share)
|
||||
private val forwardButton: ImageButton = findViewById(R.id.exo_forward)
|
||||
|
||||
enum class MediaMode {
|
||||
IMAGE, VIDEO;
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromString(contentType: String): MediaMode {
|
||||
if (MediaUtil.isVideo(contentType)) return VIDEO
|
||||
if (MediaUtil.isImageType(contentType)) return IMAGE
|
||||
throw IllegalArgumentException("Unknown content type: $contentType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setShowPreviousButton(false)
|
||||
setShowNextButton(false)
|
||||
showShuffleButton = false
|
||||
showVrButton = false
|
||||
showTimeoutMs = -1
|
||||
}
|
||||
|
||||
fun setVisibility(mediaMode: MediaMode) {
|
||||
durationBar.visibility = if (mediaMode == MediaMode.VIDEO) VISIBLE else GONE
|
||||
videoControls.visibility = if (mediaMode == MediaMode.VIDEO) VISIBLE else INVISIBLE
|
||||
}
|
||||
|
||||
fun setShareButtonListener(listener: OnClickListener?) = shareButton.setOnClickListener(listener)
|
||||
|
||||
fun setForwardButtonListener(listener: OnClickListener?) = forwardButton.setOnClickListener(listener)
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class MediaPreviewV2Activity : AppCompatActivity(R.layout.activity_mediapreview_
|
||||
super.onCreate(savedInstanceState)
|
||||
setTheme(R.style.TextSecure_MediaPreview)
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
val systemBarColor = ContextCompat.getColor(this, R.color.media_preview_bar_background)
|
||||
val systemBarColor = ContextCompat.getColor(this, R.color.signal_dark_colorSurface)
|
||||
window.statusBarColor = systemBarColor
|
||||
window.navigationBarColor = systemBarColor
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT
|
||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
@@ -72,12 +72,10 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val args = MediaIntentFactory.requireArguments(requireArguments())
|
||||
|
||||
initializeViewModel(args)
|
||||
initializeToolbar(binding.toolbar)
|
||||
initializeViewPager()
|
||||
initializeFullScreenUi()
|
||||
initializeAlbumRail()
|
||||
anchorMarginsToBottomInsets(binding.mediaPreviewDetailsContainer)
|
||||
lifecycleDisposable += viewModel.state.distinctUntilChanged().observeOn(AndroidSchedulers.mainThread()).subscribe {
|
||||
bindCurrentState(it)
|
||||
@@ -92,10 +90,7 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
requireActivity().finish()
|
||||
}.show()
|
||||
}
|
||||
|
||||
viewModel.setShowThread(args.showThread)
|
||||
viewModel.setAlwaysShowAlbumRail(args.allMediaInRail)
|
||||
viewModel.setLeftIsRecent(args.leftIsRecent)
|
||||
viewModel.initialize(args.showThread, args.allMediaInRail, args.leftIsRecent)
|
||||
val sorting = MediaDatabase.Sorting.deserialize(args.sorting)
|
||||
viewModel.fetchAttachments(PartAuthority.requireAttachmentId(args.initialMediaUri), args.threadId, sorting)
|
||||
}
|
||||
@@ -105,6 +100,8 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
requireActivity().onBackPressed()
|
||||
}
|
||||
|
||||
toolbar.setTitleTextAppearance(requireContext(), R.style.Signal_Text_TitleMedium)
|
||||
toolbar.setSubtitleTextAppearance(requireContext(), R.style.Signal_Text_BodyMedium)
|
||||
binding.toolbar.inflateMenu(R.menu.media_preview)
|
||||
}
|
||||
|
||||
@@ -123,10 +120,9 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
})
|
||||
}
|
||||
|
||||
private fun initializeAlbumRail() {
|
||||
binding.mediaPreviewAlbumRail.itemAnimator = null // Or can crash when set to INVISIBLE while animating by FullscreenHelper https://issuetracker.google.com/issues/148720682
|
||||
binding.mediaPreviewAlbumRail.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
|
||||
binding.mediaPreviewAlbumRail.adapter = MediaRailAdapter(
|
||||
private fun initializeAlbumRail(recyclerView: RecyclerView, albumThumbnailMedia: List<Media?>, albumPosition: Int) {
|
||||
recyclerView.itemAnimator = null // Or can crash when set to INVISIBLE while animating by FullscreenHelper https://issuetracker.google.com/issues/148720682
|
||||
val mediaRailAdapter = MediaRailAdapter(
|
||||
GlideApp.with(this),
|
||||
object : MediaRailAdapter.RailItemListener {
|
||||
override fun onRailItemClicked(distanceFromActive: Int) {
|
||||
@@ -139,6 +135,9 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
},
|
||||
false
|
||||
)
|
||||
mediaRailAdapter.setMedia(albumThumbnailMedia, albumPosition)
|
||||
recyclerView.adapter = mediaRailAdapter
|
||||
recyclerView.smoothScrollToPosition(albumPosition)
|
||||
}
|
||||
|
||||
private fun initializeFullScreenUi() {
|
||||
@@ -152,21 +151,43 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
return
|
||||
}
|
||||
when (currentState.loadState) {
|
||||
MediaPreviewV2State.LoadState.READY -> bindReadyState(currentState)
|
||||
MediaPreviewV2State.LoadState.LOADED -> {
|
||||
bindReadyState(currentState)
|
||||
bindLoadedState(currentState)
|
||||
}
|
||||
MediaPreviewV2State.LoadState.DATA_LOADED -> bindDataLoadedState(currentState)
|
||||
MediaPreviewV2State.LoadState.MEDIA_READY -> bindMediaReadyState(currentState)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindReadyState(currentState: MediaPreviewV2State) {
|
||||
private fun bindDataLoadedState(currentState: MediaPreviewV2State) {
|
||||
(binding.mediaPager.adapter as MediaPreviewV2Adapter).updateBackingItems(currentState.mediaRecords.mapNotNull { it.attachment })
|
||||
if (binding.mediaPager.currentItem != currentState.position) {
|
||||
binding.mediaPager.setCurrentItem(currentState.position, false)
|
||||
val currentPosition = currentState.position
|
||||
if (binding.mediaPager.currentItem != currentPosition) {
|
||||
binding.mediaPager.setCurrentItem(currentPosition, false)
|
||||
}
|
||||
val currentItem: MediaDatabase.MediaRecord = currentState.mediaRecords[currentState.position]
|
||||
}
|
||||
|
||||
/**
|
||||
* These are binding steps that need a reference to the actual fragment within the pager.
|
||||
* This is not available until after a page has been chosen by the ViewPager, and we receive the
|
||||
* {@link OnPageChangeCallback}.
|
||||
*/
|
||||
private fun bindMediaReadyState(currentState: MediaPreviewV2State) {
|
||||
val currentPosition = currentState.position
|
||||
val currentItem: MediaDatabase.MediaRecord = currentState.mediaRecords[currentPosition]
|
||||
|
||||
// pause all other fragments
|
||||
childFragmentManager.fragments.map { fragment ->
|
||||
if (fragment.tag != "f$currentPosition") {
|
||||
(fragment as? MediaPreviewFragment)?.pause()
|
||||
}
|
||||
}
|
||||
|
||||
val mediaType: MediaPreviewPlayerControlView.MediaMode = if (currentItem.attachment?.isVideoGif == true) {
|
||||
MediaPreviewPlayerControlView.MediaMode.IMAGE
|
||||
} else {
|
||||
MediaPreviewPlayerControlView.MediaMode.fromString(currentItem.contentType)
|
||||
}
|
||||
binding.mediaPreviewPlaybackControls.setVisibility(mediaType)
|
||||
|
||||
binding.toolbar.title = getTitleText(currentItem, currentState.showThread)
|
||||
binding.toolbar.subtitle = getSubTitleText(currentItem)
|
||||
|
||||
@@ -184,16 +205,6 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These are binding steps that need a reference to the actual fragment within the pager.
|
||||
* This is not available until after a page has been chosen by the ViewPager, and we receive the
|
||||
* {@link OnPageChangeCallback}.
|
||||
*/
|
||||
private fun bindLoadedState(currentState: MediaPreviewV2State) {
|
||||
val currentItem: MediaDatabase.MediaRecord = currentState.mediaRecords[currentState.position]
|
||||
|
||||
val albumThumbnailMedia = if (currentState.allMediaInAlbumRail) {
|
||||
currentState.mediaRecords.map { it.toMedia() }
|
||||
} else {
|
||||
@@ -201,11 +212,10 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
.filter { it.attachment != null && it.attachment!!.mmsId == currentItem.attachment?.mmsId }
|
||||
.map { it.toMedia() }
|
||||
}
|
||||
|
||||
val caption = currentItem.attachment?.caption
|
||||
|
||||
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)
|
||||
val albumRailEnabled = albumThumbnailMedia.size > 1
|
||||
|
||||
if (caption != null) {
|
||||
binding.mediaPreviewCaption.text = caption
|
||||
@@ -214,24 +224,21 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
binding.mediaPreviewCaption.visibility = View.GONE
|
||||
}
|
||||
|
||||
val fragmentTag = "f${currentState.position}"
|
||||
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 {
|
||||
binding.mediaPreviewDetailsContainer.visibility = View.VISIBLE
|
||||
binding.mediaPreviewPlaybackControls.setShareButtonListener { share(currentItem) }
|
||||
binding.mediaPreviewPlaybackControls.setForwardButtonListener { forward(currentItem) }
|
||||
|
||||
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.findViewById(R.id.media_preview_album_rail)
|
||||
if (albumRailEnabled) {
|
||||
val albumPosition = albumThumbnailMedia.indexOfFirst { it?.uri == currentItem.attachment?.uri }
|
||||
initializeAlbumRail(albumRail, albumThumbnailMedia, albumPosition)
|
||||
}
|
||||
binding.mediaPreviewPlaybackControlsContainer.removeAllViews()
|
||||
if (playbackControls != null) {
|
||||
val params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
playbackControls.layoutParams = params
|
||||
binding.mediaPreviewPlaybackControlsContainer.addView(playbackControls)
|
||||
}
|
||||
currentFragment?.setShareButtonListener { share(currentItem) }
|
||||
currentFragment?.setForwardButtonListener { forward(currentItem) }
|
||||
albumRail.visibility = if (albumRailEnabled) View.VISIBLE else View.GONE
|
||||
val currentFragment: MediaPreviewFragment? = getMediaPreviewFragmentFromChildFragmentManager(currentPosition)
|
||||
currentFragment?.setBottomButtonControls(binding.mediaPreviewPlaybackControls)
|
||||
}
|
||||
|
||||
private fun getMediaPreviewFragmentFromChildFragmentManager(currentPosition: Int) = childFragmentManager.findFragmentByTag("f$currentPosition") as? MediaPreviewFragment
|
||||
|
||||
private fun getTitleText(mediaRecord: MediaDatabase.MediaRecord, showThread: Boolean): String {
|
||||
val recipient: Recipient = Recipient.live(mediaRecord.recipientId).get()
|
||||
val defaultFromString: String = if (mediaRecord.isOutgoing) {
|
||||
@@ -315,7 +322,7 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
}
|
||||
|
||||
override fun onMediaReady() {
|
||||
Log.d(TAG, "onMediaReady()")
|
||||
viewModel.setMediaReady()
|
||||
}
|
||||
|
||||
private fun forward(mediaItem: MediaDatabase.MediaRecord) {
|
||||
@@ -400,6 +407,11 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
getMediaPreviewFragmentFromChildFragmentManager(binding.mediaPager.currentItem)?.pause()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ARGS_KEY: String = "args"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ data class MediaPreviewV2State(
|
||||
val position: Int = 0,
|
||||
val showThread: Boolean = false,
|
||||
val allMediaInAlbumRail: Boolean = false,
|
||||
val leftIsRecent: Boolean = false
|
||||
val leftIsRecent: Boolean = false,
|
||||
) {
|
||||
enum class LoadState { INIT, READY, LOADED }
|
||||
enum class LoadState { INIT, DATA_LOADED, MEDIA_READY }
|
||||
}
|
||||
|
||||
@@ -24,46 +24,44 @@ class MediaPreviewV2ViewModel : ViewModel() {
|
||||
|
||||
val state: Flowable<MediaPreviewV2State> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
fun fetchAttachments(startingAttachmentId: AttachmentId, threadId: Long, sorting: MediaDatabase.Sorting) {
|
||||
disposables += store.update(repository.getAttachments(startingAttachmentId, threadId, sorting)) {
|
||||
result: MediaPreviewRepository.Result, oldState: MediaPreviewV2State ->
|
||||
if (oldState.leftIsRecent) {
|
||||
oldState.copy(
|
||||
position = result.initialPosition,
|
||||
mediaRecords = result.records,
|
||||
loadState = MediaPreviewV2State.LoadState.READY,
|
||||
)
|
||||
} else {
|
||||
oldState.copy(
|
||||
position = result.records.size - result.initialPosition - 1,
|
||||
mediaRecords = result.records.reversed(),
|
||||
loadState = MediaPreviewV2State.LoadState.READY,
|
||||
)
|
||||
fun fetchAttachments(startingAttachmentId: AttachmentId, threadId: Long, sorting: MediaDatabase.Sorting, forceRefresh: Boolean = false) {
|
||||
if (store.state.loadState == MediaPreviewV2State.LoadState.INIT || forceRefresh) {
|
||||
disposables += store.update(repository.getAttachments(startingAttachmentId, threadId, sorting)) {
|
||||
result: MediaPreviewRepository.Result, oldState: MediaPreviewV2State ->
|
||||
if (oldState.leftIsRecent) {
|
||||
oldState.copy(
|
||||
position = result.initialPosition,
|
||||
mediaRecords = result.records,
|
||||
loadState = MediaPreviewV2State.LoadState.DATA_LOADED,
|
||||
)
|
||||
} else {
|
||||
oldState.copy(
|
||||
position = result.records.size - result.initialPosition - 1,
|
||||
mediaRecords = result.records.reversed(),
|
||||
loadState = MediaPreviewV2State.LoadState.DATA_LOADED,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setShowThread(value: Boolean) {
|
||||
store.update { oldState ->
|
||||
oldState.copy(showThread = value)
|
||||
}
|
||||
}
|
||||
|
||||
fun setAlwaysShowAlbumRail(value: Boolean) {
|
||||
store.update { oldState ->
|
||||
oldState.copy(allMediaInAlbumRail = value)
|
||||
}
|
||||
}
|
||||
|
||||
fun setLeftIsRecent(value: Boolean) {
|
||||
store.update { oldState ->
|
||||
oldState.copy(leftIsRecent = value)
|
||||
fun initialize(showThread: Boolean, allMediaInAlbumRail: Boolean, leftIsRecent: Boolean) {
|
||||
if (store.state.loadState == MediaPreviewV2State.LoadState.INIT) {
|
||||
store.update { oldState ->
|
||||
oldState.copy(showThread = showThread, allMediaInAlbumRail = allMediaInAlbumRail, leftIsRecent = leftIsRecent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setCurrentPage(position: Int) {
|
||||
store.update { oldState ->
|
||||
oldState.copy(position = position, loadState = MediaPreviewV2State.LoadState.LOADED)
|
||||
oldState.copy(position = position)
|
||||
}
|
||||
}
|
||||
|
||||
fun setMediaReady() {
|
||||
store.update { oldState ->
|
||||
oldState.copy(loadState = MediaPreviewV2State.LoadState.MEDIA_READY)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ 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;
|
||||
@@ -30,8 +29,6 @@ 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) {
|
||||
@@ -90,21 +87,15 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment {
|
||||
});
|
||||
|
||||
if (isVideoGif) {
|
||||
videoView.hideControls();
|
||||
videoView.loopForever();
|
||||
}
|
||||
|
||||
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 = getBottomBarControls();
|
||||
final PlayerControlView playbackControls = videoView.getControlView();
|
||||
if (playbackControls != null) {
|
||||
boolean shouldShowSkipButtons = videoView.getDuration() > MINIMUM_DURATION_FOR_SKIP_MS;
|
||||
playbackControls.setShowFastForwardButton(shouldShowSkipButtons);
|
||||
@@ -149,20 +140,16 @@ 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 getBottomBarControls() {
|
||||
return videoView != null && !isVideoGif ? videoView.getControlView() : null;
|
||||
public MediaPreviewPlayerControlView getBottomBarControls() {
|
||||
return (MediaPreviewPlayerControlView) videoView.getControlView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBottomButtonControls(@NonNull MediaPreviewPlayerControlView playerControlView) {
|
||||
videoView.setControlView(playerControlView);
|
||||
updateSkipButtonState();
|
||||
}
|
||||
|
||||
private @NonNull Uri getUri() {
|
||||
|
||||
@@ -19,15 +19,12 @@ package org.thoughtcrime.securesms.video;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.TypedArrayKt;
|
||||
import androidx.core.content.res.TypedArrayUtils;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
@@ -45,6 +42,7 @@ import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewPlayerControlView;
|
||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -56,10 +54,10 @@ public class VideoPlayer extends FrameLayout {
|
||||
private static final String TAG = Log.tag(VideoPlayer.class);
|
||||
|
||||
private final PlayerView exoView;
|
||||
private final PlayerControlView exoControls;
|
||||
private final DefaultMediaSourceFactory mediaSourceFactory;
|
||||
|
||||
private ExoPlayer exoPlayer;
|
||||
private PlayerControlView exoControls;
|
||||
private Window window;
|
||||
private PlayerStateCallback playerStateCallback;
|
||||
private PlayerPositionDiscontinuityCallback playerPositionDiscontinuityCallback;
|
||||
@@ -233,6 +231,11 @@ public class VideoPlayer extends FrameLayout {
|
||||
return this.exoControls;
|
||||
}
|
||||
|
||||
public void setControlView(MediaPreviewPlayerControlView controller) {
|
||||
exoControls = controller;
|
||||
exoControls.setPlayer(exoPlayer);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (this.exoPlayer != null) {
|
||||
exoPlayer.stop();
|
||||
|
||||
Reference in New Issue
Block a user