2022-04-08 09:35:14 +02:00
|
|
|
package org.schabi.newpipe.player.gesture
|
|
|
|
|
|
|
|
import android.util.Log
|
|
|
|
import android.view.MotionEvent
|
|
|
|
import android.view.View
|
|
|
|
import android.view.View.OnTouchListener
|
|
|
|
import android.widget.ProgressBar
|
2022-04-09 10:48:34 +02:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2022-04-08 09:35:14 +02:00
|
|
|
import androidx.appcompat.content.res.AppCompatResources
|
2022-08-08 03:40:16 +02:00
|
|
|
import androidx.core.math.MathUtils
|
2022-07-07 11:09:07 +02:00
|
|
|
import androidx.core.view.isVisible
|
2022-04-08 09:35:14 +02:00
|
|
|
import org.schabi.newpipe.MainActivity
|
|
|
|
import org.schabi.newpipe.R
|
|
|
|
import org.schabi.newpipe.ktx.AnimationType
|
|
|
|
import org.schabi.newpipe.ktx.animate
|
|
|
|
import org.schabi.newpipe.player.Player
|
2022-07-07 11:09:07 +02:00
|
|
|
import org.schabi.newpipe.player.helper.AudioReactor
|
2022-04-08 09:35:14 +02:00
|
|
|
import org.schabi.newpipe.player.helper.PlayerHelper
|
|
|
|
import org.schabi.newpipe.player.ui.MainPlayerUi
|
2022-07-07 11:59:00 +02:00
|
|
|
import org.schabi.newpipe.util.ThemeHelper.getAndroidDimenPx
|
2022-04-08 09:35:14 +02:00
|
|
|
import kotlin.math.abs
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GestureListener for the player
|
|
|
|
*
|
|
|
|
* While [BasePlayerGestureListener] contains the logic behind the single gestures
|
|
|
|
* this class focuses on the visual aspect like hiding and showing the controls or changing
|
|
|
|
* volume/brightness during scrolling for specific events.
|
|
|
|
*/
|
|
|
|
class MainPlayerGestureListener(
|
|
|
|
private val playerUi: MainPlayerUi
|
|
|
|
) : BasePlayerGestureListener(playerUi), OnTouchListener {
|
|
|
|
private var isMoving = false
|
|
|
|
|
|
|
|
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
|
|
|
super.onTouch(v, event)
|
|
|
|
if (event.action == MotionEvent.ACTION_UP && isMoving) {
|
|
|
|
isMoving = false
|
|
|
|
onScrollEnd(event)
|
|
|
|
}
|
|
|
|
return when (event.action) {
|
|
|
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
|
2022-04-09 10:48:34 +02:00
|
|
|
v.parent?.requestDisallowInterceptTouchEvent(playerUi.isFullscreen)
|
2022-04-08 09:35:14 +02:00
|
|
|
true
|
|
|
|
}
|
|
|
|
MotionEvent.ACTION_UP -> {
|
2022-04-09 10:48:34 +02:00
|
|
|
v.parent?.requestDisallowInterceptTouchEvent(false)
|
2022-04-08 09:35:14 +02:00
|
|
|
false
|
|
|
|
}
|
|
|
|
else -> true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
|
|
|
if (DEBUG)
|
|
|
|
Log.d(TAG, "onSingleTapConfirmed() called with: e = [$e]")
|
|
|
|
|
|
|
|
if (isDoubleTapping)
|
|
|
|
return true
|
|
|
|
super.onSingleTapConfirmed(e)
|
|
|
|
|
|
|
|
if (player.currentState != Player.STATE_BLOCKED)
|
|
|
|
onSingleTap()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onScrollVolume(distanceY: Float) {
|
2022-07-07 11:09:07 +02:00
|
|
|
val bar: ProgressBar = binding.volumeProgressBar
|
|
|
|
val audioReactor: AudioReactor = player.audioReactor
|
|
|
|
|
2022-04-08 09:35:14 +02:00
|
|
|
// If we just started sliding, change the progress bar to match the system volume
|
2022-07-07 11:09:07 +02:00
|
|
|
if (!binding.volumeRelativeLayout.isVisible) {
|
|
|
|
val volumePercent: Float = audioReactor.volume / audioReactor.maxVolume.toFloat()
|
|
|
|
bar.progress = (volumePercent * bar.max).toInt()
|
2022-04-08 09:35:14 +02:00
|
|
|
}
|
|
|
|
|
2022-07-07 11:09:07 +02:00
|
|
|
// Update progress bar
|
2022-04-08 09:35:14 +02:00
|
|
|
binding.volumeProgressBar.incrementProgressBy(distanceY.toInt())
|
2022-07-07 11:09:07 +02:00
|
|
|
|
|
|
|
// Update volume
|
|
|
|
val currentProgressPercent: Float = bar.progress / bar.max.toFloat()
|
|
|
|
val currentVolume = (audioReactor.maxVolume * currentProgressPercent).toInt()
|
|
|
|
audioReactor.volume = currentVolume
|
2022-04-08 09:35:14 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "onScroll().volumeControl, currentVolume = $currentVolume")
|
|
|
|
}
|
|
|
|
|
2022-07-07 11:09:07 +02:00
|
|
|
// Update player center image
|
2022-04-08 09:35:14 +02:00
|
|
|
binding.volumeImageView.setImageDrawable(
|
|
|
|
AppCompatResources.getDrawable(
|
|
|
|
player.context,
|
|
|
|
when {
|
|
|
|
currentProgressPercent <= 0 -> R.drawable.ic_volume_off
|
|
|
|
currentProgressPercent < 0.25 -> R.drawable.ic_volume_mute
|
|
|
|
currentProgressPercent < 0.75 -> R.drawable.ic_volume_down
|
|
|
|
else -> R.drawable.ic_volume_up
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2022-07-07 11:09:07 +02:00
|
|
|
// Make sure the correct layout is visible
|
|
|
|
if (!binding.volumeRelativeLayout.isVisible) {
|
2022-04-08 09:35:14 +02:00
|
|
|
binding.volumeRelativeLayout.animate(true, 200, AnimationType.SCALE_AND_ALPHA)
|
|
|
|
}
|
2022-07-07 11:09:07 +02:00
|
|
|
binding.brightnessRelativeLayout.isVisible = false
|
2022-04-08 09:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun onScrollBrightness(distanceY: Float) {
|
2022-04-09 10:48:34 +02:00
|
|
|
val parent: AppCompatActivity = playerUi.parentActivity.orElse(null) ?: return
|
2022-04-08 09:35:14 +02:00
|
|
|
val window = parent.window
|
|
|
|
val layoutParams = window.attributes
|
|
|
|
val bar: ProgressBar = binding.brightnessProgressBar
|
2022-07-07 11:09:07 +02:00
|
|
|
|
|
|
|
// Update progress bar
|
2022-04-08 09:35:14 +02:00
|
|
|
val oldBrightness = layoutParams.screenBrightness
|
2022-08-08 03:40:16 +02:00
|
|
|
bar.progress = (bar.max * MathUtils.clamp(oldBrightness, 0f, 1f)).toInt()
|
2022-04-08 09:35:14 +02:00
|
|
|
bar.incrementProgressBy(distanceY.toInt())
|
2022-07-07 11:09:07 +02:00
|
|
|
|
|
|
|
// Update brightness
|
2022-04-08 09:35:14 +02:00
|
|
|
val currentProgressPercent = bar.progress.toFloat() / bar.max
|
|
|
|
layoutParams.screenBrightness = currentProgressPercent
|
|
|
|
window.attributes = layoutParams
|
|
|
|
|
|
|
|
// Save current brightness level
|
|
|
|
PlayerHelper.setScreenBrightness(parent, currentProgressPercent)
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(
|
|
|
|
TAG,
|
|
|
|
"onScroll().brightnessControl, " +
|
|
|
|
"currentBrightness = " + currentProgressPercent
|
|
|
|
)
|
|
|
|
}
|
2022-07-07 11:09:07 +02:00
|
|
|
|
|
|
|
// Update player center image
|
2022-04-08 09:35:14 +02:00
|
|
|
binding.brightnessImageView.setImageDrawable(
|
|
|
|
AppCompatResources.getDrawable(
|
|
|
|
player.context,
|
2022-07-07 11:09:07 +02:00
|
|
|
when {
|
|
|
|
currentProgressPercent < 0.25 -> R.drawable.ic_brightness_low
|
|
|
|
currentProgressPercent < 0.75 -> R.drawable.ic_brightness_medium
|
|
|
|
else -> R.drawable.ic_brightness_high
|
|
|
|
}
|
2022-04-08 09:35:14 +02:00
|
|
|
)
|
|
|
|
)
|
2022-07-07 11:09:07 +02:00
|
|
|
|
|
|
|
// Make sure the correct layout is visible
|
|
|
|
if (!binding.brightnessRelativeLayout.isVisible) {
|
2022-04-08 09:35:14 +02:00
|
|
|
binding.brightnessRelativeLayout.animate(true, 200, AnimationType.SCALE_AND_ALPHA)
|
|
|
|
}
|
2022-07-07 11:09:07 +02:00
|
|
|
binding.volumeRelativeLayout.isVisible = false
|
2022-04-08 09:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onScrollEnd(event: MotionEvent) {
|
|
|
|
super.onScrollEnd(event)
|
2022-07-07 11:09:07 +02:00
|
|
|
if (binding.volumeRelativeLayout.isVisible) {
|
2022-04-08 09:35:14 +02:00
|
|
|
binding.volumeRelativeLayout.animate(false, 200, AnimationType.SCALE_AND_ALPHA, 200)
|
|
|
|
}
|
2022-07-07 11:09:07 +02:00
|
|
|
if (binding.brightnessRelativeLayout.isVisible) {
|
2022-04-08 09:35:14 +02:00
|
|
|
binding.brightnessRelativeLayout.animate(false, 200, AnimationType.SCALE_AND_ALPHA, 200)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onScroll(
|
|
|
|
initialEvent: MotionEvent,
|
|
|
|
movingEvent: MotionEvent,
|
|
|
|
distanceX: Float,
|
|
|
|
distanceY: Float
|
|
|
|
): Boolean {
|
|
|
|
|
|
|
|
if (!playerUi.isFullscreen) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-07-07 11:59:00 +02:00
|
|
|
// Calculate heights of status and navigation bars
|
|
|
|
val statusBarHeight = getAndroidDimenPx(player.context, "status_bar_height")
|
|
|
|
val navigationBarHeight = getAndroidDimenPx(player.context, "navigation_bar_height")
|
|
|
|
|
|
|
|
// Do not handle this event if initially it started from status or navigation bars
|
|
|
|
val isTouchingStatusBar = initialEvent.y < statusBarHeight
|
|
|
|
val isTouchingNavigationBar = initialEvent.y > (binding.root.height - navigationBarHeight)
|
2022-04-08 09:35:14 +02:00
|
|
|
if (isTouchingStatusBar || isTouchingNavigationBar) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
val insideThreshold = abs(movingEvent.y - initialEvent.y) <= MOVEMENT_THRESHOLD
|
|
|
|
if (
|
|
|
|
!isMoving && (insideThreshold || abs(distanceX) > abs(distanceY)) ||
|
|
|
|
player.currentState == Player.STATE_COMPLETED
|
|
|
|
) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
isMoving = true
|
|
|
|
|
|
|
|
// -- Brightness and Volume control --
|
2023-01-29 19:23:39 +01:00
|
|
|
if (getDisplayHalfPortion(initialEvent) == DisplayPortion.RIGHT_HALF) {
|
2023-02-07 19:08:47 +01:00
|
|
|
when (PlayerHelper.getActionForRightGestureSide(player.context)) {
|
2023-02-07 18:10:18 +01:00
|
|
|
player.context.getString(R.string.volume_control_key) ->
|
2023-01-29 19:23:39 +01:00
|
|
|
onScrollVolume(distanceY)
|
2023-02-07 18:10:18 +01:00
|
|
|
player.context.getString(R.string.brightness_control_key) ->
|
2023-01-29 19:23:39 +01:00
|
|
|
onScrollBrightness(distanceY)
|
|
|
|
}
|
|
|
|
} else {
|
2023-02-07 19:08:47 +01:00
|
|
|
when (PlayerHelper.getActionForLeftGestureSide(player.context)) {
|
2023-02-07 18:10:18 +01:00
|
|
|
player.context.getString(R.string.volume_control_key) ->
|
2023-01-29 19:23:39 +01:00
|
|
|
onScrollVolume(distanceY)
|
2023-02-07 18:10:18 +01:00
|
|
|
player.context.getString(R.string.brightness_control_key) ->
|
2023-01-29 19:23:39 +01:00
|
|
|
onScrollBrightness(distanceY)
|
2022-04-08 09:35:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun getDisplayPortion(e: MotionEvent): DisplayPortion {
|
|
|
|
return when {
|
|
|
|
e.x < binding.root.width / 3.0 -> DisplayPortion.LEFT
|
|
|
|
e.x > binding.root.width * 2.0 / 3.0 -> DisplayPortion.RIGHT
|
|
|
|
else -> DisplayPortion.MIDDLE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun getDisplayHalfPortion(e: MotionEvent): DisplayPortion {
|
|
|
|
return when {
|
|
|
|
e.x < binding.root.width / 2.0 -> DisplayPortion.LEFT_HALF
|
|
|
|
else -> DisplayPortion.RIGHT_HALF
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
private val TAG = MainPlayerGestureListener::class.java.simpleName
|
|
|
|
private val DEBUG = MainActivity.DEBUG
|
|
|
|
private const val MOVEMENT_THRESHOLD = 40
|
|
|
|
}
|
|
|
|
}
|