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
This commit is contained in:
parent
21a7201f8a
commit
a7c1345085
4 changed files with 93 additions and 8 deletions
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Some media description" />
|
||||
|
||||
<VideoView
|
||||
<com.keylesspalace.tusky.view.ExposedPlayPauseVideoView
|
||||
android:id="@+id/videoView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
|
|
Loading…
Reference in a new issue