Rework the mute button logic

See https://github.com/FossifyOrg/Gallery/issues/171#issuecomment-2378649690

- Mute button is now completely unlinked from the system volume. The only way to unmute the player is to use the unmute button.
- Mute button state is now persisted.
This commit is contained in:
Naveen Singh 2024-09-27 16:27:04 +05:30
parent 6e98294147
commit fafd6343d9
No known key found for this signature in database
GPG key ID: AF5D43C216778C0B
6 changed files with 62 additions and 81 deletions

View file

@ -61,7 +61,6 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
private var mVideoSize = Point(0, 0) private var mVideoSize = Point(0, 0)
private var mTimerHandler = Handler() private var mTimerHandler = Handler()
private var mPlayWhenReadyHandler = Handler() private var mPlayWhenReadyHandler = Handler()
private var mVolumeController: VolumeController? = null
private var mIgnoreCloseDown = false private var mIgnoreCloseDown = false
@ -119,7 +118,6 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
binding.bottomVideoTimeHolder.videoSeekbar.progress = 0 binding.bottomVideoTimeHolder.videoSeekbar.progress = 0
mTimerHandler.removeCallbacksAndMessages(null) mTimerHandler.removeCallbacksAndMessages(null)
mPlayWhenReadyHandler.removeCallbacksAndMessages(null) mPlayWhenReadyHandler.removeCallbacksAndMessages(null)
mVolumeController?.destroy()
} }
} }
@ -189,7 +187,11 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
binding.bottomVideoTimeHolder.videoDuration.setOnClickListener { doSkip(true) } binding.bottomVideoTimeHolder.videoDuration.setOnClickListener { doSkip(true) }
binding.bottomVideoTimeHolder.videoTogglePlayPause.setOnClickListener { togglePlayPause() } binding.bottomVideoTimeHolder.videoTogglePlayPause.setOnClickListener { togglePlayPause() }
binding.bottomVideoTimeHolder.videoPlaybackSpeed.setOnClickListener { showPlaybackSpeedPicker() } binding.bottomVideoTimeHolder.videoPlaybackSpeed.setOnClickListener { showPlaybackSpeedPicker() }
binding.bottomVideoTimeHolder.videoToggleMute.setOnClickListener { mVolumeController?.toggleMute() } binding.bottomVideoTimeHolder.videoToggleMute.setOnClickListener {
config.muteVideos = !config.muteVideos
updatePlayerMuteState()
}
binding.videoSurfaceFrame.setOnClickListener { toggleFullscreen() } binding.videoSurfaceFrame.setOnClickListener { toggleFullscreen() }
binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
@ -239,12 +241,6 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
} }
mDragThreshold = DRAG_THRESHOLD * resources.displayMetrics.density mDragThreshold = DRAG_THRESHOLD * resources.displayMetrics.density
mVolumeController = VolumeController(this) { isMuted ->
val icon = if (isMuted) R.drawable.ic_vector_speaker_off else R.drawable.ic_vector_speaker_on
binding.bottomVideoTimeHolder.videoToggleMute.setImageDrawable(
AppCompatResources.getDrawable(this, icon)
)
}
} }
private fun initExoPlayer() { private fun initExoPlayer() {
@ -285,6 +281,8 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
prepare() prepare()
initListeners() initListeners()
} }
updatePlayerMuteState()
} }
private fun ExoPlayer.initListeners() { private fun ExoPlayer.initListeners() {
@ -397,6 +395,21 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
} }
} }
private fun updatePlayerMuteState() {
val isMuted = config.muteVideos
val drawableId = if (isMuted) {
mExoPlayer?.mute()
R.drawable.ic_vector_speaker_off
} else {
mExoPlayer?.unmute()
R.drawable.ic_vector_speaker_on
}
binding.bottomVideoTimeHolder.videoToggleMute.setImageDrawable(
AppCompatResources.getDrawable(this, drawableId)
)
}
private fun setPosition(seconds: Int) { private fun setPosition(seconds: Int) {
mExoPlayer?.seekTo(seconds * 1000L) mExoPlayer?.seekTo(seconds * 1000L)
binding.bottomVideoTimeHolder.videoSeekbar.progress = seconds binding.bottomVideoTimeHolder.videoSeekbar.progress = seconds

View file

@ -0,0 +1,11 @@
package org.fossify.gallery.extensions
import androidx.media3.common.Player
fun Player.mute() {
volume = 0f
}
fun Player.unmute() {
volume = 1f
}

View file

@ -34,9 +34,7 @@ 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.config import org.fossify.gallery.extensions.*
import org.fossify.gallery.extensions.hasNavBar
import org.fossify.gallery.extensions.parseFileChannel
import org.fossify.gallery.helpers.* import org.fossify.gallery.helpers.*
import org.fossify.gallery.interfaces.PlaybackSpeedListener import org.fossify.gallery.interfaces.PlaybackSpeedListener
import org.fossify.gallery.models.Medium import org.fossify.gallery.models.Medium
@ -87,8 +85,6 @@ 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
private var mVolumeController: VolumeController? = null
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()
@ -103,7 +99,11 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
videoHolder.setOnClickListener { toggleFullscreen() } videoHolder.setOnClickListener { toggleFullscreen() }
videoPreview.setOnClickListener { toggleFullscreen() } videoPreview.setOnClickListener { toggleFullscreen() }
bottomVideoTimeHolder.videoPlaybackSpeed.setOnClickListener { showPlaybackSpeedPicker() } bottomVideoTimeHolder.videoPlaybackSpeed.setOnClickListener { showPlaybackSpeedPicker() }
bottomVideoTimeHolder.videoToggleMute.setOnClickListener { mVolumeController?.toggleMute() } bottomVideoTimeHolder.videoToggleMute.setOnClickListener {
mConfig.muteVideos = !mConfig.muteVideos
updatePlayerMuteState()
}
videoSurfaceFrame.controller.settings.swallowDoubleTaps = true videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
videoPlayOutline.setOnClickListener { videoPlayOutline.setOnClickListener {
@ -246,13 +246,6 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
restoreLastVideoSavedPosition() restoreLastVideoSavedPosition()
} }
mVolumeController = VolumeController(context) { isMuted ->
val icon = if (isMuted) R.drawable.ic_vector_speaker_off else R.drawable.ic_vector_speaker_on
binding.bottomVideoTimeHolder.videoToggleMute.setImageDrawable(
AppCompatResources.getDrawable(context, icon)
)
}
return mView return mView
} }
@ -423,6 +416,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
initListeners() initListeners()
} }
updatePlayerMuteState()
} }
private fun ExoPlayer.initListeners() { private fun ExoPlayer.initListeners() {
@ -692,6 +687,22 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
} }
} }
private fun updatePlayerMuteState() {
val context = context ?: return
val isMuted = mConfig.muteVideos
val drawableId = if (isMuted) {
mExoPlayer?.mute()
R.drawable.ic_vector_speaker_off
} else {
mExoPlayer?.unmute()
R.drawable.ic_vector_speaker_on
}
binding.bottomVideoTimeHolder.videoToggleMute.setImageDrawable(
AppCompatResources.getDrawable(context, drawableId)
)
}
fun playVideo() { fun playVideo() {
if (mExoPlayer == null) { if (mExoPlayer == null) {
initExoPlayer() initExoPlayer()
@ -822,7 +833,6 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
private fun cleanup() { private fun cleanup() {
pauseVideo() pauseVideo()
releaseExoPlayer() releaseExoPlayer()
mVolumeController?.destroy()
if (mWasFragmentInit) { if (mWasFragmentInit) {
mCurrTimeView.text = 0.getFormattedDuration() mCurrTimeView.text = 0.getFormattedDuration()

View file

@ -205,6 +205,10 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getBoolean(LOOP_VIDEOS, false) get() = prefs.getBoolean(LOOP_VIDEOS, false)
set(loop) = prefs.edit().putBoolean(LOOP_VIDEOS, loop).apply() set(loop) = prefs.edit().putBoolean(LOOP_VIDEOS, loop).apply()
var muteVideos: Boolean
get() = prefs.getBoolean(MUTE_VIDEOS, false)
set(muteVideos) = prefs.edit().putBoolean(MUTE_VIDEOS, muteVideos).apply()
var openVideosOnSeparateScreen: Boolean var openVideosOnSeparateScreen: Boolean
get() = prefs.getBoolean(OPEN_VIDEOS_ON_SEPARATE_SCREEN, false) get() = prefs.getBoolean(OPEN_VIDEOS_ON_SEPARATE_SCREEN, false)
set(openVideosOnSeparateScreen) = prefs.edit().putBoolean(OPEN_VIDEOS_ON_SEPARATE_SCREEN, openVideosOnSeparateScreen).apply() set(openVideosOnSeparateScreen) = prefs.edit().putBoolean(OPEN_VIDEOS_ON_SEPARATE_SCREEN, openVideosOnSeparateScreen).apply()

View file

@ -16,6 +16,7 @@ const val IS_THIRD_PARTY_INTENT = "is_third_party_intent"
const val AUTOPLAY_VIDEOS = "autoplay_videos" const val AUTOPLAY_VIDEOS = "autoplay_videos"
const val REMEMBER_LAST_VIDEO_POSITION = "remember_last_video_position" const val REMEMBER_LAST_VIDEO_POSITION = "remember_last_video_position"
const val LOOP_VIDEOS = "loop_videos" const val LOOP_VIDEOS = "loop_videos"
const val MUTE_VIDEOS = "mute_videos"
const val OPEN_VIDEOS_ON_SEPARATE_SCREEN = "open_videos_on_separate_screen" const val OPEN_VIDEOS_ON_SEPARATE_SCREEN = "open_videos_on_separate_screen"
const val ANIMATE_GIFS = "animate_gifs" const val ANIMATE_GIFS = "animate_gifs"
const val MAX_BRIGHTNESS = "max_brightness" const val MAX_BRIGHTNESS = "max_brightness"

View file

@ -1,58 +0,0 @@
package org.fossify.gallery.helpers
import android.content.Context
import android.database.ContentObserver
import android.media.AudioManager
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import org.fossify.gallery.extensions.audioManager
class VolumeController(
private val context: Context,
private val streamType: Int = AudioManager.STREAM_MUSIC,
private val onVolumeChanged: (isMuted: Boolean) -> Unit
) {
private var audioManager = context.audioManager
private var savedVolume = audioManager.getStreamMaxVolume(streamType)
private val currentVolume: Int
get() = audioManager.getStreamVolume(streamType)
private val volumeObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
onVolumeChanged(isMuted())
}
}
init {
context.contentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, volumeObserver)
onVolumeChanged(isMuted())
}
private fun isMuted() = currentVolume == 0
private fun mute() {
savedVolume = audioManager.getStreamVolume(streamType)
audioManager.setStreamVolume(streamType, 0, 0)
}
private fun unmute() {
audioManager.setStreamVolume(streamType, savedVolume, 0)
}
fun toggleMute() {
if (isMuted()) {
unmute()
onVolumeChanged(false)
} else {
mute()
onVolumeChanged(true)
}
}
fun destroy() {
context.contentResolver.unregisterContentObserver(volumeObserver)
}
}