mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 00:10:32 +05:30
Merge pull request #2040 from Kruna1Pate1/feat/brightness-volume-swipe-control
Add support for swipe gesture
This commit is contained in:
commit
55fbd3437a
@ -83,6 +83,7 @@ object PreferenceKeys {
|
|||||||
const val ALTERNATIVE_PLAYER_LAYOUT = "alternative_player_layout"
|
const val ALTERNATIVE_PLAYER_LAYOUT = "alternative_player_layout"
|
||||||
const val USE_HLS_OVER_DASH = "use_hls"
|
const val USE_HLS_OVER_DASH = "use_hls"
|
||||||
const val QUEUE_AUTO_INSERT_RELATED = "queue_insert_related_videos"
|
const val QUEUE_AUTO_INSERT_RELATED = "queue_insert_related_videos"
|
||||||
|
const val PLAYER_SWIPE_CONTROLS = "player_swipe_controls"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background mode
|
* Background mode
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.github.libretube.extensions
|
||||||
|
|
||||||
|
fun Int.normalize(oldMin: Int, oldMax: Int, newMin: Int, newMax: Int): Int {
|
||||||
|
val oldRange = oldMax - oldMin
|
||||||
|
val newRange = newMax - newMin
|
||||||
|
|
||||||
|
return (this - oldMin) * newRange / oldRange + newMin
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Float.normalize(oldMin: Float, oldMax: Float, newMin: Float, newMax: Float): Float {
|
||||||
|
val oldRange = oldMax - oldMin
|
||||||
|
val newRange = newMax - newMin
|
||||||
|
|
||||||
|
return (this - oldMin) * newRange / oldRange + newMin
|
||||||
|
}
|
@ -70,6 +70,7 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
binding.player.initialize(
|
binding.player.initialize(
|
||||||
null,
|
null,
|
||||||
binding.doubleTapOverlay.binding,
|
binding.doubleTapOverlay.binding,
|
||||||
|
binding.playerGestureControlsView.binding,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ import com.github.libretube.constants.PreferenceKeys
|
|||||||
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
import com.github.libretube.databinding.FragmentPlayerBinding
|
import com.github.libretube.databinding.FragmentPlayerBinding
|
||||||
|
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
|
||||||
import com.github.libretube.db.DatabaseHelper
|
import com.github.libretube.db.DatabaseHelper
|
||||||
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
import com.github.libretube.db.DatabaseHolder.Companion.Database
|
||||||
import com.github.libretube.db.obj.WatchPosition
|
import com.github.libretube.db.obj.WatchPosition
|
||||||
@ -118,6 +119,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
lateinit var binding: FragmentPlayerBinding
|
lateinit var binding: FragmentPlayerBinding
|
||||||
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
||||||
private lateinit var doubleTapOverlayBinding: DoubleTapOverlayBinding
|
private lateinit var doubleTapOverlayBinding: DoubleTapOverlayBinding
|
||||||
|
private lateinit var playerGestureControlsViewBinding: PlayerGestureControlsViewBinding
|
||||||
private val viewModel: PlayerViewModel by activityViewModels()
|
private val viewModel: PlayerViewModel by activityViewModels()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,6 +185,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
exoPlayerView = binding.player
|
exoPlayerView = binding.player
|
||||||
playerBinding = binding.player.binding
|
playerBinding = binding.player.binding
|
||||||
doubleTapOverlayBinding = binding.doubleTapOverlay.binding
|
doubleTapOverlayBinding = binding.doubleTapOverlay.binding
|
||||||
|
playerGestureControlsViewBinding = binding.playerGestureControlsView.binding
|
||||||
|
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
return binding.root
|
return binding.root
|
||||||
@ -775,6 +778,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
binding.player.initialize(
|
binding.player.initialize(
|
||||||
this,
|
this,
|
||||||
doubleTapOverlayBinding,
|
doubleTapOverlayBinding,
|
||||||
|
playerGestureControlsViewBinding,
|
||||||
trackSelector
|
trackSelector
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
package com.github.libretube.ui.interfaces
|
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.os.SystemClock
|
|
||||||
import android.view.View
|
|
||||||
|
|
||||||
abstract class DoubleTapListener : View.OnClickListener {
|
|
||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
|
||||||
|
|
||||||
private var lastClick = 0L
|
|
||||||
private var lastDoubleClick = 0L
|
|
||||||
|
|
||||||
abstract fun onDoubleClick()
|
|
||||||
abstract fun onSingleClick()
|
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
|
||||||
if (isSecondClick()) {
|
|
||||||
handler.removeCallbacks(runnable)
|
|
||||||
lastDoubleClick = elapsedTime()
|
|
||||||
onDoubleClick()
|
|
||||||
} else {
|
|
||||||
if (recentDoubleClick()) return
|
|
||||||
handler.removeCallbacks(runnable)
|
|
||||||
handler.postDelayed(runnable, MAX_TIME_DIFF)
|
|
||||||
lastClick = elapsedTime()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val runnable = Runnable {
|
|
||||||
if (isSecondClick()) return@Runnable
|
|
||||||
onSingleClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSecondClick(): Boolean {
|
|
||||||
return elapsedTime() - lastClick < MAX_TIME_DIFF
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recentDoubleClick(): Boolean {
|
|
||||||
return elapsedTime() - lastDoubleClick < MAX_TIME_DIFF / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
fun elapsedTime() = SystemClock.elapsedRealtime()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val MAX_TIME_DIFF = 400L
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.github.libretube.ui.interfaces
|
||||||
|
|
||||||
|
interface PlayerGestureOptions {
|
||||||
|
|
||||||
|
fun onSingleTap()
|
||||||
|
|
||||||
|
fun onDoubleTapCenterScreen()
|
||||||
|
|
||||||
|
fun onDoubleTapLeftScreen()
|
||||||
|
|
||||||
|
fun onDoubleTapRightScreen()
|
||||||
|
|
||||||
|
fun onSwipeLeftScreen(distanceY: Float)
|
||||||
|
|
||||||
|
fun onSwipeRightScreen(distanceY: Float)
|
||||||
|
|
||||||
|
fun onSwipeEnd()
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.github.libretube.ui.views
|
package com.github.libretube.ui.views
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
@ -8,18 +9,24 @@ import android.os.Looper
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
|
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
|
||||||
|
import com.github.libretube.extensions.normalize
|
||||||
import com.github.libretube.extensions.toDp
|
import com.github.libretube.extensions.toDp
|
||||||
import com.github.libretube.obj.BottomSheetItem
|
import com.github.libretube.obj.BottomSheetItem
|
||||||
import com.github.libretube.ui.activities.MainActivity
|
import com.github.libretube.ui.activities.MainActivity
|
||||||
import com.github.libretube.ui.base.BaseActivity
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
import com.github.libretube.ui.interfaces.DoubleTapListener
|
|
||||||
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
|
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
|
||||||
|
import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
||||||
import com.github.libretube.ui.interfaces.PlayerOptions
|
import com.github.libretube.ui.interfaces.PlayerOptions
|
||||||
import com.github.libretube.ui.sheets.BaseBottomSheet
|
import com.github.libretube.ui.sheets.BaseBottomSheet
|
||||||
import com.github.libretube.ui.sheets.PlaybackSpeedSheet
|
import com.github.libretube.ui.sheets.PlaybackSpeedSheet
|
||||||
|
import com.github.libretube.util.AudioHelper
|
||||||
|
import com.github.libretube.util.BrightnessHelper
|
||||||
|
import com.github.libretube.util.PlayerGestureController
|
||||||
import com.github.libretube.util.PlayerHelper
|
import com.github.libretube.util.PlayerHelper
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
import com.google.android.exoplayer2.PlaybackParameters
|
import com.google.android.exoplayer2.PlaybackParameters
|
||||||
@ -33,8 +40,16 @@ import com.google.android.exoplayer2.util.RepeatModeUtil
|
|||||||
internal class CustomExoPlayerView(
|
internal class CustomExoPlayerView(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null
|
attributeSet: AttributeSet? = null
|
||||||
) : StyledPlayerView(context, attributeSet), PlayerOptions {
|
) : StyledPlayerView(context, attributeSet), PlayerOptions, PlayerGestureOptions {
|
||||||
val binding: ExoStyledPlayerControlViewBinding = ExoStyledPlayerControlViewBinding.bind(this)
|
val binding: ExoStyledPlayerControlViewBinding = ExoStyledPlayerControlViewBinding.bind(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Objects for player tap and swipe gesture
|
||||||
|
*/
|
||||||
|
private lateinit var gestureViewBinding: PlayerGestureControlsViewBinding
|
||||||
|
private lateinit var playerGestureController: PlayerGestureController
|
||||||
|
private lateinit var brightnessHelper: BrightnessHelper
|
||||||
|
private lateinit var audioHelper: AudioHelper
|
||||||
private var doubleTapOverlayBinding: DoubleTapOverlayBinding? = null
|
private var doubleTapOverlayBinding: DoubleTapOverlayBinding? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,16 +60,12 @@ internal class CustomExoPlayerView(
|
|||||||
|
|
||||||
private val runnableHandler = Handler(Looper.getMainLooper())
|
private val runnableHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
// the x-position of where the user clicked
|
|
||||||
private var xPos = 0F
|
|
||||||
|
|
||||||
var isPlayerLocked: Boolean = false
|
var isPlayerLocked: Boolean = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preferences
|
* Preferences
|
||||||
*/
|
*/
|
||||||
var autoplayEnabled = PlayerHelper.autoPlayEnabled
|
var autoplayEnabled = PlayerHelper.autoPlayEnabled
|
||||||
private var doubleTapAllowed = true
|
|
||||||
|
|
||||||
private var resizeModePref = PlayerHelper.resizeModePref
|
private var resizeModePref = PlayerHelper.resizeModePref
|
||||||
|
|
||||||
@ -65,42 +76,23 @@ internal class CustomExoPlayerView(
|
|||||||
if (isControllerFullyVisible) hideController() else showController()
|
if (isControllerFullyVisible) hideController() else showController()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val doubleTouchListener = object : DoubleTapListener() {
|
|
||||||
override fun onDoubleClick() {
|
|
||||||
if (!doubleTapAllowed) return
|
|
||||||
val eventPositionPercentageX = xPos / width
|
|
||||||
when {
|
|
||||||
eventPositionPercentageX < 0.4 -> rewind()
|
|
||||||
eventPositionPercentageX > 0.6 -> forward()
|
|
||||||
else -> {
|
|
||||||
player?.let { player ->
|
|
||||||
if (player.isPlaying) {
|
|
||||||
player.pause()
|
|
||||||
} else {
|
|
||||||
player.play()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSingleClick() {
|
|
||||||
toggleController()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun initialize(
|
fun initialize(
|
||||||
playerViewInterface: OnlinePlayerOptions?,
|
playerViewInterface: OnlinePlayerOptions?,
|
||||||
doubleTapOverlayBinding: DoubleTapOverlayBinding,
|
doubleTapOverlayBinding: DoubleTapOverlayBinding,
|
||||||
|
playerGestureControlsViewBinding: PlayerGestureControlsViewBinding,
|
||||||
trackSelector: TrackSelector?
|
trackSelector: TrackSelector?
|
||||||
) {
|
) {
|
||||||
this.playerOptionsInterface = playerViewInterface
|
this.playerOptionsInterface = playerViewInterface
|
||||||
this.doubleTapOverlayBinding = doubleTapOverlayBinding
|
this.doubleTapOverlayBinding = doubleTapOverlayBinding
|
||||||
this.trackSelector = trackSelector
|
this.trackSelector = trackSelector
|
||||||
|
this.gestureViewBinding = playerGestureControlsViewBinding
|
||||||
|
this.playerGestureController = PlayerGestureController(context, this)
|
||||||
|
this.brightnessHelper = BrightnessHelper(context as Activity)
|
||||||
|
this.audioHelper = AudioHelper(context)
|
||||||
|
|
||||||
// set the double click listener for rewind/forward
|
// Set touch listner for tap and swipe gestures.
|
||||||
setOnClickListener(doubleTouchListener)
|
setOnTouchListener(playerGestureController)
|
||||||
|
initializeGestureProgress()
|
||||||
enableDoubleTapToSeek()
|
enableDoubleTapToSeek()
|
||||||
|
|
||||||
initializeAdvancedOptions(context)
|
initializeAdvancedOptions(context)
|
||||||
@ -144,10 +136,6 @@ internal class CustomExoPlayerView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||||
// save the x position of the touch event
|
|
||||||
xPos = event.x
|
|
||||||
// listen for a double touch
|
|
||||||
doubleTouchListener.onClick(this)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,8 +249,8 @@ internal class CustomExoPlayerView(
|
|||||||
binding.exoBottomBar.visibility = visibility
|
binding.exoBottomBar.visibility = visibility
|
||||||
binding.closeImageButton.visibility = visibility
|
binding.closeImageButton.visibility = visibility
|
||||||
|
|
||||||
// disable double tap to seek if the player is locked
|
// disable tap and swipe gesture if the player is locked
|
||||||
doubleTapAllowed = !isLocked
|
playerGestureController.isEnabled = isLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableDoubleTapToSeek() {
|
private fun enableDoubleTapToSeek() {
|
||||||
@ -331,6 +319,56 @@ internal class CustomExoPlayerView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initializeGestureProgress() {
|
||||||
|
val brightnessBar = gestureViewBinding.brightnessProgressBar
|
||||||
|
val volumeBar = gestureViewBinding.volumeProgressBar
|
||||||
|
|
||||||
|
brightnessBar.progress = if (brightnessHelper.brightness == WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE) {
|
||||||
|
25.normalize(0, 100, 0, volumeBar.max)
|
||||||
|
} else {
|
||||||
|
brightnessHelper.getBrightnessWithScale(brightnessBar.max.toFloat()).toInt()
|
||||||
|
}
|
||||||
|
volumeBar.progress = audioHelper.getVolumeWithScale(volumeBar.max)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateBrightness(distance: Float) {
|
||||||
|
gestureViewBinding.brightnessControlView.visibility = View.VISIBLE
|
||||||
|
val bar = gestureViewBinding.brightnessProgressBar
|
||||||
|
|
||||||
|
if (bar.progress == 0) {
|
||||||
|
// If brightness progress goes to below 0, set to system brightness
|
||||||
|
if (distance <= 0) {
|
||||||
|
brightnessHelper.resetToSystemBrightness()
|
||||||
|
gestureViewBinding.brightnessImageView.setImageResource(R.drawable.ic_brightness_auto)
|
||||||
|
gestureViewBinding.brightnessTextView.text = resources.getString(R.string.auto)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gestureViewBinding.brightnessImageView.setImageResource(R.drawable.ic_brightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
bar.incrementProgressBy(distance.toInt())
|
||||||
|
gestureViewBinding.brightnessTextView.text = "${bar.progress.normalize(0, bar.max, 0, 100)}"
|
||||||
|
brightnessHelper.setBrightnessWithScale(bar.progress.toFloat(), bar.max.toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateVolume(distance: Float) {
|
||||||
|
gestureViewBinding.volumeControlView.visibility = View.VISIBLE
|
||||||
|
val bar = gestureViewBinding.volumeProgressBar
|
||||||
|
|
||||||
|
if (bar.progress == 0) {
|
||||||
|
gestureViewBinding.volumeImageView.setImageResource(
|
||||||
|
when {
|
||||||
|
distance > 0 -> R.drawable.ic_volume_up
|
||||||
|
else -> R.drawable.ic_volume_off
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
bar.incrementProgressBy(distance.toInt())
|
||||||
|
audioHelper.setVolumeWithScale(bar.progress, bar.max)
|
||||||
|
|
||||||
|
gestureViewBinding.volumeTextView.text = "${bar.progress.normalize(0, bar.max, 0, 100)}"
|
||||||
|
}
|
||||||
|
|
||||||
override fun onAutoplayClicked() {
|
override fun onAutoplayClicked() {
|
||||||
// autoplay options dialog
|
// autoplay options dialog
|
||||||
BaseBottomSheet()
|
BaseBottomSheet()
|
||||||
@ -408,4 +446,47 @@ internal class CustomExoPlayerView(
|
|||||||
it.layoutParams = params
|
it.layoutParams = params
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSingleTap() {
|
||||||
|
toggleController()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDoubleTapCenterScreen() {
|
||||||
|
player?.let { player ->
|
||||||
|
if (player.isPlaying) {
|
||||||
|
player.pause()
|
||||||
|
if (!isControllerFullyVisible) showController()
|
||||||
|
} else {
|
||||||
|
player.play()
|
||||||
|
if (isControllerFullyVisible) hideController()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDoubleTapLeftScreen() {
|
||||||
|
rewind()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDoubleTapRightScreen() {
|
||||||
|
forward()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwipeLeftScreen(distanceY: Float) {
|
||||||
|
if (!PlayerHelper.swipeGestureEnabled || resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) return
|
||||||
|
|
||||||
|
if (isControllerFullyVisible) hideController()
|
||||||
|
updateBrightness(distanceY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwipeRightScreen(distanceY: Float) {
|
||||||
|
if (!PlayerHelper.swipeGestureEnabled || resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) return
|
||||||
|
|
||||||
|
if (isControllerFullyVisible) hideController()
|
||||||
|
updateVolume(distanceY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwipeEnd() {
|
||||||
|
gestureViewBinding.brightnessControlView.visibility = View.GONE
|
||||||
|
gestureViewBinding.volumeControlView.visibility = View.GONE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.github.libretube.ui.views
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import com.github.libretube.databinding.PlayerGestureControlsViewBinding
|
||||||
|
|
||||||
|
class PlayerGestureControlsView(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null
|
||||||
|
) : ConstraintLayout(context, attrs) {
|
||||||
|
var binding: PlayerGestureControlsViewBinding
|
||||||
|
|
||||||
|
init {
|
||||||
|
val layoutInflater = LayoutInflater.from(context)
|
||||||
|
binding = PlayerGestureControlsViewBinding.inflate(layoutInflater, this, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
|
||||||
|
super.onSizeChanged(width, height, oldHeight, oldHeight)
|
||||||
|
|
||||||
|
binding.brightnessProgressBar.max = (height * 0.7).toInt()
|
||||||
|
binding.volumeProgressBar.max = (height * 0.7).toInt()
|
||||||
|
}
|
||||||
|
}
|
50
app/src/main/java/com/github/libretube/util/AudioHelper.kt
Normal file
50
app/src/main/java/com/github/libretube/util/AudioHelper.kt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package com.github.libretube.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.media.AudioManager
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.math.MathUtils
|
||||||
|
import com.github.libretube.extensions.normalize
|
||||||
|
|
||||||
|
class AudioHelper(
|
||||||
|
context: Context,
|
||||||
|
private val stream: Int = AudioManager.STREAM_MUSIC
|
||||||
|
) {
|
||||||
|
|
||||||
|
private lateinit var audioManager: AudioManager
|
||||||
|
private var minimumVolumeIndex = 0
|
||||||
|
private var maximumVolumeIndex = 16
|
||||||
|
|
||||||
|
init {
|
||||||
|
(context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager)?.let {
|
||||||
|
audioManager = it
|
||||||
|
maximumVolumeIndex = it.getStreamMaxVolume(stream)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
minimumVolumeIndex = it.getStreamMinVolume(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var volume: Int
|
||||||
|
get() {
|
||||||
|
return if (this::audioManager.isInitialized) {
|
||||||
|
audioManager.getStreamVolume(stream) - minimumVolumeIndex
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
if (this::audioManager.isInitialized) {
|
||||||
|
val vol = MathUtils.clamp(value, minimumVolumeIndex, maximumVolumeIndex)
|
||||||
|
audioManager.setStreamVolume(stream, vol, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setVolumeWithScale(value: Int, maxValue: Int, minValue: Int = 0) {
|
||||||
|
volume = value.normalize(minValue, maxValue, minimumVolumeIndex, maximumVolumeIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVolumeWithScale(maxValue: Int, minValue: Int = 0): Int {
|
||||||
|
return volume.normalize(minimumVolumeIndex, maximumVolumeIndex, minValue, maxValue)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.github.libretube.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.WindowManager
|
||||||
|
import com.github.libretube.extensions.normalize
|
||||||
|
|
||||||
|
class BrightnessHelper(activity: Activity) {
|
||||||
|
|
||||||
|
private val window = activity.window
|
||||||
|
private val minBrightness = 0.0f
|
||||||
|
private val maxBrightness = 1.0f
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for the current screen brightness
|
||||||
|
*/
|
||||||
|
var brightness: Float
|
||||||
|
get() = window.attributes.screenBrightness
|
||||||
|
private set(value) {
|
||||||
|
val lp = window.attributes
|
||||||
|
lp.screenBrightness = value
|
||||||
|
window.attributes = lp
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore screen brightness to device system brightness.
|
||||||
|
*/
|
||||||
|
fun resetToSystemBrightness() {
|
||||||
|
brightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBrightnessWithScale(value: Float, maxValue: Float, minValue: Float = 0.0f) {
|
||||||
|
brightness = value.normalize(minValue, maxValue, minBrightness, maxBrightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBrightnessWithScale(maxValue: Float, minValue: Float = 0.0f): Float {
|
||||||
|
return brightness.normalize(minBrightness, maxBrightness, minValue, maxValue)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package com.github.libretube.util
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.view.GestureDetector
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import com.github.libretube.ui.interfaces.PlayerGestureOptions
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class PlayerGestureController(context: Context, private val listner: PlayerGestureOptions) :
|
||||||
|
View.OnTouchListener {
|
||||||
|
|
||||||
|
// width and height should be obtained each time using getter to adopt layout size changes.
|
||||||
|
private val width get() = Resources.getSystem().displayMetrics.widthPixels
|
||||||
|
private val height get() = Resources.getSystem().displayMetrics.heightPixels
|
||||||
|
private val orientation get() = Resources.getSystem().configuration.orientation
|
||||||
|
private val elapsedTime get() = SystemClock.elapsedRealtime()
|
||||||
|
|
||||||
|
private val handler: Handler = Handler(Looper.getMainLooper())
|
||||||
|
private val gestureDetector: GestureDetector
|
||||||
|
private var isMoving = false
|
||||||
|
var isEnabled = true
|
||||||
|
|
||||||
|
init {
|
||||||
|
gestureDetector = GestureDetector(context, GestureListener(), handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||||
|
if (event.action == MotionEvent.ACTION_UP && isMoving) {
|
||||||
|
isMoving = false
|
||||||
|
listner.onSwipeEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event can be already consumed by some view which may lead to NPE.
|
||||||
|
try {
|
||||||
|
gestureDetector.onTouchEvent(event)
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
|
// If orientation is landscape then allow `onScroll` to consume event and return true.
|
||||||
|
return orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
|
||||||
|
private var lastClick = 0L
|
||||||
|
private var lastDoubleClick = 0L
|
||||||
|
private var xPos = 0.0F
|
||||||
|
|
||||||
|
override fun onDown(e: MotionEvent): Boolean {
|
||||||
|
if (isMoving) return false
|
||||||
|
|
||||||
|
if (isEnabled && isSecondClick()) {
|
||||||
|
handler.removeCallbacks(runnable)
|
||||||
|
lastDoubleClick = elapsedTime
|
||||||
|
val eventPositionPercentageX = xPos / width
|
||||||
|
|
||||||
|
when {
|
||||||
|
eventPositionPercentageX < 0.4 -> listner.onDoubleTapLeftScreen()
|
||||||
|
eventPositionPercentageX > 0.6 -> listner.onDoubleTapRightScreen()
|
||||||
|
else -> listner.onDoubleTapCenterScreen()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (recentDoubleClick()) return true
|
||||||
|
handler.removeCallbacks(runnable)
|
||||||
|
handler.postDelayed(runnable, MAX_TIME_DIFF)
|
||||||
|
lastClick = elapsedTime
|
||||||
|
xPos = e.x
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onScroll(
|
||||||
|
e1: MotionEvent,
|
||||||
|
e2: MotionEvent,
|
||||||
|
distanceX: Float,
|
||||||
|
distanceY: Float
|
||||||
|
): Boolean {
|
||||||
|
if (!isEnabled) return false
|
||||||
|
|
||||||
|
val insideThreshHold = abs(e2.y - e1.y) <= MOVEMENT_THRESHOLD
|
||||||
|
val insideBorder = (e1.x < BORDER_THRESHOLD || e1.y < BORDER_THRESHOLD || e1.x > width - BORDER_THRESHOLD || e1.y > height - BORDER_THRESHOLD)
|
||||||
|
|
||||||
|
// If the movement is inside threshold or scroll is horizontal then return false
|
||||||
|
if (
|
||||||
|
!isMoving && (
|
||||||
|
insideThreshHold || insideBorder ||
|
||||||
|
abs(distanceX) > abs(
|
||||||
|
distanceY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
isMoving = true
|
||||||
|
|
||||||
|
when {
|
||||||
|
width * 0.5 > e1.x -> listner.onSwipeLeftScreen(distanceY)
|
||||||
|
width * 0.5 < e1.x -> listner.onSwipeRightScreen(distanceY)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val runnable = Runnable {
|
||||||
|
// If user is scrolling then avoid single tap call
|
||||||
|
if (isMoving || isSecondClick()) return@Runnable
|
||||||
|
listner.onSingleTap()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSecondClick(): Boolean {
|
||||||
|
return elapsedTime - lastClick < MAX_TIME_DIFF
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recentDoubleClick(): Boolean {
|
||||||
|
return elapsedTime - lastDoubleClick < MAX_TIME_DIFF / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_TIME_DIFF = 400L
|
||||||
|
private const val MOVEMENT_THRESHOLD = 30
|
||||||
|
private const val BORDER_THRESHOLD = 90
|
||||||
|
}
|
||||||
|
}
|
@ -294,6 +294,12 @@ object PlayerHelper {
|
|||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val swipeGestureEnabled: Boolean
|
||||||
|
get() = PreferenceHelper.getBoolean(
|
||||||
|
PreferenceKeys.PLAYER_SWIPE_CONTROLS,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
fun getDefaultResolution(context: Context): String {
|
fun getDefaultResolution(context: Context): String {
|
||||||
return if (NetworkHelper.isNetworkMobile(context)) {
|
return if (NetworkHelper.isNetworkMobile(context)) {
|
||||||
PreferenceHelper.getString(
|
PreferenceHelper.getString(
|
||||||
|
10
app/src/main/res/drawable/controls_layout_bg.xml
Normal file
10
app/src/main/res/drawable/controls_layout_bg.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#4d000000" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<padding
|
||||||
|
android:bottom="8dp"
|
||||||
|
android:left="8dp"
|
||||||
|
android:right="8dp"
|
||||||
|
android:top="8dp" />
|
||||||
|
</shape>
|
10
app/src/main/res/drawable/ic_brightness.xml
Normal file
10
app/src/main/res/drawable/ic_brightness.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_brightness_auto.xml
Normal file
10
app/src/main/res/drawable/ic_brightness_auto.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M10.85,12.65h2.3L12,9l-1.15,3.65zM20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM14.3,16l-0.7,-2h-3.2l-0.7,2H7.8L11,7h2l3.2,9h-1.9z" />
|
||||||
|
</vector>
|
16
app/src/main/res/drawable/ic_swipe_gesture.xml
Normal file
16
app/src/main/res/drawable/ic_swipe_gesture.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="427"
|
||||||
|
android:viewportHeight="427.97">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="m352.85,188.96h-0.63c-11.65,0 -20.96,9.86 -20.96,22.62v18.05c0,3.87 -3.14,7 -7,7 -3.87,0 -7,-3.13 -7,-7v-33.33c0,-12.76 -9.32,-23.13 -20.97,-23.13 -11.71,-0.01 -21.03,10.38 -21.03,23.13v35.33c0,3.87 -3.14,7 -7,7 -3.87,0 -7,-3.13 -7,-7v-45.03c0,-12.76 -9.98,-22.64 -21.63,-22.64h-0.66c-11.36,0 -20.71,9.36 -20.71,21.67v50.7c0,3.86 -3.14,7 -7,7 -3.87,0 -7,-3.14 -7,-7v-49.23c0,-0.17 -0.02,-0.34 -0.02,-0.51 0,-0.4 0.02,-0.8 0.02,-1.2v-115.11c0,-12.76 -9.68,-23.13 -21.33,-23.13 -11.65,0 -21.32,10.37 -21.34,23.11l-0.13,190.9c0,2.96 -1.86,5.59 -4.64,6.59 -2.79,1 -5.89,0.14 -7.77,-2.15l-22.79,-27.74c-5.93,-7.43 -14.68,-12.06 -24.15,-12.78 -9.27,-0.61 -18.38,2.66 -25.15,9.03 -0.09,0.08 -0.18,0.16 -0.27,0.24l-4.16,3.46 78.84,151.49c12.39,23.81 35.82,38.65 61.14,38.65h91.35c38.61,0 70.05,-33.93 70.09,-75.55 0.02,-22.16 0.04,-38.73 0.06,-52.13 0.05,-35.86 0.06,-49.01 -0.03,-88.51 -0.03,-12.72 -9.51,-22.81 -21.13,-22.81zM352.85,188.96" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="m58.83,47.74c2.73,2.73 7.17,2.73 9.9,0s2.73,-7.16 0,-9.9l-25.71,-25.71c-0.68,-2.57 -2.76,-4.53 -5.36,-5.06 -2.61,-0.53 -5.29,0.46 -6.91,2.57 -0.02,0.02 -0.04,0.04 -0.06,0.05l-28.15,28.15c-2.73,2.73 -2.73,7.17 0,9.9 2.73,2.73 7.16,2.73 9.9,0l16.83,-16.83v173.77l-16.83,-16.83c-2.73,-2.73 -7.17,-2.73 -9.9,0 -2.73,2.73 -2.73,7.17 0,9.9l28.15,28.15c2.73,2.73 7.16,2.73 9.9,0l28.15,-28.15c2.73,-2.73 2.73,-7.16 0,-9.9s-7.16,-2.73 -9.9,0l-15.57,15.57v-171.25zM58.83,47.74" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="m130.59,66.05c0,-28.75 23.3,-52.05 52.05,-52.05 28.75,0 52.05,23.3 52.05,52.05 0,3.86 3.14,7 7,7 3.87,0 7,-3.14 7,-7 0,-36.48 -29.57,-66.05 -66.05,-66.05 -36.48,0 -66.05,29.57 -66.05,66.05 0,3.86 3.13,7 7,7 3.87,0 7,-3.14 7,-7zM130.59,66.05" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_volume_off.xml
Normal file
10
app/src/main/res/drawable/ic_volume_off.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
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.63zM19,12c0,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,9L3,9v6h4l5,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.18L12,4z" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_volume_up.xml
Normal file
10
app/src/main/res/drawable/ic_volume_up.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-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>
|
20
app/src/main/res/drawable/vertical_progressbar.xml
Normal file
20
app/src/main/res/drawable/vertical_progressbar.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#40ffffff" />
|
||||||
|
<corners android:radius="20dip" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item android:id="@android:id/progress">
|
||||||
|
<scale
|
||||||
|
android:scaleWidth="0%"
|
||||||
|
android:scaleHeight="100%"
|
||||||
|
android:scaleGravity="bottom">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="?attr/colorPrimary" />
|
||||||
|
<corners android:radius="20dip" />
|
||||||
|
</shape>
|
||||||
|
</scale>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
@ -19,6 +19,13 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center" />
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<com.github.libretube.ui.views.PlayerGestureControlsView
|
||||||
|
android:id="@+id/playerGestureControlsView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
</com.github.libretube.ui.views.CustomExoPlayerView>
|
</com.github.libretube.ui.views.CustomExoPlayerView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -397,6 +397,13 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center" />
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<com.github.libretube.ui.views.PlayerGestureControlsView
|
||||||
|
android:id="@+id/playerGestureControlsView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
83
app/src/main/res/layout/player_gesture_controls_view.xml
Normal file
83
app/src/main/res/layout/player_gesture_controls_view.xml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/volumeControlView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="36dp"
|
||||||
|
android:background="@drawable/controls_layout_bg"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/volume_textView"
|
||||||
|
style="@style/SwipeControlString"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/volume_progressBar"
|
||||||
|
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="7dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_marginVertical="4dp"
|
||||||
|
android:progressDrawable="@drawable/vertical_progressbar" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/volume_imageView"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:contentDescription="@string/volume"
|
||||||
|
android:src="@drawable/ic_volume_up" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/brightnessControlView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginRight="36dp"
|
||||||
|
android:background="@drawable/controls_layout_bg"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/brightness_textView"
|
||||||
|
style="@style/SwipeControlString"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/brightness_progressBar"
|
||||||
|
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="7dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_marginVertical="4dp"
|
||||||
|
android:progressDrawable="@drawable/vertical_progressbar" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/brightness_imageView"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:contentDescription="@string/brightness"
|
||||||
|
android:src="@drawable/ic_brightness" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -389,6 +389,11 @@
|
|||||||
<string name="local_playlists">Local playlists</string>
|
<string name="local_playlists">Local playlists</string>
|
||||||
<string name="not_enabled">Menu item not enabled!</string>
|
<string name="not_enabled">Menu item not enabled!</string>
|
||||||
<string name="select_other_start_tab">Please select an other start tab first!</string>
|
<string name="select_other_start_tab">Please select an other start tab first!</string>
|
||||||
|
<string name="brightness">Brightness</string>
|
||||||
|
<string name="volume">Volume</string>
|
||||||
|
<string name="auto">Auto</string>
|
||||||
|
<string name="swipe_controls">Swipe controls</string>
|
||||||
|
<string name="swipe_controls_summary">Use swipe gesture to adjust the brightness and volume.</string>
|
||||||
|
|
||||||
<!-- Notification channel strings -->
|
<!-- Notification channel strings -->
|
||||||
<string name="download_channel_name">Download Service</string>
|
<string name="download_channel_name">Download Service</string>
|
||||||
|
@ -149,6 +149,18 @@
|
|||||||
<item name="android:textColor">@android:color/white</item>
|
<item name="android:textColor">@android:color/white</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="SwipeControlString">
|
||||||
|
<item name="android:gravity">center</item>
|
||||||
|
<item name="android:layout_gravity">center_vertical</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="paddingEnd">5dp</item>
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:textStyle">normal</item>
|
||||||
|
<item name="android:textColor">@android:color/white</item>
|
||||||
|
<item name="android:text">0</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="Chip" parent="Widget.Material3.Chip.Suggestion">
|
<style name="Chip" parent="Widget.Material3.Chip.Suggestion">
|
||||||
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
@ -85,6 +85,13 @@
|
|||||||
app:key="picture_in_picture"
|
app:key="picture_in_picture"
|
||||||
app:title="@string/picture_in_picture" />
|
app:title="@string/picture_in_picture" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:icon="@drawable/ic_swipe_gesture"
|
||||||
|
android:summary="@string/swipe_controls_summary"
|
||||||
|
app:key="player_swipe_controls"
|
||||||
|
app:title="@string/swipe_controls" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:icon="@drawable/ic_pause_filled"
|
android:icon="@drawable/ic_pause_filled"
|
||||||
android:summary="@string/pauseOnScreenOff_summary"
|
android:summary="@string/pauseOnScreenOff_summary"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user