mirror of
https://github.com/FossifyOrg/Gallery.git
synced 2024-11-25 05:57:59 +01:00
Video playback speed control (#222)
* add 2x playback speed option when longpress * changed/removed the x2 speed to x0.25-x5.0 with swip up/down after longpress * add visual indicator to playback speed, and possibility to reset speed when clicking * add setting for default playback speed * improve swip up/down sensitivity * revert changes that won't be used * added the playback system from Fossify Music-Player * adjust playback speed UI and added background * fixed playback speed control missing while a specific setting is enabled * fixed wrong playback speed icon when opening video * Use small letters for preference key * Restructure layout and cleanup code --------- Co-authored-by: Naveen Singh <36371707+naveensingh@users.noreply.github.com> Co-authored-by: Naveen Singh <snaveen935@gmail.com>
This commit is contained in:
parent
267fdaf69f
commit
ee755ef6de
13 changed files with 335 additions and 25 deletions
|
@ -1,5 +1,6 @@
|
||||||
package org.fossify.gallery.activities
|
package org.fossify.gallery.activities
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
|
@ -30,10 +31,13 @@ import org.fossify.commons.extensions.*
|
||||||
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.*
|
||||||
|
import org.fossify.gallery.fragments.PlaybackSpeedFragment
|
||||||
import org.fossify.gallery.helpers.*
|
import org.fossify.gallery.helpers.*
|
||||||
|
import org.fossify.gallery.interfaces.PlaybackSpeedListener
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener {
|
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
|
||||||
|
@ -181,6 +185,7 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
binding.bottomVideoTimeHolder.videoCurrTime.setOnClickListener { doSkip(false) }
|
binding.bottomVideoTimeHolder.videoCurrTime.setOnClickListener { doSkip(false) }
|
||||||
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.videoSurfaceFrame.setOnClickListener { toggleFullscreen() }
|
binding.videoSurfaceFrame.setOnClickListener { toggleFullscreen() }
|
||||||
binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
binding.videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
||||||
|
|
||||||
|
@ -256,6 +261,7 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
.setLoadControl(loadControl)
|
.setLoadControl(loadControl)
|
||||||
.build()
|
.build()
|
||||||
.apply {
|
.apply {
|
||||||
|
setPlaybackSpeed(config.playbackSpeed)
|
||||||
setMediaSource(mediaSource)
|
setMediaSource(mediaSource)
|
||||||
setAudioAttributes(
|
setAudioAttributes(
|
||||||
AudioAttributes
|
AudioAttributes
|
||||||
|
@ -299,10 +305,13 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
private fun videoPrepared() {
|
private fun videoPrepared() {
|
||||||
if (!mWasVideoStarted) {
|
if (!mWasVideoStarted) {
|
||||||
binding.bottomVideoTimeHolder.videoTogglePlayPause.beVisible()
|
binding.bottomVideoTimeHolder.videoTogglePlayPause.beVisible()
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.beVisible()
|
||||||
|
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()
|
||||||
setPosition(mCurrTime)
|
setPosition(mCurrTime)
|
||||||
|
updatePlaybackSpeed(config.playbackSpeed)
|
||||||
|
|
||||||
if (config.rememberLastVideoPosition) {
|
if (config.rememberLastVideoPosition) {
|
||||||
setLastVideoSavedPosition()
|
setLastVideoSavedPosition()
|
||||||
|
@ -468,6 +477,7 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
binding.bottomVideoTimeHolder.videoPrevFile,
|
binding.bottomVideoTimeHolder.videoPrevFile,
|
||||||
binding.bottomVideoTimeHolder.videoTogglePlayPause,
|
binding.bottomVideoTimeHolder.videoTogglePlayPause,
|
||||||
binding.bottomVideoTimeHolder.videoNextFile,
|
binding.bottomVideoTimeHolder.videoNextFile,
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed,
|
||||||
binding.bottomVideoTimeHolder.videoCurrTime,
|
binding.bottomVideoTimeHolder.videoCurrTime,
|
||||||
binding.bottomVideoTimeHolder.videoSeekbar,
|
binding.bottomVideoTimeHolder.videoSeekbar,
|
||||||
binding.bottomVideoTimeHolder.videoDuration,
|
binding.bottomVideoTimeHolder.videoDuration,
|
||||||
|
@ -480,6 +490,7 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
arrayOf(
|
arrayOf(
|
||||||
binding.bottomVideoTimeHolder.videoPrevFile,
|
binding.bottomVideoTimeHolder.videoPrevFile,
|
||||||
binding.bottomVideoTimeHolder.videoNextFile,
|
binding.bottomVideoTimeHolder.videoNextFile,
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed,
|
||||||
binding.bottomVideoTimeHolder.videoCurrTime,
|
binding.bottomVideoTimeHolder.videoCurrTime,
|
||||||
binding.bottomVideoTimeHolder.videoDuration,
|
binding.bottomVideoTimeHolder.videoDuration,
|
||||||
).forEach {
|
).forEach {
|
||||||
|
@ -493,6 +504,27 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showPlaybackSpeedPicker() {
|
||||||
|
val fragment = PlaybackSpeedFragment()
|
||||||
|
fragment.show(supportFragmentManager, PlaybackSpeedFragment::class.java.simpleName)
|
||||||
|
fragment.setListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updatePlaybackSpeed(speed: Float) {
|
||||||
|
val isSlow = speed < 1f
|
||||||
|
if (isSlow != binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag as? Boolean) {
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag = isSlow
|
||||||
|
|
||||||
|
val drawableId = if (isSlow) R.drawable.ic_playback_speed_slow_vector else R.drawable.ic_playback_speed_vector
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed
|
||||||
|
.setCompoundDrawablesRelativeWithIntrinsicBounds(resources.getDrawable(drawableId), null, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.text = "${DecimalFormat("#.##").format(speed)}x"
|
||||||
|
mExoPlayer?.setPlaybackSpeed(speed)
|
||||||
|
}
|
||||||
|
|
||||||
private fun initTimeHolder() {
|
private fun initTimeHolder() {
|
||||||
var right = 0
|
var right = 0
|
||||||
var bottom = 0
|
var bottom = 0
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
package org.fossify.gallery.fragments
|
||||||
|
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import org.fossify.commons.extensions.*
|
||||||
|
import org.fossify.commons.views.MySeekBar
|
||||||
|
import org.fossify.commons.views.MyTextView
|
||||||
|
import org.fossify.gallery.R
|
||||||
|
import org.fossify.gallery.databinding.FragmentPlaybackSpeedBinding
|
||||||
|
import org.fossify.gallery.extensions.config
|
||||||
|
import org.fossify.gallery.helpers.Config
|
||||||
|
import org.fossify.gallery.interfaces.PlaybackSpeedListener
|
||||||
|
|
||||||
|
class PlaybackSpeedFragment : BottomSheetDialogFragment() {
|
||||||
|
private val MIN_PLAYBACK_SPEED = 0.25f
|
||||||
|
private val MAX_PLAYBACK_SPEED = 3f
|
||||||
|
private val MAX_PROGRESS = (MAX_PLAYBACK_SPEED * 100 + MIN_PLAYBACK_SPEED * 100).toInt()
|
||||||
|
private val HALF_PROGRESS = MAX_PROGRESS / 2
|
||||||
|
private val STEP = 0.05f
|
||||||
|
|
||||||
|
private var seekBar: MySeekBar? = null
|
||||||
|
private var listener: PlaybackSpeedListener? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setStyle(STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
val config = requireContext().config
|
||||||
|
val binding = FragmentPlaybackSpeedBinding.inflate(inflater, container, false)
|
||||||
|
val background = ResourcesCompat.getDrawable(resources, org.fossify.commons.R.drawable.bottom_sheet_bg, requireContext().theme)
|
||||||
|
(background as LayerDrawable).findDrawableByLayerId(org.fossify.commons.R.id.bottom_sheet_background)
|
||||||
|
.applyColorFilter(requireContext().getProperBackgroundColor())
|
||||||
|
|
||||||
|
binding.apply {
|
||||||
|
seekBar = playbackSpeedSeekbar
|
||||||
|
root.setBackgroundDrawable(background)
|
||||||
|
requireContext().updateTextColors(playbackSpeedHolder)
|
||||||
|
playbackSpeedSlow.applyColorFilter(requireContext().getProperTextColor())
|
||||||
|
playbackSpeedFast.applyColorFilter(requireContext().getProperTextColor())
|
||||||
|
playbackSpeedSlow.setOnClickListener { reduceSpeed() }
|
||||||
|
playbackSpeedFast.setOnClickListener { increaseSpeed() }
|
||||||
|
initSeekbar(playbackSpeedSeekbar, playbackSpeedLabel, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initSeekbar(seekbar: MySeekBar, speedLabel: MyTextView, config: Config) {
|
||||||
|
val formattedValue = formatPlaybackSpeed(config.playbackSpeed)
|
||||||
|
speedLabel.text = "${formattedValue}x"
|
||||||
|
seekbar.max = MAX_PROGRESS
|
||||||
|
|
||||||
|
val playbackSpeedProgress = config.playbackSpeedProgress
|
||||||
|
if (playbackSpeedProgress == -1) {
|
||||||
|
config.playbackSpeedProgress = HALF_PROGRESS
|
||||||
|
}
|
||||||
|
seekbar.progress = config.playbackSpeedProgress
|
||||||
|
|
||||||
|
var lastUpdatedProgress = config.playbackSpeedProgress
|
||||||
|
var lastUpdatedFormattedValue = formattedValue
|
||||||
|
|
||||||
|
seekbar.onSeekBarChangeListener { progress ->
|
||||||
|
val playbackSpeed = getPlaybackSpeed(progress)
|
||||||
|
if (playbackSpeed.toString() != lastUpdatedFormattedValue) {
|
||||||
|
lastUpdatedProgress = progress
|
||||||
|
lastUpdatedFormattedValue = playbackSpeed.toString()
|
||||||
|
config.playbackSpeed = playbackSpeed
|
||||||
|
config.playbackSpeedProgress = progress
|
||||||
|
|
||||||
|
speedLabel.text = "${formatPlaybackSpeed(playbackSpeed)}x"
|
||||||
|
listener?.updatePlaybackSpeed(playbackSpeed)
|
||||||
|
} else {
|
||||||
|
seekbar.progress = lastUpdatedProgress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPlaybackSpeed(progress: Int): Float {
|
||||||
|
var playbackSpeed = when {
|
||||||
|
progress < HALF_PROGRESS -> {
|
||||||
|
val lowerProgressPercent = progress / HALF_PROGRESS.toFloat()
|
||||||
|
val lowerProgress = (1 - MIN_PLAYBACK_SPEED) * lowerProgressPercent + MIN_PLAYBACK_SPEED
|
||||||
|
lowerProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
progress > HALF_PROGRESS -> {
|
||||||
|
val upperProgressPercent = progress / HALF_PROGRESS.toFloat() - 1
|
||||||
|
val upperDiff = MAX_PLAYBACK_SPEED - 1
|
||||||
|
upperDiff * upperProgressPercent + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> 1f
|
||||||
|
}
|
||||||
|
playbackSpeed = Math.min(Math.max(playbackSpeed, MIN_PLAYBACK_SPEED), MAX_PLAYBACK_SPEED)
|
||||||
|
val stepMultiplier = 1 / STEP
|
||||||
|
return Math.round(playbackSpeed * stepMultiplier) / stepMultiplier
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reduceSpeed() {
|
||||||
|
var currentProgress = seekBar?.progress ?: return
|
||||||
|
val currentSpeed = requireContext().config.playbackSpeed
|
||||||
|
while (currentProgress > 0) {
|
||||||
|
val newSpeed = getPlaybackSpeed(--currentProgress)
|
||||||
|
if (newSpeed != currentSpeed) {
|
||||||
|
seekBar!!.progress = currentProgress
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun increaseSpeed() {
|
||||||
|
var currentProgress = seekBar?.progress ?: return
|
||||||
|
val currentSpeed = requireContext().config.playbackSpeed
|
||||||
|
while (currentProgress < MAX_PROGRESS) {
|
||||||
|
val newSpeed = getPlaybackSpeed(++currentProgress)
|
||||||
|
if (newSpeed != currentSpeed) {
|
||||||
|
seekBar!!.progress = currentProgress
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatPlaybackSpeed(value: Float) = String.format("%.2f", value)
|
||||||
|
|
||||||
|
fun setListener(playbackSpeedListener: PlaybackSpeedListener) {
|
||||||
|
listener = playbackSpeedListener
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package org.fossify.gallery.fragments
|
package org.fossify.gallery.fragments
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.graphics.SurfaceTexture
|
import android.graphics.SurfaceTexture
|
||||||
|
@ -34,13 +35,15 @@ import org.fossify.gallery.extensions.config
|
||||||
import org.fossify.gallery.extensions.hasNavBar
|
import org.fossify.gallery.extensions.hasNavBar
|
||||||
import org.fossify.gallery.extensions.parseFileChannel
|
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.models.Medium
|
import org.fossify.gallery.models.Medium
|
||||||
import org.fossify.gallery.views.MediaSideScroll
|
import org.fossify.gallery.views.MediaSideScroll
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener {
|
class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener, PlaybackSpeedListener {
|
||||||
private val PROGRESS = "progress"
|
private val PROGRESS = "progress"
|
||||||
|
|
||||||
private var mIsFullscreen = false
|
private var mIsFullscreen = false
|
||||||
|
@ -94,6 +97,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
bottomVideoTimeHolder.videoDuration.setOnClickListener { skip(true) }
|
bottomVideoTimeHolder.videoDuration.setOnClickListener { skip(true) }
|
||||||
videoHolder.setOnClickListener { toggleFullscreen() }
|
videoHolder.setOnClickListener { toggleFullscreen() }
|
||||||
videoPreview.setOnClickListener { toggleFullscreen() }
|
videoPreview.setOnClickListener { toggleFullscreen() }
|
||||||
|
bottomVideoTimeHolder.videoPlaybackSpeed.setOnClickListener { showPlaybackSpeedPicker() }
|
||||||
videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
videoSurfaceFrame.controller.settings.swallowDoubleTaps = true
|
||||||
|
|
||||||
videoPlayOutline.setOnClickListener {
|
videoPlayOutline.setOnClickListener {
|
||||||
|
@ -390,6 +394,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
if (mConfig.loopVideos && listener?.isSlideShowActive() == false) {
|
if (mConfig.loopVideos && listener?.isSlideShowActive() == false) {
|
||||||
repeatMode = Player.REPEAT_MODE_ONE
|
repeatMode = Player.REPEAT_MODE_ONE
|
||||||
}
|
}
|
||||||
|
setPlaybackSpeed(mConfig.playbackSpeed)
|
||||||
setMediaSource(mediaSource)
|
setMediaSource(mediaSource)
|
||||||
setAudioAttributes(
|
setAudioAttributes(
|
||||||
AudioAttributes
|
AudioAttributes
|
||||||
|
@ -521,7 +526,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
arrayOf(
|
arrayOf(
|
||||||
binding.bottomVideoTimeHolder.videoCurrTime,
|
binding.bottomVideoTimeHolder.videoCurrTime,
|
||||||
binding.bottomVideoTimeHolder.videoDuration,
|
binding.bottomVideoTimeHolder.videoDuration,
|
||||||
binding.bottomVideoTimeHolder.videoTogglePlayPause
|
binding.bottomVideoTimeHolder.videoTogglePlayPause,
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed
|
||||||
).forEach {
|
).forEach {
|
||||||
it.isClickable = !mIsFullscreen
|
it.isClickable = !mIsFullscreen
|
||||||
}
|
}
|
||||||
|
@ -538,6 +544,27 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showPlaybackSpeedPicker() {
|
||||||
|
val fragment = PlaybackSpeedFragment()
|
||||||
|
childFragmentManager.beginTransaction().add(fragment, fragment::class.java.simpleName).commit()
|
||||||
|
fragment.setListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updatePlaybackSpeed(speed: Float) {
|
||||||
|
val isSlow = speed < 1f
|
||||||
|
if (isSlow != binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag as? Boolean) {
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.tag = isSlow
|
||||||
|
|
||||||
|
val drawableId = if (isSlow) R.drawable.ic_playback_speed_slow_vector else R.drawable.ic_playback_speed_vector
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed
|
||||||
|
.setCompoundDrawablesRelativeWithIntrinsicBounds(resources.getDrawable(drawableId), null, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.text = "${DecimalFormat("#.##").format(speed)}x"
|
||||||
|
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 = context?.resources?.getDimension(org.fossify.commons.R.dimen.small_margin) ?: return 0f
|
||||||
val fullscreenOffset = smallMargin + if (mIsFullscreen) 0 else requireContext().navigationBarHeight
|
val fullscreenOffset = smallMargin + if (mIsFullscreen) 0 else requireContext().navigationBarHeight
|
||||||
|
@ -662,6 +689,8 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
if (!mWasVideoStarted) {
|
if (!mWasVideoStarted) {
|
||||||
binding.videoPlayOutline.beGone()
|
binding.videoPlayOutline.beGone()
|
||||||
mPlayPauseButton.beVisible()
|
mPlayPauseButton.beVisible()
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.beVisible()
|
||||||
|
binding.bottomVideoTimeHolder.videoPlaybackSpeed.text = "${DecimalFormat("#.##").format(mConfig.playbackSpeed)}x"
|
||||||
}
|
}
|
||||||
|
|
||||||
mWasVideoStarted = true
|
mWasVideoStarted = true
|
||||||
|
@ -736,6 +765,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
mExoPlayer?.seekTo(mPositionAtPause)
|
mExoPlayer?.seekTo(mPositionAtPause)
|
||||||
mPositionAtPause = 0L
|
mPositionAtPause = 0L
|
||||||
}
|
}
|
||||||
|
updatePlaybackSpeed(mConfig.playbackSpeed)
|
||||||
playVideo()
|
playVideo()
|
||||||
}
|
}
|
||||||
mWasPlayerInited = true
|
mWasPlayerInited = true
|
||||||
|
|
|
@ -169,6 +169,14 @@ class Config(context: Context) : BaseConfig(context) {
|
||||||
get() = prefs.getBoolean(MAX_BRIGHTNESS, false)
|
get() = prefs.getBoolean(MAX_BRIGHTNESS, false)
|
||||||
set(maxBrightness) = prefs.edit().putBoolean(MAX_BRIGHTNESS, maxBrightness).apply()
|
set(maxBrightness) = prefs.edit().putBoolean(MAX_BRIGHTNESS, maxBrightness).apply()
|
||||||
|
|
||||||
|
var playbackSpeed: Float
|
||||||
|
get() = prefs.getFloat(PLAYBACK_SPEED, 1f)
|
||||||
|
set(playbackSpeed) = prefs.edit().putFloat(PLAYBACK_SPEED, playbackSpeed).apply()
|
||||||
|
|
||||||
|
var playbackSpeedProgress: Int
|
||||||
|
get() = prefs.getInt(PLAYBACK_SPEED_PROGRESS, -1)
|
||||||
|
set(playbackSpeedProgress) = prefs.edit().putInt(PLAYBACK_SPEED_PROGRESS, playbackSpeedProgress).apply()
|
||||||
|
|
||||||
var cropThumbnails: Boolean
|
var cropThumbnails: Boolean
|
||||||
get() = prefs.getBoolean(CROP_THUMBNAILS, true)
|
get() = prefs.getBoolean(CROP_THUMBNAILS, true)
|
||||||
set(cropThumbnails) = prefs.edit().putBoolean(CROP_THUMBNAILS, cropThumbnails).apply()
|
set(cropThumbnails) = prefs.edit().putBoolean(CROP_THUMBNAILS, cropThumbnails).apply()
|
||||||
|
|
|
@ -19,6 +19,8 @@ const val LOOP_VIDEOS = "loop_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"
|
||||||
|
const val PLAYBACK_SPEED = "playback_speed"
|
||||||
|
const val PLAYBACK_SPEED_PROGRESS = "playback_speed_progress"
|
||||||
const val CROP_THUMBNAILS = "crop_thumbnails"
|
const val CROP_THUMBNAILS = "crop_thumbnails"
|
||||||
const val SHOW_THUMBNAIL_VIDEO_DURATION = "show_thumbnail_video_duration"
|
const val SHOW_THUMBNAIL_VIDEO_DURATION = "show_thumbnail_video_duration"
|
||||||
const val SCREEN_ROTATION = "screen_rotation"
|
const val SCREEN_ROTATION = "screen_rotation"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.fossify.gallery.interfaces
|
||||||
|
|
||||||
|
interface PlaybackSpeedListener {
|
||||||
|
fun updatePlaybackSpeed(speed: Float)
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#FFFFFFFF" android:pathData="M13.5 5.5c1.1 0 2-0.9 2-2s-0.9-2-2-2-2 0.9-2 2 0.9 2 2 2zm6.5 7V23h-1V12.5c0-0.28-0.22-0.5-0.5-0.5S18 12.22 18 12.5v1h-1v-0.69c-1.46-0.38-2.7-1.29-3.51-2.52C13.18 11.16 13 12.07 13 13c0 0.23 0.02 0.46 0.03 0.69L15 16.5V23h-2v-5l-1.78-2.54L11 19l-3 4-1.6-1.2L9 18.33V13c0-1.15 0.18-2.29 0.5-3.39L8 10.46V14H6V9.3l5.4-3.07v0.01c0.59-0.31 1.32-0.33 1.94 0.03 0.36 0.21 0.63 0.51 0.8 0.85l0.79 1.67C15.58 10.1 16.94 11 18.5 11c0.83 0 1.5 0.67 1.5 1.5z"/>
|
||||||
|
</vector>
|
3
app/src/main/res/drawable/ic_playback_speed_vector.xml
Normal file
3
app/src/main/res/drawable/ic_playback_speed_vector.xml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:fillColor="#FFFFFFFF" android:pathData="M13.49 5.48c1.1 0 2-0.9 2-2s-0.9-2-2-2-2 0.9-2 2 0.9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 0.6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-0.4-0.6-1-1-1.7-1-0.3 0-0.5 0.1-0.8 0.1l-5.2 2.2v4.7h2v-3.4l1.8-0.7-1.6 8.1-4.9-1-0.4 2 7 1.4z"/>
|
||||||
|
</vector>
|
|
@ -1,54 +1,78 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/video_time_holder"
|
android:id="@+id/video_time_holder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentBottom="true">
|
android:layout_alignParentBottom="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/video_playback_speed"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/big_margin"
|
||||||
|
android:background="@drawable/darkened_automatic_circle_background"
|
||||||
|
android:drawablePadding="@dimen/tiny_margin"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingHorizontal="@dimen/normal_margin"
|
||||||
|
android:paddingVertical="@dimen/medium_margin"
|
||||||
|
android:shadowColor="@color/default_background_color"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:drawableStartCompat="@drawable/ic_playback_speed_vector"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/video_toggle_play_pause"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/video_toggle_play_pause"
|
||||||
|
tools:text="1x"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/video_prev_file"
|
android:id="@+id/video_prev_file"
|
||||||
android:layout_width="@dimen/video_player_play_pause_size"
|
android:layout_width="@dimen/video_player_play_pause_size"
|
||||||
android:layout_height="@dimen/video_player_play_pause_size"
|
android:layout_height="@dimen/video_player_play_pause_size"
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_marginStart="@dimen/small_margin"
|
android:layout_marginStart="@dimen/small_margin"
|
||||||
android:layout_marginTop="@dimen/activity_margin"
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:padding="@dimen/normal_margin"
|
android:padding="@dimen/normal_margin"
|
||||||
android:src="@drawable/ic_prev_outline_vector"
|
android:src="@drawable/ic_prev_outline_vector"
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/video_toggle_play_pause"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/video_toggle_play_pause"
|
android:id="@+id/video_toggle_play_pause"
|
||||||
android:layout_width="@dimen/video_player_play_pause_size"
|
android:layout_width="@dimen/video_player_play_pause_size"
|
||||||
android:layout_height="@dimen/video_player_play_pause_size"
|
android:layout_height="@dimen/video_player_play_pause_size"
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:layout_marginTop="@dimen/activity_margin"
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:padding="@dimen/small_margin"
|
android:padding="@dimen/small_margin"
|
||||||
android:src="@drawable/ic_pause_outline_vector"
|
android:src="@drawable/ic_pause_outline_vector"
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/video_next_file"
|
android:id="@+id/video_next_file"
|
||||||
android:layout_width="@dimen/video_player_play_pause_size"
|
android:layout_width="@dimen/video_player_play_pause_size"
|
||||||
android:layout_height="@dimen/video_player_play_pause_size"
|
android:layout_height="@dimen/video_player_play_pause_size"
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_marginTop="@dimen/activity_margin"
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
android:layout_marginEnd="@dimen/small_margin"
|
android:layout_marginEnd="@dimen/small_margin"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:padding="@dimen/normal_margin"
|
android:padding="@dimen/normal_margin"
|
||||||
android:src="@drawable/ic_next_outline_vector"
|
android:src="@drawable/ic_next_outline_vector"
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/video_toggle_play_pause"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/video_curr_time"
|
android:id="@+id/video_curr_time"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@+id/video_toggle_play_pause"
|
|
||||||
android:layout_alignTop="@+id/video_seekbar"
|
|
||||||
android:layout_alignBottom="@+id/video_seekbar"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingLeft="@dimen/activity_margin"
|
android:paddingLeft="@dimen/activity_margin"
|
||||||
|
@ -56,26 +80,26 @@
|
||||||
android:shadowColor="@color/default_background_color"
|
android:shadowColor="@color/default_background_color"
|
||||||
android:shadowRadius="2"
|
android:shadowRadius="2"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/video_seekbar"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/video_toggle_play_pause"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/video_seekbar"
|
||||||
tools:text="00:00" />
|
tools:text="00:00" />
|
||||||
|
|
||||||
<org.fossify.commons.views.MySeekBar
|
<org.fossify.commons.views.MySeekBar
|
||||||
android:id="@+id/video_seekbar"
|
android:id="@+id/video_seekbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/video_toggle_play_pause"
|
|
||||||
android:layout_toStartOf="@+id/video_duration"
|
|
||||||
android:layout_toEndOf="@+id/video_curr_time"
|
|
||||||
android:paddingTop="@dimen/activity_margin"
|
android:paddingTop="@dimen/activity_margin"
|
||||||
android:paddingBottom="@dimen/activity_margin" />
|
android:paddingBottom="@dimen/activity_margin"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/video_duration"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/video_curr_time"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/video_toggle_play_pause" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/video_duration"
|
android:id="@+id/video_duration"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/video_toggle_play_pause"
|
|
||||||
android:layout_alignTop="@+id/video_seekbar"
|
|
||||||
android:layout_alignBottom="@+id/video_seekbar"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingLeft="@dimen/activity_margin"
|
android:paddingLeft="@dimen/activity_margin"
|
||||||
|
@ -83,6 +107,10 @@
|
||||||
android:shadowColor="@color/default_background_color"
|
android:shadowColor="@color/default_background_color"
|
||||||
android:shadowRadius="2"
|
android:shadowRadius="2"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/video_seekbar"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/video_toggle_play_pause"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/video_seekbar"
|
||||||
tools:text="00:00" />
|
tools:text="00:00" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
54
app/src/main/res/layout/fragment_playback_speed.xml
Normal file
54
app/src/main/res/layout/fragment_playback_speed.xml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/playback_speed_holder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/activity_margin">
|
||||||
|
|
||||||
|
<org.fossify.commons.views.MyTextView
|
||||||
|
android:id="@+id/playback_speed_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/activity_margin"
|
||||||
|
android:padding="@dimen/medium_margin"
|
||||||
|
android:text="@string/playback_speed"
|
||||||
|
android:textSize="@dimen/actionbar_text_size" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playback_speed_slow"
|
||||||
|
android:layout_width="@dimen/smaller_icon_size"
|
||||||
|
android:layout_height="@dimen/smaller_icon_size"
|
||||||
|
android:layout_below="@+id/playback_speed_title"
|
||||||
|
android:layout_alignStart="@+id/playback_speed_seekbar"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_playback_speed_slow_vector" />
|
||||||
|
|
||||||
|
<org.fossify.commons.views.MyTextView
|
||||||
|
android:id="@+id/playback_speed_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBottom="@+id/playback_speed_slow"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:textSize="@dimen/big_text_size"
|
||||||
|
tools:text="1.00x" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/playback_speed_fast"
|
||||||
|
android:layout_width="@dimen/smaller_icon_size"
|
||||||
|
android:layout_height="@dimen/smaller_icon_size"
|
||||||
|
android:layout_below="@+id/playback_speed_title"
|
||||||
|
android:layout_alignEnd="@+id/playback_speed_seekbar"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@drawable/ic_playback_speed_vector" />
|
||||||
|
|
||||||
|
<org.fossify.commons.views.MySeekBar
|
||||||
|
android:id="@+id/playback_speed_seekbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/playback_speed_slow"
|
||||||
|
android:layout_marginBottom="@dimen/medium_margin"
|
||||||
|
android:padding="@dimen/activity_margin" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -27,4 +27,5 @@
|
||||||
<dimen name="lock_padding">30dp</dimen>
|
<dimen name="lock_padding">30dp</dimen>
|
||||||
<dimen name="sample_thumbnail_size">180dp</dimen>
|
<dimen name="sample_thumbnail_size">180dp</dimen>
|
||||||
<dimen name="directory_picker_dialog_min_height">180dp</dimen>
|
<dimen name="directory_picker_dialog_min_height">180dp</dimen>
|
||||||
|
<dimen name="smaller_icon_size">36dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -182,6 +182,7 @@
|
||||||
<string name="delete_empty_folders">Delete empty folders after deleting their content</string>
|
<string name="delete_empty_folders">Delete empty folders after deleting their content</string>
|
||||||
<string name="allow_photo_gestures">Allow controlling photo brightness with vertical gestures</string>
|
<string name="allow_photo_gestures">Allow controlling photo brightness with vertical gestures</string>
|
||||||
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string>
|
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string>
|
||||||
|
<string name="playback_speed">Playback speed</string>
|
||||||
<string name="show_media_count">Show folder media count on the main view</string>
|
<string name="show_media_count">Show folder media count on the main view</string>
|
||||||
<string name="show_extended_details">Show extended details over fullscreen media</string>
|
<string name="show_extended_details">Show extended details over fullscreen media</string>
|
||||||
<string name="manage_extended_details">Manage extended details</string>
|
<string name="manage_extended_details">Manage extended details</string>
|
||||||
|
|
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
<style name="AppTheme" parent="AppTheme.Base" />
|
<style name="AppTheme" parent="AppTheme.Base" />
|
||||||
|
|
||||||
|
<style name="CustomBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
|
||||||
|
<item name="bottomSheetStyle">@style/CustomBottomSheetStyle</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="CustomBottomSheetStyle" parent="Widget.Design.BottomSheet.Modal">
|
||||||
|
<item name="android:background">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="FullScreenTheme.Base" parent="AppTheme">
|
<style name="FullScreenTheme.Base" parent="AppTheme">
|
||||||
<item name="android:windowContentOverlay">@null</item>
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
<item name="android:windowTranslucentNavigation">true</item>
|
<item name="android:windowTranslucentNavigation">true</item>
|
||||||
|
|
Loading…
Reference in a new issue