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
|
@ -186,8 +186,14 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
for (listener in toolbarVisibilityListeners) {
|
for (listener in toolbarVisibilityListeners) {
|
||||||
listener(isToolbarVisible)
|
listener(isToolbarVisible)
|
||||||
}
|
}
|
||||||
|
|
||||||
val visibility = if (isToolbarVisible) View.VISIBLE else View.INVISIBLE
|
val visibility = if (isToolbarVisible) View.VISIBLE else View.INVISIBLE
|
||||||
val alpha = if (isToolbarVisible) 1.0f else 0.0f
|
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)
|
toolbar.animate().alpha(alpha)
|
||||||
.setListener(object : AnimatorListenerAdapter() {
|
.setListener(object : AnimatorListenerAdapter() {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -31,6 +32,7 @@ import com.keylesspalace.tusky.ViewMediaActivity
|
||||||
import com.keylesspalace.tusky.entity.Attachment
|
import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.visible
|
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.activity_view_media.*
|
||||||
import kotlinx.android.synthetic.main.fragment_view_video.*
|
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
|
// 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
|
// This is explicitly stored as runnable so that we pass it to the handler later for cancellation
|
||||||
mediaActivity.onPhotoTap()
|
mediaActivity.onPhotoTap()
|
||||||
|
mediaController.hide()
|
||||||
}
|
}
|
||||||
private lateinit var mediaActivity: ViewMediaActivity
|
private lateinit var mediaActivity: ViewMediaActivity
|
||||||
private val TOOLBAR_HIDE_DELAY_MS = 3000L
|
private val TOOLBAR_HIDE_DELAY_MS = 3000L
|
||||||
override lateinit var descriptionView : TextView
|
override lateinit var descriptionView : TextView
|
||||||
private lateinit var mediaController : MediaController
|
private lateinit var mediaController : MediaController
|
||||||
|
private var isAudio = false
|
||||||
|
|
||||||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||||
// Start/pause/resume video playback as fragment is shown/hidden
|
// Start/pause/resume video playback as fragment is shown/hidden
|
||||||
|
@ -72,14 +76,43 @@ class ViewVideoFragment : ViewMediaFragment() {
|
||||||
|
|
||||||
videoView.transitionName = url
|
videoView.transitionName = url
|
||||||
videoView.setVideoPath(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)
|
mediaController.setMediaPlayer(videoView)
|
||||||
videoView.setMediaController(mediaController)
|
videoView.setMediaController(mediaController)
|
||||||
videoView.requestFocus()
|
videoView.requestFocus()
|
||||||
videoView.setOnTouchListener { _, _ ->
|
videoView.setPlayPauseListener(object: ExposedPlayPauseVideoView.PlayPauseListener {
|
||||||
mediaActivity.onPhotoTap()
|
override fun onPause() {
|
||||||
false
|
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 ->
|
videoView.setOnPreparedListener { mp ->
|
||||||
val containerWidth = videoContainer.measuredWidth.toFloat()
|
val containerWidth = videoContainer.measuredWidth.toFloat()
|
||||||
val containerHeight = videoContainer.measuredHeight.toFloat()
|
val containerHeight = videoContainer.measuredHeight.toFloat()
|
||||||
|
@ -94,10 +127,16 @@ class ViewVideoFragment : ViewMediaFragment() {
|
||||||
videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
|
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()
|
progressBar.hide()
|
||||||
mp.isLooping = true
|
mp.isLooping = true
|
||||||
if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
|
if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
|
||||||
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
|
|
||||||
videoView.start()
|
videoView.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +165,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
||||||
throw IllegalArgumentException("attachment has to be set")
|
throw IllegalArgumentException("attachment has to be set")
|
||||||
}
|
}
|
||||||
url = attachment.url
|
url = attachment.url
|
||||||
|
isAudio = attachment.type == Attachment.Type.AUDIO
|
||||||
finalizeViewSetup(url, attachment.previewUrl, attachment.description)
|
finalizeViewSetup(url, attachment.previewUrl, attachment.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +176,12 @@ class ViewVideoFragment : ViewMediaFragment() {
|
||||||
|
|
||||||
isDescriptionVisible = showingDescription && visible
|
isDescriptionVisible = showingDescription && visible
|
||||||
val alpha = if (isDescriptionVisible) 1.0f else 0.0f
|
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)
|
descriptionView.animate().alpha(alpha)
|
||||||
.setListener(object : AnimatorListenerAdapter() {
|
.setListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
|
@ -145,7 +191,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
||||||
})
|
})
|
||||||
.start()
|
.start()
|
||||||
|
|
||||||
if (visible) {
|
if (visible && videoView.isPlaying && !isAudio) {
|
||||||
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
|
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
|
||||||
} else {
|
} else {
|
||||||
handler.removeCallbacks(hideToolbar)
|
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"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Some media description" />
|
tools:text="Some media description" />
|
||||||
|
|
||||||
<VideoView
|
<com.keylesspalace.tusky.view.ExposedPlayPauseVideoView
|
||||||
android:id="@+id/videoView"
|
android:id="@+id/videoView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|
Loading…
Reference in New Issue