diff --git a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt
index b28968f45..a7dc511db 100644
--- a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt
+++ b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt
@@ -83,6 +83,7 @@ object PreferenceKeys {
const val ALTERNATIVE_PLAYER_LAYOUT = "alternative_player_layout"
const val USE_HLS_OVER_DASH = "use_hls"
const val QUEUE_AUTO_INSERT_RELATED = "queue_insert_related_videos"
+ const val PLAYER_SWIPE_CONTROLS = "player_swipe_controls"
/**
* Background mode
diff --git a/app/src/main/java/com/github/libretube/extensions/Normalize.kt b/app/src/main/java/com/github/libretube/extensions/Normalize.kt
new file mode 100644
index 000000000..0550a62c0
--- /dev/null
+++ b/app/src/main/java/com/github/libretube/extensions/Normalize.kt
@@ -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
+}
diff --git a/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt
index 4f6d26638..1bdd22255 100644
--- a/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt
+++ b/app/src/main/java/com/github/libretube/ui/activities/OfflinePlayerActivity.kt
@@ -70,6 +70,7 @@ class OfflinePlayerActivity : BaseActivity() {
binding.player.initialize(
null,
binding.doubleTapOverlay.binding,
+ binding.playerGestureControlsView.binding,
null
)
}
diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt
index 9d5fc24fc..d0c224963 100644
--- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt
+++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt
@@ -49,6 +49,7 @@ import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.DoubleTapOverlayBinding
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
import com.github.libretube.databinding.FragmentPlayerBinding
+import com.github.libretube.databinding.PlayerGestureControlsViewBinding
import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.DatabaseHolder.Companion.Database
import com.github.libretube.db.obj.WatchPosition
@@ -118,6 +119,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
lateinit var binding: FragmentPlayerBinding
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
private lateinit var doubleTapOverlayBinding: DoubleTapOverlayBinding
+ private lateinit var playerGestureControlsViewBinding: PlayerGestureControlsViewBinding
private val viewModel: PlayerViewModel by activityViewModels()
/**
@@ -183,6 +185,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
exoPlayerView = binding.player
playerBinding = binding.player.binding
doubleTapOverlayBinding = binding.doubleTapOverlay.binding
+ playerGestureControlsViewBinding = binding.playerGestureControlsView.binding
// Inflate the layout for this fragment
return binding.root
@@ -775,6 +778,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
binding.player.initialize(
this,
doubleTapOverlayBinding,
+ playerGestureControlsViewBinding,
trackSelector
)
diff --git a/app/src/main/java/com/github/libretube/ui/interfaces/DoubleTapListener.kt b/app/src/main/java/com/github/libretube/ui/interfaces/DoubleTapListener.kt
deleted file mode 100644
index 24fd9b1eb..000000000
--- a/app/src/main/java/com/github/libretube/ui/interfaces/DoubleTapListener.kt
+++ /dev/null
@@ -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
- }
-}
diff --git a/app/src/main/java/com/github/libretube/ui/interfaces/PlayerGestureOptions.kt b/app/src/main/java/com/github/libretube/ui/interfaces/PlayerGestureOptions.kt
new file mode 100644
index 000000000..ee6e60dd1
--- /dev/null
+++ b/app/src/main/java/com/github/libretube/ui/interfaces/PlayerGestureOptions.kt
@@ -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()
+}
diff --git a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt
index dd51ca16f..c46ad917a 100644
--- a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt
+++ b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt
@@ -1,6 +1,7 @@
package com.github.libretube.ui.views
import android.annotation.SuppressLint
+import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.os.Handler
@@ -8,18 +9,24 @@ import android.os.Looper
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
+import android.view.WindowManager
import com.github.libretube.R
import com.github.libretube.databinding.DoubleTapOverlayBinding
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.obj.BottomSheetItem
import com.github.libretube.ui.activities.MainActivity
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.PlayerGestureOptions
import com.github.libretube.ui.interfaces.PlayerOptions
import com.github.libretube.ui.sheets.BaseBottomSheet
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.PlayingQueue
import com.google.android.exoplayer2.PlaybackParameters
@@ -33,8 +40,16 @@ import com.google.android.exoplayer2.util.RepeatModeUtil
internal class CustomExoPlayerView(
context: Context,
attributeSet: AttributeSet? = null
-) : StyledPlayerView(context, attributeSet), PlayerOptions {
+) : StyledPlayerView(context, attributeSet), PlayerOptions, PlayerGestureOptions {
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
/**
@@ -45,16 +60,12 @@ internal class CustomExoPlayerView(
private val runnableHandler = Handler(Looper.getMainLooper())
- // the x-position of where the user clicked
- private var xPos = 0F
-
var isPlayerLocked: Boolean = false
/**
* Preferences
*/
var autoplayEnabled = PlayerHelper.autoPlayEnabled
- private var doubleTapAllowed = true
private var resizeModePref = PlayerHelper.resizeModePref
@@ -65,42 +76,23 @@ internal class CustomExoPlayerView(
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(
playerViewInterface: OnlinePlayerOptions?,
doubleTapOverlayBinding: DoubleTapOverlayBinding,
+ playerGestureControlsViewBinding: PlayerGestureControlsViewBinding,
trackSelector: TrackSelector?
) {
this.playerOptionsInterface = playerViewInterface
this.doubleTapOverlayBinding = doubleTapOverlayBinding
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
- setOnClickListener(doubleTouchListener)
-
+ // Set touch listner for tap and swipe gestures.
+ setOnTouchListener(playerGestureController)
+ initializeGestureProgress()
enableDoubleTapToSeek()
initializeAdvancedOptions(context)
@@ -144,10 +136,6 @@ internal class CustomExoPlayerView(
}
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
}
@@ -261,8 +249,8 @@ internal class CustomExoPlayerView(
binding.exoBottomBar.visibility = visibility
binding.closeImageButton.visibility = visibility
- // disable double tap to seek if the player is locked
- doubleTapAllowed = !isLocked
+ // disable tap and swipe gesture if the player is locked
+ playerGestureController.isEnabled = isLocked
}
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() {
// autoplay options dialog
BaseBottomSheet()
@@ -408,4 +446,47 @@ internal class CustomExoPlayerView(
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
+ }
}
diff --git a/app/src/main/java/com/github/libretube/ui/views/PlayerGestureControlsView.kt b/app/src/main/java/com/github/libretube/ui/views/PlayerGestureControlsView.kt
new file mode 100644
index 000000000..8e3ac3fd7
--- /dev/null
+++ b/app/src/main/java/com/github/libretube/ui/views/PlayerGestureControlsView.kt
@@ -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()
+ }
+}
diff --git a/app/src/main/java/com/github/libretube/util/AudioHelper.kt b/app/src/main/java/com/github/libretube/util/AudioHelper.kt
new file mode 100644
index 000000000..603933cd5
--- /dev/null
+++ b/app/src/main/java/com/github/libretube/util/AudioHelper.kt
@@ -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)
+ }
+}
diff --git a/app/src/main/java/com/github/libretube/util/BrightnessHelper.kt b/app/src/main/java/com/github/libretube/util/BrightnessHelper.kt
new file mode 100644
index 000000000..e03581191
--- /dev/null
+++ b/app/src/main/java/com/github/libretube/util/BrightnessHelper.kt
@@ -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)
+ }
+}
diff --git a/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt b/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt
new file mode 100644
index 000000000..d647306b8
--- /dev/null
+++ b/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt
@@ -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
+ }
+}
diff --git a/app/src/main/java/com/github/libretube/util/PlayerHelper.kt b/app/src/main/java/com/github/libretube/util/PlayerHelper.kt
index 346bb854d..177665efc 100644
--- a/app/src/main/java/com/github/libretube/util/PlayerHelper.kt
+++ b/app/src/main/java/com/github/libretube/util/PlayerHelper.kt
@@ -294,6 +294,12 @@ object PlayerHelper {
true
)
+ val swipeGestureEnabled: Boolean
+ get() = PreferenceHelper.getBoolean(
+ PreferenceKeys.PLAYER_SWIPE_CONTROLS,
+ true
+ )
+
fun getDefaultResolution(context: Context): String {
return if (NetworkHelper.isNetworkMobile(context)) {
PreferenceHelper.getString(
diff --git a/app/src/main/res/drawable/controls_layout_bg.xml b/app/src/main/res/drawable/controls_layout_bg.xml
new file mode 100644
index 000000000..9e7c439b7
--- /dev/null
+++ b/app/src/main/res/drawable/controls_layout_bg.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_brightness.xml b/app/src/main/res/drawable/ic_brightness.xml
new file mode 100644
index 000000000..ecf23dfb9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_brightness.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_brightness_auto.xml b/app/src/main/res/drawable/ic_brightness_auto.xml
new file mode 100644
index 000000000..aa74c4915
--- /dev/null
+++ b/app/src/main/res/drawable/ic_brightness_auto.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_swipe_gesture.xml b/app/src/main/res/drawable/ic_swipe_gesture.xml
new file mode 100644
index 000000000..f10107b2e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_swipe_gesture.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_volume_off.xml b/app/src/main/res/drawable/ic_volume_off.xml
new file mode 100644
index 000000000..767b08878
--- /dev/null
+++ b/app/src/main/res/drawable/ic_volume_off.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_volume_up.xml b/app/src/main/res/drawable/ic_volume_up.xml
new file mode 100644
index 000000000..cdd4aac9e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_volume_up.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/vertical_progressbar.xml b/app/src/main/res/drawable/vertical_progressbar.xml
new file mode 100644
index 000000000..c4350b1f8
--- /dev/null
+++ b/app/src/main/res/drawable/vertical_progressbar.xml
@@ -0,0 +1,20 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_offline_player.xml b/app/src/main/res/layout/activity_offline_player.xml
index f7c5345ec..963c69bf7 100644
--- a/app/src/main/res/layout/activity_offline_player.xml
+++ b/app/src/main/res/layout/activity_offline_player.xml
@@ -19,6 +19,13 @@
android:layout_gravity="center"
android:gravity="center" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml
index 775e8af8c..3c2db25e5 100644
--- a/app/src/main/res/layout/fragment_player.xml
+++ b/app/src/main/res/layout/fragment_player.xml
@@ -397,6 +397,13 @@
android:layout_gravity="center"
android:gravity="center" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 771ca0c8c..55d17262c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -389,6 +389,11 @@
Local playlists
Menu item not enabled!
Please select an other start tab first!
+ Brightness
+ Volume
+ Auto
+ Swipe controls
+ Use swipe gesture to adjust the brightness and volume.
Download Service
diff --git a/app/src/main/res/values/style.xml b/app/src/main/res/values/style.xml
index c4c8be643..72cbd129c 100644
--- a/app/src/main/res/values/style.xml
+++ b/app/src/main/res/values/style.xml
@@ -149,6 +149,18 @@
- @android:color/white
+
+