Remove wildcard imports

This commit is contained in:
Naveen Singh 2024-09-28 00:19:03 +05:30
parent 1edcd5d161
commit 571866543d
No known key found for this signature in database
GPG key ID: AF5D43C216778C0B
3 changed files with 303 additions and 105 deletions

View file

@ -13,11 +13,21 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.* import android.view.GestureDetector
import android.view.MotionEvent
import android.view.Surface
import android.view.TextureView
import android.view.View
import android.view.WindowManager
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.SeekBar import android.widget.SeekBar
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.media3.common.* import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.VideoSize
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.ContentDataSource import androidx.media3.datasource.ContentDataSource
import androidx.media3.datasource.DataSource import androidx.media3.datasource.DataSource
@ -28,17 +38,56 @@ import androidx.media3.exoplayer.SeekParameters
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource import androidx.media3.exoplayer.source.ProgressiveMediaSource
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.actionBarHeight
import org.fossify.commons.extensions.beGone
import org.fossify.commons.extensions.beVisible
import org.fossify.commons.extensions.beVisibleIf
import org.fossify.commons.extensions.fadeIn
import org.fossify.commons.extensions.getColoredDrawableWithColor
import org.fossify.commons.extensions.getFilenameFromUri
import org.fossify.commons.extensions.getFormattedDuration
import org.fossify.commons.extensions.getProperTextColor
import org.fossify.commons.extensions.navigationBarHeight
import org.fossify.commons.extensions.navigationBarOnSide
import org.fossify.commons.extensions.navigationBarWidth
import org.fossify.commons.extensions.onGlobalLayout
import org.fossify.commons.extensions.portrait
import org.fossify.commons.extensions.setDrawablesRelativeWithIntrinsicBounds
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.extensions.statusBarHeight
import org.fossify.commons.extensions.updateTextColors
import org.fossify.commons.extensions.viewBinding
import org.fossify.gallery.R import org.fossify.gallery.R
import org.fossify.gallery.databinding.ActivityVideoPlayerBinding import org.fossify.gallery.databinding.ActivityVideoPlayerBinding
import org.fossify.gallery.extensions.* import org.fossify.gallery.extensions.config
import org.fossify.gallery.extensions.getFriendlyMessage
import org.fossify.gallery.extensions.hasNavBar
import org.fossify.gallery.extensions.hideSystemUI
import org.fossify.gallery.extensions.mute
import org.fossify.gallery.extensions.openPath
import org.fossify.gallery.extensions.shareMediumPath
import org.fossify.gallery.extensions.showSystemUI
import org.fossify.gallery.extensions.unmute
import org.fossify.gallery.fragments.PlaybackSpeedFragment import org.fossify.gallery.fragments.PlaybackSpeedFragment
import org.fossify.gallery.helpers.* import org.fossify.gallery.helpers.DRAG_THRESHOLD
import org.fossify.gallery.helpers.EXOPLAYER_MAX_BUFFER_MS
import org.fossify.gallery.helpers.EXOPLAYER_MIN_BUFFER_MS
import org.fossify.gallery.helpers.FAST_FORWARD_VIDEO_MS
import org.fossify.gallery.helpers.GO_TO_NEXT_ITEM
import org.fossify.gallery.helpers.GO_TO_PREV_ITEM
import org.fossify.gallery.helpers.HIDE_SYSTEM_UI_DELAY
import org.fossify.gallery.helpers.MAX_CLOSE_DOWN_GESTURE_DURATION
import org.fossify.gallery.helpers.ROTATE_BY_ASPECT_RATIO
import org.fossify.gallery.helpers.ROTATE_BY_DEVICE_ROTATION
import org.fossify.gallery.helpers.ROTATE_BY_SYSTEM_SETTING
import org.fossify.gallery.helpers.SHOW_NEXT_ITEM
import org.fossify.gallery.helpers.SHOW_PREV_ITEM
import org.fossify.gallery.interfaces.PlaybackSpeedListener import org.fossify.gallery.interfaces.PlaybackSpeedListener
import java.text.DecimalFormat import java.text.DecimalFormat
@UnstableApi @UnstableApi
open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener, PlaybackSpeedListener { open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener,
TextureView.SurfaceTextureListener, PlaybackSpeedListener {
private val PLAY_WHEN_READY_DRAG_DELAY = 100L private val PLAY_WHEN_READY_DRAG_DELAY = 100L
private var mIsFullscreen = false private var mIsFullscreen = false
@ -122,11 +171,18 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
} }
private fun setupOptionsMenu() { private fun setupOptionsMenu() {
(binding.videoAppbar.layoutParams as RelativeLayout.LayoutParams).topMargin = statusBarHeight (binding.videoAppbar.layoutParams as RelativeLayout.LayoutParams).topMargin =
statusBarHeight
binding.videoToolbar.apply { binding.videoToolbar.apply {
setTitleTextColor(Color.WHITE) setTitleTextColor(Color.WHITE)
overflowIcon = resources.getColoredDrawableWithColor(org.fossify.commons.R.drawable.ic_three_dots_vector, Color.WHITE) overflowIcon = resources.getColoredDrawableWithColor(
navigationIcon = resources.getColoredDrawableWithColor(org.fossify.commons.R.drawable.ic_arrow_left_vector, Color.WHITE) org.fossify.commons.R.drawable.ic_three_dots_vector,
Color.WHITE
)
navigationIcon = resources.getColoredDrawableWithColor(
org.fossify.commons.R.drawable.ic_arrow_left_vector,
Color.WHITE
)
} }
updateMenuItemColors(binding.videoToolbar.menu, forceWhiteIcons = true) updateMenuItemColors(binding.videoToolbar.menu, forceWhiteIcons = true)
@ -154,7 +210,8 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
} }
binding.topShadow.layoutParams.height = statusBarHeight + actionBarHeight binding.topShadow.layoutParams.height = statusBarHeight + actionBarHeight
(binding.videoAppbar.layoutParams as RelativeLayout.LayoutParams).topMargin = statusBarHeight (binding.videoAppbar.layoutParams as RelativeLayout.LayoutParams).topMargin =
statusBarHeight
if (!portrait && navigationBarOnSide && navigationBarWidth > 0) { if (!portrait && navigationBarOnSide && navigationBarWidth > 0) {
binding.videoToolbar.setPadding(0, 0, navigationBarWidth, 0) binding.videoToolbar.setPadding(0, 0, navigationBarWidth, 0)
} else { } else {
@ -195,18 +252,29 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
binding.videoSurfaceFrame.setOnClickListener { toggleFullscreen() } binding.videoSurfaceFrame.setOnClickListener { toggleFullscreen() }
binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
binding.bottomVideoTimeHolder.videoNextFile.beVisibleIf(intent.getBooleanExtra(SHOW_NEXT_ITEM, false)) binding.bottomVideoTimeHolder.videoNextFile.beVisibleIf(
intent.getBooleanExtra(
SHOW_NEXT_ITEM,
false
)
)
binding.bottomVideoTimeHolder.videoNextFile.setOnClickListener { handleNextFile() } binding.bottomVideoTimeHolder.videoNextFile.setOnClickListener { handleNextFile() }
binding.bottomVideoTimeHolder.videoPrevFile.beVisibleIf(intent.getBooleanExtra(SHOW_PREV_ITEM, false)) binding.bottomVideoTimeHolder.videoPrevFile.beVisibleIf(
intent.getBooleanExtra(
SHOW_PREV_ITEM,
false
)
)
binding.bottomVideoTimeHolder.videoPrevFile.setOnClickListener { handlePrevFile() } binding.bottomVideoTimeHolder.videoPrevFile.setOnClickListener { handlePrevFile() }
val gestureDetector = GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() { val gestureDetector =
override fun onDoubleTap(e: MotionEvent): Boolean { GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
handleDoubleTap(e.rawX) override fun onDoubleTap(e: MotionEvent): Boolean {
return true handleDoubleTap(e.rawX)
} return true
}) }
})
binding.videoSurfaceFrame.setOnTouchListener { view, event -> binding.videoSurfaceFrame.setOnTouchListener { view, event ->
handleEvent(event) handleEvent(event)
@ -218,17 +286,29 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
binding.videoSurface.surfaceTextureListener = this binding.videoSurface.surfaceTextureListener = this
if (config.allowVideoGestures) { if (config.allowVideoGestures) {
binding.videoBrightnessController.initialize(this, binding.slideInfo, true, binding.videoPlayerHolder, singleTap = { x, y -> binding.videoBrightnessController.initialize(
toggleFullscreen() this,
}, doubleTap = { x, y -> binding.slideInfo,
doSkip(false) true,
}) binding.videoPlayerHolder,
singleTap = { x, y ->
toggleFullscreen()
},
doubleTap = { x, y ->
doSkip(false)
})
binding.videoVolumeController.initialize(this, binding.slideInfo, false, binding.videoPlayerHolder, singleTap = { x, y -> binding.videoVolumeController.initialize(
toggleFullscreen() this,
}, doubleTap = { x, y -> binding.slideInfo,
doSkip(true) false,
}) binding.videoPlayerHolder,
singleTap = { x, y ->
toggleFullscreen()
},
doubleTap = { x, y ->
doSkip(true)
})
} else { } else {
binding.videoBrightnessController.beGone() binding.videoBrightnessController.beGone()
binding.videoVolumeController.beGone() binding.videoVolumeController.beGone()
@ -257,7 +337,12 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
.createMediaSource(MediaItem.fromUri(fileDataSource.uri!!)) .createMediaSource(MediaItem.fromUri(fileDataSource.uri!!))
val loadControl = DefaultLoadControl.Builder() val loadControl = DefaultLoadControl.Builder()
.setBufferDurationsMs(EXOPLAYER_MIN_BUFFER_MS, EXOPLAYER_MAX_BUFFER_MS, EXOPLAYER_MIN_BUFFER_MS, EXOPLAYER_MIN_BUFFER_MS) .setBufferDurationsMs(
EXOPLAYER_MIN_BUFFER_MS,
EXOPLAYER_MAX_BUFFER_MS,
EXOPLAYER_MIN_BUFFER_MS,
EXOPLAYER_MIN_BUFFER_MS
)
.setPrioritizeTimeOverSizeThresholds(true) .setPrioritizeTimeOverSizeThresholds(true)
.build() .build()
@ -287,7 +372,11 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
private fun ExoPlayer.initListeners() { private fun ExoPlayer.initListeners() {
addListener(object : Player.Listener { addListener(object : Player.Listener {
override fun onPositionDiscontinuity(oldPosition: Player.PositionInfo, newPosition: Player.PositionInfo, @Player.DiscontinuityReason reason: Int) { override fun onPositionDiscontinuity(
oldPosition: Player.PositionInfo,
newPosition: Player.PositionInfo,
@Player.DiscontinuityReason reason: Int
) {
// Reset progress views when video loops. // Reset progress views when video loops.
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) { if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
binding.bottomVideoTimeHolder.videoSeekbar.progress = 0 binding.bottomVideoTimeHolder.videoSeekbar.progress = 0
@ -327,7 +416,8 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
binding.bottomVideoTimeHolder.videoTogglePlayPause.beVisible() binding.bottomVideoTimeHolder.videoTogglePlayPause.beVisible()
binding.bottomVideoTimeHolder.videoPlaybackSpeed.beVisible() binding.bottomVideoTimeHolder.videoPlaybackSpeed.beVisible()
binding.bottomVideoTimeHolder.videoToggleMute.beVisible() binding.bottomVideoTimeHolder.videoToggleMute.beVisible()
binding.bottomVideoTimeHolder.videoPlaybackSpeed.text = "${DecimalFormat("#.##").format(config.playbackSpeed)}x" binding.bottomVideoTimeHolder.videoPlaybackSpeed.text =
"${DecimalFormat("#.##").format(config.playbackSpeed)}x"
mDuration = (mExoPlayer!!.duration / 1000).toInt() mDuration = (mExoPlayer!!.duration / 1000).toInt()
binding.bottomVideoTimeHolder.videoSeekbar.max = mDuration binding.bottomVideoTimeHolder.videoSeekbar.max = mDuration
binding.bottomVideoTimeHolder.videoDuration.text = mDuration.getFormattedDuration() binding.bottomVideoTimeHolder.videoDuration.text = mDuration.getFormattedDuration()
@ -430,7 +520,8 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
clearLastVideoSavedProgress() clearLastVideoSavedProgress()
mCurrTime = (mExoPlayer!!.duration / 1000).toInt() mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
binding.bottomVideoTimeHolder.videoSeekbar.progress = binding.bottomVideoTimeHolder.videoSeekbar.max binding.bottomVideoTimeHolder.videoSeekbar.progress =
binding.bottomVideoTimeHolder.videoSeekbar.max
binding.bottomVideoTimeHolder.videoCurrTime.text = mDuration.getFormattedDuration() binding.bottomVideoTimeHolder.videoCurrTime.text = mDuration.getFormattedDuration()
pauseVideo() pauseVideo()
} }
@ -443,7 +534,10 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
private fun saveVideoProgress() { private fun saveVideoProgress() {
if (!didVideoEnd()) { if (!didVideoEnd()) {
config.saveLastVideoPosition(mUri.toString(), mExoPlayer!!.currentPosition.toInt() / 1000) config.saveLastVideoPosition(
mUri.toString(),
mExoPlayer!!.currentPosition.toInt() / 1000
)
} }
} }
@ -489,11 +583,12 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
private fun changeOrientation() { private fun changeOrientation() {
mIsOrientationLocked = true mIsOrientationLocked = true
requestedOrientation = if (resources.configuration.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE if (resources.configuration.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
} else { ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT } else {
} ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
} }
private fun toggleFullscreen() { private fun toggleFullscreen() {
@ -553,13 +648,20 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
if (isSlow != binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag as? Boolean) { if (isSlow != binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag as? Boolean) {
binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag = isSlow binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag = isSlow
val drawableId = if (isSlow) R.drawable.ic_playback_speed_slow_vector else R.drawable.ic_playback_speed_vector val drawableId =
if (isSlow) R.drawable.ic_playback_speed_slow_vector else R.drawable.ic_playback_speed_vector
binding.bottomVideoTimeHolder.videoPlaybackSpeed binding.bottomVideoTimeHolder.videoPlaybackSpeed
.setDrawablesRelativeWithIntrinsicBounds(AppCompatResources.getDrawable(this, drawableId)) .setDrawablesRelativeWithIntrinsicBounds(
AppCompatResources.getDrawable(
this,
drawableId
)
)
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.bottomVideoTimeHolder.videoPlaybackSpeed.text = "${DecimalFormat("#.##").format(speed)}x" binding.bottomVideoTimeHolder.videoPlaybackSpeed.text =
"${DecimalFormat("#.##").format(speed)}x"
mExoPlayer?.setPlaybackSpeed(speed) mExoPlayer?.setPlaybackSpeed(speed)
} }
@ -590,7 +692,8 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
if (mExoPlayer != null && !mIsDragged && mIsPlaying) { if (mExoPlayer != null && !mIsDragged && mIsPlaying) {
mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt() mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt()
binding.bottomVideoTimeHolder.videoSeekbar.progress = mCurrTime binding.bottomVideoTimeHolder.videoSeekbar.progress = mCurrTime
binding.bottomVideoTimeHolder.videoCurrTime.text = mCurrTime.getFormattedDuration() binding.bottomVideoTimeHolder.videoCurrTime.text =
mCurrTime.getFormattedDuration()
} }
mTimerHandler.postDelayed(this, 1000) mTimerHandler.postDelayed(this, 1000)
@ -604,9 +707,11 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
} }
val curr = mExoPlayer!!.currentPosition val curr = mExoPlayer!!.currentPosition
val newProgress = if (forward) curr + FAST_FORWARD_VIDEO_MS else curr - FAST_FORWARD_VIDEO_MS val newProgress =
if (forward) curr + FAST_FORWARD_VIDEO_MS else curr - FAST_FORWARD_VIDEO_MS
val roundProgress = Math.round(newProgress / 1000f) val roundProgress = Math.round(newProgress / 1000f)
val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt() / 1000, roundProgress), 0) val limitedProgress =
Math.max(Math.min(mExoPlayer!!.duration.toInt() / 1000, roundProgress), 0)
setPosition(limitedProgress) setPosition(limitedProgress)
if (!mIsPlaying) { if (!mIsPlaying) {
togglePlayPause() togglePlayPause()
@ -627,7 +732,10 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
val diffX = event.rawX - mTouchDownX val diffX = event.rawX - mTouchDownX
val diffY = event.rawY - mTouchDownY val diffY = event.rawY - mTouchDownY
if (mIsDragged || (Math.abs(diffX) > mDragThreshold && Math.abs(diffX) > Math.abs(diffY)) && binding.videoSurfaceFrame.controller.state.zoom == 1f) { if (mIsDragged || (Math.abs(diffX) > mDragThreshold && Math.abs(diffX) > Math.abs(
diffY
)) && binding.videoSurfaceFrame.controller.state.zoom == 1f
) {
if (!mIsDragged) { if (!mIsDragged) {
arrayOf( arrayOf(
binding.bottomVideoTimeHolder.videoCurrTime, binding.bottomVideoTimeHolder.videoCurrTime,
@ -644,7 +752,8 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
val skipLength = (mDuration * 1000f) * (percent / 100f) val skipLength = (mDuration * 1000f) * (percent / 100f)
var newProgress = mProgressAtDown + skipLength var newProgress = mProgressAtDown + skipLength
newProgress = Math.max(Math.min(mExoPlayer!!.duration.toFloat(), newProgress), 0f) newProgress =
Math.max(Math.min(mExoPlayer!!.duration.toFloat(), newProgress), 0f)
val newSeconds = (newProgress / 1000).toInt() val newSeconds = (newProgress / 1000).toInt()
setPosition(newSeconds) setPosition(newSeconds)
resetPlayWhenReady() resetPlayWhenReady()

View file

@ -2,7 +2,14 @@ package org.fossify.gallery.extensions
import android.content.Context import android.content.Context
import androidx.media3.common.PlaybackException import androidx.media3.common.PlaybackException
import androidx.media3.common.PlaybackException.* import androidx.media3.common.PlaybackException.ERROR_CODE_DECODER_INIT_FAILED
import androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FAILED
import androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES
import androidx.media3.common.PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED
import androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED
import androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED
import androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED
import androidx.media3.common.PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED
import androidx.media3.common.Player import androidx.media3.common.Player
import org.fossify.gallery.R import org.fossify.gallery.R
@ -14,7 +21,7 @@ fun Player.unmute() {
volume = 1f volume = 1f
} }
fun PlaybackException.getFriendlyMessage(context: Context): String? { fun PlaybackException.getFriendlyMessage(context: Context): String {
val resource = when (errorCode) { val resource = when (errorCode) {
ERROR_CODE_PARSING_CONTAINER_MALFORMED, ERROR_CODE_PARSING_CONTAINER_MALFORMED,
ERROR_CODE_PARSING_MANIFEST_MALFORMED -> R.string.file_is_malformed_or_corrupted ERROR_CODE_PARSING_MANIFEST_MALFORMED -> R.string.file_is_malformed_or_corrupted

View file

@ -9,14 +9,26 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.* import android.view.GestureDetector
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.Surface
import android.view.TextureView
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView import android.widget.ImageView
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.children import androidx.core.view.children
import androidx.media3.common.* import androidx.media3.common.AudioAttributes
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.VideoSize
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.ContentDataSource import androidx.media3.datasource.ContentDataSource
import androidx.media3.datasource.DataSource import androidx.media3.datasource.DataSource
@ -29,13 +41,42 @@ import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.ProgressiveMediaSource import androidx.media3.exoplayer.source.ProgressiveMediaSource
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.beGone
import org.fossify.commons.extensions.beGoneIf
import org.fossify.commons.extensions.beInvisible
import org.fossify.commons.extensions.beInvisibleIf
import org.fossify.commons.extensions.beVisible
import org.fossify.commons.extensions.beVisibleIf
import org.fossify.commons.extensions.fadeIn
import org.fossify.commons.extensions.getDuration
import org.fossify.commons.extensions.getFormattedDuration
import org.fossify.commons.extensions.getProperTextColor
import org.fossify.commons.extensions.getVideoResolution
import org.fossify.commons.extensions.isGone
import org.fossify.commons.extensions.isVisible
import org.fossify.commons.extensions.navigationBarHeight
import org.fossify.commons.extensions.navigationBarWidth
import org.fossify.commons.extensions.onGlobalLayout
import org.fossify.commons.extensions.realScreenSize
import org.fossify.commons.extensions.setDrawablesRelativeWithIntrinsicBounds
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.extensions.updateTextColors
import org.fossify.commons.helpers.ensureBackgroundThread import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.gallery.R import org.fossify.gallery.R
import org.fossify.gallery.activities.VideoActivity import org.fossify.gallery.activities.VideoActivity
import org.fossify.gallery.databinding.PagerVideoItemBinding import org.fossify.gallery.databinding.PagerVideoItemBinding
import org.fossify.gallery.extensions.* import org.fossify.gallery.extensions.config
import org.fossify.gallery.helpers.* import org.fossify.gallery.extensions.getFriendlyMessage
import org.fossify.gallery.extensions.hasNavBar
import org.fossify.gallery.extensions.mute
import org.fossify.gallery.extensions.parseFileChannel
import org.fossify.gallery.extensions.unmute
import org.fossify.gallery.helpers.Config
import org.fossify.gallery.helpers.EXOPLAYER_MAX_BUFFER_MS
import org.fossify.gallery.helpers.EXOPLAYER_MIN_BUFFER_MS
import org.fossify.gallery.helpers.FAST_FORWARD_VIDEO_MS
import org.fossify.gallery.helpers.MEDIUM
import org.fossify.gallery.helpers.SHOULD_INIT_FRAGMENT
import org.fossify.gallery.interfaces.PlaybackSpeedListener import org.fossify.gallery.interfaces.PlaybackSpeedListener
import org.fossify.gallery.models.Medium import org.fossify.gallery.models.Medium
import org.fossify.gallery.views.MediaSideScroll import org.fossify.gallery.views.MediaSideScroll
@ -44,7 +85,8 @@ import java.io.FileInputStream
import java.text.DecimalFormat import java.text.DecimalFormat
@UnstableApi @UnstableApi
class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener, PlaybackSpeedListener { class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener,
SeekBar.OnSeekBarChangeListener, PlaybackSpeedListener {
private val PROGRESS = "progress" private val PROGRESS = "progress"
private var mIsFullscreen = false private var mIsFullscreen = false
@ -85,7 +127,11 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
private lateinit var mPlayPauseButton: ImageView private lateinit var mPlayPauseButton: ImageView
private lateinit var mSeekBar: SeekBar private lateinit var mSeekBar: SeekBar
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val context = requireContext() val context = requireContext()
val activity = requireActivity() val activity = requireActivity()
val arguments = requireArguments() val arguments = requireArguments()
@ -131,29 +177,30 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mTextureView = videoSurface mTextureView = videoSurface
mTextureView.surfaceTextureListener = this@VideoFragment mTextureView.surfaceTextureListener = this@VideoFragment
val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { val gestureDetector =
override fun onSingleTapConfirmed(e: MotionEvent): Boolean { GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
if (!mConfig.allowInstantChange) { override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
toggleFullscreen() if (!mConfig.allowInstantChange) {
toggleFullscreen()
return true
}
val viewWidth = root.width
val instantWidth = viewWidth / 7
val clickedX = e.rawX
when {
clickedX <= instantWidth -> listener?.goToPrevItem()
clickedX >= viewWidth - instantWidth -> listener?.goToNextItem()
else -> toggleFullscreen()
}
return true return true
} }
val viewWidth = root.width override fun onDoubleTap(e: MotionEvent): Boolean {
val instantWidth = viewWidth / 7 handleDoubleTap(e.rawX)
val clickedX = e.rawX return true
when {
clickedX <= instantWidth -> listener?.goToPrevItem()
clickedX >= viewWidth - instantWidth -> listener?.goToNextItem()
else -> toggleFullscreen()
} }
return true })
}
override fun onDoubleTap(e: MotionEvent): Boolean {
handleDoubleTap(e.rawX)
return true
}
})
videoPreview.setOnTouchListener { view, event -> videoPreview.setOnTouchListener { view, event ->
handleEvent(event) handleEvent(event)
@ -183,7 +230,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mIsFragmentVisible = true mIsFragmentVisible = true
} }
mIsFullscreen = activity.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN mIsFullscreen =
activity.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN
initTimeHolder() initTimeHolder()
// checkIfPanorama() TODO: Implement panorama using a FOSS library // checkIfPanorama() TODO: Implement panorama using a FOSS library
@ -213,25 +261,37 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
setVideoSize() setVideoSize()
binding.apply { binding.apply {
mBrightnessSideScroll.initialize(activity, slideInfo, true, container, singleTap = { x, y -> mBrightnessSideScroll.initialize(
if (mConfig.allowInstantChange) { activity,
listener?.goToPrevItem() slideInfo,
} else { true,
toggleFullscreen() container,
} singleTap = { x, y ->
}, doubleTap = { x, y -> if (mConfig.allowInstantChange) {
doSkip(false) listener?.goToPrevItem()
}) } else {
toggleFullscreen()
}
},
doubleTap = { x, y ->
doSkip(false)
})
mVolumeSideScroll.initialize(activity, slideInfo, false, container, singleTap = { x, y -> mVolumeSideScroll.initialize(
if (mConfig.allowInstantChange) { activity,
listener?.goToNextItem() slideInfo,
} else { false,
toggleFullscreen() container,
} singleTap = { x, y ->
}, doubleTap = { x, y -> if (mConfig.allowInstantChange) {
doSkip(true) listener?.goToNextItem()
}) } else {
toggleFullscreen()
}
},
doubleTap = { x, y ->
doSkip(true)
})
videoSurface.onGlobalLayout { videoSurface.onGlobalLayout {
if (mIsFragmentVisible && mConfig.autoplayVideos && !mConfig.openVideosOnSeparateScreen) { if (mIsFragmentVisible && mConfig.autoplayVideos && !mConfig.openVideosOnSeparateScreen) {
@ -251,7 +311,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
mConfig = requireContext().config // make sure we get a new config, in case the user changed something in the app settings mConfig =
requireContext().config // make sure we get a new config, in case the user changed something in the app settings
requireActivity().updateTextColors(binding.videoHolder) requireActivity().updateTextColors(binding.videoHolder)
val allowVideoGestures = mConfig.allowVideoGestures val allowVideoGestures = mConfig.allowVideoGestures
mTextureView.beGoneIf(mConfig.openVideosOnSeparateScreen || mIsPanorama) mTextureView.beGoneIf(mConfig.openVideosOnSeparateScreen || mIsPanorama)
@ -321,7 +382,10 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
private fun saveVideoProgress() { private fun saveVideoProgress() {
if (!videoEnded()) { if (!videoEnded()) {
if (mExoPlayer != null) { if (mExoPlayer != null) {
mConfig.saveLastVideoPosition(mMedium.path, mExoPlayer!!.currentPosition.toInt() / 1000) mConfig.saveLastVideoPosition(
mMedium.path,
mExoPlayer!!.currentPosition.toInt() / 1000
)
} else { } else {
mConfig.saveLastVideoPosition(mMedium.path, mPositionAtPause.toInt() / 1000) mConfig.saveLastVideoPosition(mMedium.path, mPositionAtPause.toInt() / 1000)
} }
@ -387,7 +451,12 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mPlayOnPrepared = true mPlayOnPrepared = true
val loadControl = DefaultLoadControl.Builder() val loadControl = DefaultLoadControl.Builder()
.setBufferDurationsMs(EXOPLAYER_MIN_BUFFER_MS, EXOPLAYER_MAX_BUFFER_MS, EXOPLAYER_MIN_BUFFER_MS, EXOPLAYER_MIN_BUFFER_MS) .setBufferDurationsMs(
EXOPLAYER_MIN_BUFFER_MS,
EXOPLAYER_MAX_BUFFER_MS,
EXOPLAYER_MIN_BUFFER_MS,
EXOPLAYER_MIN_BUFFER_MS
)
.setPrioritizeTimeOverSizeThresholds(true) .setPrioritizeTimeOverSizeThresholds(true)
.build() .build()
@ -570,7 +639,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
private fun showPlaybackSpeedPicker() { private fun showPlaybackSpeedPicker() {
val fragment = PlaybackSpeedFragment() val fragment = PlaybackSpeedFragment()
childFragmentManager.beginTransaction().add(fragment, fragment::class.java.simpleName).commit() childFragmentManager.beginTransaction().add(fragment, fragment::class.java.simpleName)
.commit()
fragment.setListener(this) fragment.setListener(this)
} }
@ -579,19 +649,28 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
if (isSlow != binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag as? Boolean) { if (isSlow != binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag as? Boolean) {
binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag = isSlow binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag = isSlow
val drawableId = if (isSlow) R.drawable.ic_playback_speed_slow_vector else R.drawable.ic_playback_speed_vector val drawableId =
if (isSlow) R.drawable.ic_playback_speed_slow_vector else R.drawable.ic_playback_speed_vector
binding.bottomVideoTimeHolder.videoPlaybackSpeed binding.bottomVideoTimeHolder.videoPlaybackSpeed
.setDrawablesRelativeWithIntrinsicBounds(AppCompatResources.getDrawable(requireContext(), drawableId)) .setDrawablesRelativeWithIntrinsicBounds(
AppCompatResources.getDrawable(
requireContext(),
drawableId
)
)
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.bottomVideoTimeHolder.videoPlaybackSpeed.text = "${DecimalFormat("#.##").format(speed)}x" binding.bottomVideoTimeHolder.videoPlaybackSpeed.text =
"${DecimalFormat("#.##").format(speed)}x"
mExoPlayer?.setPlaybackSpeed(speed) mExoPlayer?.setPlaybackSpeed(speed)
} }
private fun getExtendedDetailsY(height: Int): Float { private fun getExtendedDetailsY(height: Int): Float {
val smallMargin = context?.resources?.getDimension(org.fossify.commons.R.dimen.small_margin) ?: return 0f val smallMargin =
val fullscreenOffset = smallMargin + if (mIsFullscreen) 0 else requireContext().navigationBarHeight context?.resources?.getDimension(org.fossify.commons.R.dimen.small_margin) ?: return 0f
val fullscreenOffset =
smallMargin + if (mIsFullscreen) 0 else requireContext().navigationBarHeight
var actionsHeight = 0f var actionsHeight = 0f
if (!mIsFullscreen) { if (!mIsFullscreen) {
if (binding.bottomVideoTimeHolder.root.children.any { isVisible }) { if (binding.bottomVideoTimeHolder.root.children.any { isVisible }) {
@ -623,9 +702,11 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
} }
val curr = mExoPlayer!!.currentPosition val curr = mExoPlayer!!.currentPosition
val newProgress = if (forward) curr + FAST_FORWARD_VIDEO_MS else curr - FAST_FORWARD_VIDEO_MS val newProgress =
if (forward) curr + FAST_FORWARD_VIDEO_MS else curr - FAST_FORWARD_VIDEO_MS
val roundProgress = Math.round(newProgress / 1000f) val roundProgress = Math.round(newProgress / 1000f)
val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt() / 1000, roundProgress), 0) val limitedProgress =
Math.max(Math.min(mExoPlayer!!.duration.toInt() / 1000, roundProgress), 0)
setPosition(limitedProgress) setPosition(limitedProgress)
if (!mIsPlaying) { if (!mIsPlaying) {
togglePlayPause() togglePlayPause()
@ -734,7 +815,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mPlayPauseButton.beVisible() mPlayPauseButton.beVisible()
binding.bottomVideoTimeHolder.videoToggleMute.beVisible() binding.bottomVideoTimeHolder.videoToggleMute.beVisible()
binding.bottomVideoTimeHolder.videoPlaybackSpeed.beVisible() binding.bottomVideoTimeHolder.videoPlaybackSpeed.beVisible()
binding.bottomVideoTimeHolder.videoPlaybackSpeed.text = "${DecimalFormat("#.##").format(mConfig.playbackSpeed)}x" binding.bottomVideoTimeHolder.videoPlaybackSpeed.text =
"${DecimalFormat("#.##").format(mConfig.playbackSpeed)}x"
} }
mWasVideoStarted = true mWasVideoStarted = true