From a7c13450852e8cf2c13942800025ab7b0895d067 Mon Sep 17 00:00:00 2001 From: Vivianne Date: Thu, 16 Jan 2020 10:01:02 -0800 Subject: [PATCH] Small changes to the media player (#1572) * Resolving some issues with media playback. * Changing ViewVideoFragment so that it fully handles autohiding the media controller, allowing consistency between it and the toolbar * Fixed an issue where the toolbar and description were not fading in * Do not hide the toolbar/media player if the video is loading or paused * Created "ExposedPlayPausedVideoView" to allow hooking play/pause * Fix back button when viewing videos * Back button did not work if video controls were visible. * Tweak audio player * Always show the controls when audio begins playing * Do not auto-hide the controls if the player is playing audio * Address PR comments * Use overloaded constructor * Remove m prefix * Fix subtle media player issues * Fix audio player incorrectly auto-hiding after hiding/showing toolbar * Only subscribe touch listener once content is ready - Prevents top toolbar visibility from getting out of phase with audio player visibility if hidden during load --- .../keylesspalace/tusky/ViewMediaActivity.kt | 6 ++ .../tusky/fragment/ViewVideoFragment.kt | 60 ++++++++++++++++--- .../tusky/view/ExposedPlayPauseVideoView.kt | 33 ++++++++++ .../main/res/layout/fragment_view_video.xml | 2 +- 4 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/keylesspalace/tusky/view/ExposedPlayPauseVideoView.kt diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt index 66bf8e8d..ca57b446 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt @@ -186,8 +186,14 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener for (listener in toolbarVisibilityListeners) { listener(isToolbarVisible) } + val visibility = if (isToolbarVisible) View.VISIBLE else View.INVISIBLE val alpha = if (isToolbarVisible) 1.0f else 0.0f + if (isToolbarVisible) { + // If to be visible, need to make visible immediately and animate alpha + toolbar.alpha = 0.0f + toolbar.visibility = visibility + } toolbar.animate().alpha(alpha) .setListener(object : AnimatorListenerAdapter() { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt index 34036107..0b3a23a7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt @@ -21,6 +21,7 @@ import android.annotation.SuppressLint import android.os.Bundle import android.os.Handler import android.os.Looper +import android.view.KeyEvent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -31,6 +32,7 @@ import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.visible +import com.keylesspalace.tusky.view.ExposedPlayPauseVideoView import kotlinx.android.synthetic.main.activity_view_media.* import kotlinx.android.synthetic.main.fragment_view_video.* @@ -41,11 +43,13 @@ class ViewVideoFragment : ViewMediaFragment() { // Hoist toolbar hiding to activity so it can track state across different fragments // This is explicitly stored as runnable so that we pass it to the handler later for cancellation mediaActivity.onPhotoTap() + mediaController.hide() } private lateinit var mediaActivity: ViewMediaActivity private val TOOLBAR_HIDE_DELAY_MS = 3000L override lateinit var descriptionView : TextView private lateinit var mediaController : MediaController + private var isAudio = false override fun setUserVisibleHint(isVisibleToUser: Boolean) { // Start/pause/resume video playback as fragment is shown/hidden @@ -72,14 +76,43 @@ class ViewVideoFragment : ViewMediaFragment() { videoView.transitionName = url videoView.setVideoPath(url) - mediaController = MediaController(mediaActivity) + mediaController = object : MediaController(mediaActivity) { + override fun show(timeout: Int) { + // We're doing manual auto-close management. + // Also, take focus back from the pause button so we can use the back button. + super.show(0) + mediaController.requestFocus() + } + + override fun dispatchKeyEvent(event: KeyEvent?): Boolean { + if (event?.keyCode == KeyEvent.KEYCODE_BACK) { + if (event.action == KeyEvent.ACTION_UP) { + hide() + activity?.supportFinishAfterTransition() + } + return true + } + return super.dispatchKeyEvent(event) + } + } + mediaController.setMediaPlayer(videoView) videoView.setMediaController(mediaController) videoView.requestFocus() - videoView.setOnTouchListener { _, _ -> - mediaActivity.onPhotoTap() - false - } + videoView.setPlayPauseListener(object: ExposedPlayPauseVideoView.PlayPauseListener { + override fun onPause() { + handler.removeCallbacks(hideToolbar) + } + override fun onPlay() { + // Audio doesn't cause the controller to show automatically, + // and we only want to hide the toolbar if it's a video. + if (isAudio) { + mediaController.show() + } else { + hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS) + } + } + }) videoView.setOnPreparedListener { mp -> val containerWidth = videoContainer.measuredWidth.toFloat() val containerHeight = videoContainer.measuredHeight.toFloat() @@ -94,10 +127,16 @@ class ViewVideoFragment : ViewMediaFragment() { videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT } + // Wait until the media is loaded before accepting taps as we don't want toolbar to + // be hidden until then. + videoView.setOnTouchListener { _, _ -> + mediaActivity.onPhotoTap() + false + } + progressBar.hide() mp.isLooping = true if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) { - hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS) videoView.start() } } @@ -126,6 +165,7 @@ class ViewVideoFragment : ViewMediaFragment() { throw IllegalArgumentException("attachment has to be set") } url = attachment.url + isAudio = attachment.type == Attachment.Type.AUDIO finalizeViewSetup(url, attachment.previewUrl, attachment.description) } @@ -136,6 +176,12 @@ class ViewVideoFragment : ViewMediaFragment() { isDescriptionVisible = showingDescription && visible val alpha = if (isDescriptionVisible) 1.0f else 0.0f + if (isDescriptionVisible) { + // If to be visible, need to make visible immediately and animate alpha + descriptionView.alpha = 0.0f + descriptionView.visible(isDescriptionVisible) + } + descriptionView.animate().alpha(alpha) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { @@ -145,7 +191,7 @@ class ViewVideoFragment : ViewMediaFragment() { }) .start() - if (visible) { + if (visible && videoView.isPlaying && !isAudio) { hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS) } else { handler.removeCallbacks(hideToolbar) diff --git a/app/src/main/java/com/keylesspalace/tusky/view/ExposedPlayPauseVideoView.kt b/app/src/main/java/com/keylesspalace/tusky/view/ExposedPlayPauseVideoView.kt new file mode 100644 index 00000000..ec748e04 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/view/ExposedPlayPauseVideoView.kt @@ -0,0 +1,33 @@ +package com.keylesspalace.tusky.view + +import android.content.Context +import android.util.AttributeSet +import android.widget.VideoView + +class ExposedPlayPauseVideoView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0) + : VideoView(context, attrs, defStyleAttr) { + + private var listener: PlayPauseListener? = null + + fun setPlayPauseListener(listener: PlayPauseListener) { + this.listener = listener + } + + override fun start() { + super.start() + listener?.onPlay() + } + + override fun pause() { + super.pause() + listener?.onPause() + } + + interface PlayPauseListener { + fun onPlay() + fun onPause() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_view_video.xml b/app/src/main/res/layout/fragment_view_video.xml index 083bdb20..180481c4 100644 --- a/app/src/main/res/layout/fragment_view_video.xml +++ b/app/src/main/res/layout/fragment_view_video.xml @@ -23,7 +23,7 @@ app:layout_constraintTop_toTopOf="parent" tools:text="Some media description" /> -