mirror of
https://github.com/FossifyOrg/Gallery.git
synced 2024-11-22 12:38:00 +01:00
Add mute button to player controls
Closes https://github.com/FossifyOrg/Gallery/issues/171
This commit is contained in:
parent
c5993234a7
commit
dfac193974
7 changed files with 110 additions and 3 deletions
|
@ -16,6 +16,7 @@ import android.util.DisplayMetrics
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.media3.common.*
|
import androidx.media3.common.*
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.datasource.ContentDataSource
|
import androidx.media3.datasource.ContentDataSource
|
||||||
|
@ -60,6 +61,7 @@ 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
|
||||||
|
|
||||||
|
@ -117,6 +119,7 @@ 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +189,7 @@ 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.videoSurfaceFrame.setOnClickListener { toggleFullscreen() }
|
binding.videoSurfaceFrame.setOnClickListener { toggleFullscreen() }
|
||||||
binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
||||||
|
|
||||||
|
@ -235,6 +239,12 @@ 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() {
|
||||||
|
@ -306,6 +316,7 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
if (!mWasVideoStarted) {
|
if (!mWasVideoStarted) {
|
||||||
binding.bottomVideoTimeHolder.videoTogglePlayPause.beVisible()
|
binding.bottomVideoTimeHolder.videoTogglePlayPause.beVisible()
|
||||||
binding.bottomVideoTimeHolder.videoPlaybackSpeed.beVisible()
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.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
|
||||||
|
@ -478,6 +489,7 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
binding.bottomVideoTimeHolder.videoTogglePlayPause,
|
binding.bottomVideoTimeHolder.videoTogglePlayPause,
|
||||||
binding.bottomVideoTimeHolder.videoNextFile,
|
binding.bottomVideoTimeHolder.videoNextFile,
|
||||||
binding.bottomVideoTimeHolder.videoPlaybackSpeed,
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed,
|
||||||
|
binding.bottomVideoTimeHolder.videoToggleMute,
|
||||||
binding.bottomVideoTimeHolder.videoCurrTime,
|
binding.bottomVideoTimeHolder.videoCurrTime,
|
||||||
binding.bottomVideoTimeHolder.videoSeekbar,
|
binding.bottomVideoTimeHolder.videoSeekbar,
|
||||||
binding.bottomVideoTimeHolder.videoDuration,
|
binding.bottomVideoTimeHolder.videoDuration,
|
||||||
|
@ -491,6 +503,7 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
binding.bottomVideoTimeHolder.videoPrevFile,
|
binding.bottomVideoTimeHolder.videoPrevFile,
|
||||||
binding.bottomVideoTimeHolder.videoNextFile,
|
binding.bottomVideoTimeHolder.videoNextFile,
|
||||||
binding.bottomVideoTimeHolder.videoPlaybackSpeed,
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed,
|
||||||
|
binding.bottomVideoTimeHolder.videoToggleMute,
|
||||||
binding.bottomVideoTimeHolder.videoCurrTime,
|
binding.bottomVideoTimeHolder.videoCurrTime,
|
||||||
binding.bottomVideoTimeHolder.videoDuration,
|
binding.bottomVideoTimeHolder.videoDuration,
|
||||||
).forEach {
|
).forEach {
|
||||||
|
|
|
@ -13,6 +13,7 @@ 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.media3.common.*
|
import androidx.media3.common.*
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.datasource.ContentDataSource
|
import androidx.media3.datasource.ContentDataSource
|
||||||
|
@ -84,6 +85,8 @@ 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()
|
||||||
|
@ -98,6 +101,7 @@ 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() }
|
||||||
videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
||||||
|
|
||||||
videoPlayOutline.setOnClickListener {
|
videoPlayOutline.setOnClickListener {
|
||||||
|
@ -240,6 +244,13 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +538,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
binding.bottomVideoTimeHolder.videoCurrTime,
|
binding.bottomVideoTimeHolder.videoCurrTime,
|
||||||
binding.bottomVideoTimeHolder.videoDuration,
|
binding.bottomVideoTimeHolder.videoDuration,
|
||||||
binding.bottomVideoTimeHolder.videoTogglePlayPause,
|
binding.bottomVideoTimeHolder.videoTogglePlayPause,
|
||||||
binding.bottomVideoTimeHolder.videoPlaybackSpeed
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed,
|
||||||
|
binding.bottomVideoTimeHolder.videoToggleMute
|
||||||
).forEach {
|
).forEach {
|
||||||
it.isClickable = !mIsFullscreen
|
it.isClickable = !mIsFullscreen
|
||||||
}
|
}
|
||||||
|
@ -689,6 +701,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
if (!mWasVideoStarted) {
|
if (!mWasVideoStarted) {
|
||||||
binding.videoPlayOutline.beGone()
|
binding.videoPlayOutline.beGone()
|
||||||
mPlayPauseButton.beVisible()
|
mPlayPauseButton.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"
|
||||||
}
|
}
|
||||||
|
@ -790,6 +803,7 @@ 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()
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
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) / 2
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
3
app/src/main/res/drawable/ic_vector_speaker_off.xml
Normal file
3
app/src/main/res/drawable/ic_vector_speaker_off.xml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:autoMirrored="true" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c0.03-0.2 0.05-0.41 0.05-0.63zm2.5 0c0 0.94-0.2 1.82-0.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89 0.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-0.67 0.52-1.42 0.93-2.25 1.18v2.06c1.38-0.31 2.63-0.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/>
|
||||||
|
</vector>
|
3
app/src/main/res/drawable/ic_vector_speaker_on.xml
Normal file
3
app/src/main/res/drawable/ic_vector_speaker_on.xml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:autoMirrored="true" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-0.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89 0.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-0.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
|
||||||
|
</vector>
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/video_playback_speed"
|
android:id="@+id/video_playback_speed"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="@dimen/video_player_button_width"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/big_margin"
|
android:layout_marginStart="@dimen/big_margin"
|
||||||
android:background="@drawable/darkened_automatic_circle_background"
|
android:background="@drawable/darkened_automatic_circle_background"
|
||||||
|
@ -17,7 +17,6 @@
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:paddingHorizontal="@dimen/normal_margin"
|
android:paddingHorizontal="@dimen/normal_margin"
|
||||||
android:paddingVertical="@dimen/medium_margin"
|
android:paddingVertical="@dimen/medium_margin"
|
||||||
android:shadowColor="@color/default_background_color"
|
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:drawableStartCompat="@drawable/ic_playback_speed_vector"
|
app:drawableStartCompat="@drawable/ic_playback_speed_vector"
|
||||||
|
@ -113,4 +112,20 @@
|
||||||
app:layout_constraintTop_toTopOf="@+id/video_seekbar"
|
app:layout_constraintTop_toTopOf="@+id/video_seekbar"
|
||||||
tools:text="00:00" />
|
tools:text="00:00" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/video_toggle_mute"
|
||||||
|
android:layout_width="@dimen/video_player_button_width"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="@dimen/big_margin"
|
||||||
|
android:background="@drawable/darkened_automatic_circle_background"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="@dimen/normal_margin"
|
||||||
|
android:paddingVertical="@dimen/medium_margin"
|
||||||
|
android:src="@drawable/ic_vector_speaker_on"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/video_toggle_play_pause"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/video_toggle_play_pause"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
<dimen name="tmb_shadow_height">70dp</dimen>
|
<dimen name="tmb_shadow_height">70dp</dimen>
|
||||||
<dimen name="media_side_slider_width">60dp</dimen>
|
<dimen name="media_side_slider_width">60dp</dimen>
|
||||||
<dimen name="video_player_play_pause_size">60dp</dimen>
|
<dimen name="video_player_play_pause_size">60dp</dimen>
|
||||||
|
<dimen name="video_player_button_width">68dp</dimen>
|
||||||
<dimen name="list_view_folder_thumbnail_size">72dp</dimen>
|
<dimen name="list_view_folder_thumbnail_size">72dp</dimen>
|
||||||
<dimen name="bottom_actions_height">64dp</dimen>
|
<dimen name="bottom_actions_height">64dp</dimen>
|
||||||
<dimen name="bottom_actions_height_double">128dp</dimen>
|
<dimen name="bottom_actions_height_double">128dp</dimen>
|
||||||
|
|
Loading…
Reference in a new issue