Skip to content

Commit

Permalink
feat: disable swipe-controls when player controls are visible (ReVa…
Browse files Browse the repository at this point in the history
  • Loading branch information
shadow578 committed Sep 20, 2022
1 parent 2b76337 commit 6d8c0a0
Show file tree
Hide file tree
Showing 10 changed files with 640 additions and 269 deletions.
@@ -0,0 +1,84 @@
package app.revanced.integrations.shared

import android.app.Activity
import android.view.View
import android.view.ViewGroup
import app.revanced.integrations.utils.ReVancedUtils
import java.lang.ref.WeakReference

/**
* default implementation of [PlayerControlsVisibilityObserver]
*
* @param activity activity that contains the controls_layout view
*/
class PlayerControlsVisibilityObserverImpl(
private val activity: Activity
) : PlayerControlsVisibilityObserver {

/**
* id of the direct parent of controls_layout, R.id.youtube_controls_overlay
*/
private val controlsLayoutParentId =
ReVancedUtils.getResourceIdByName(activity, "id", "youtube_controls_overlay")

/**
* id of R.id.controls_layout
*/
private val controlsLayoutId =
ReVancedUtils.getResourceIdByName(activity, "id", "controls_layout")

/**
* reference to the controls layout view
*/
private var controlsLayoutView = WeakReference<View>(null)

/**
* is the [controlsLayoutView] set to a valid reference of a view?
*/
private val isAttached: Boolean
get() {
val view = controlsLayoutView.get()
return view != null && view.parent != null
}

/**
* find and attach the controls_layout view if needed
*/
private fun maybeAttach() {
if (isAttached) return

// find parent, then controls_layout view
// this is needed because there may be two views where id=R.id.controls_layout
// because why should google confine themselves to their own guidelines...
activity.findViewById<ViewGroup>(controlsLayoutParentId)?.let { parent ->
parent.findViewById<View>(controlsLayoutId)?.let {
controlsLayoutView = WeakReference(it)
}
}
}

override val playerControlsVisibility: Int
get() {
maybeAttach()
return controlsLayoutView.get()?.visibility ?: View.GONE
}

override val arePlayerControlsVisible: Boolean
get() = playerControlsVisibility == View.VISIBLE
}

/**
* provides the visibility status of the fullscreen player controls_layout view.
* this can be used for detecting when the player controls are shown
*/
interface PlayerControlsVisibilityObserver {
/**
* current visibility int of the controls_layout view
*/
val playerControlsVisibility: Int

/**
* is the value of [playerControlsVisibility] equal to [View.VISIBLE]?
*/
val arePlayerControlsVisible: Boolean
}
Expand Up @@ -10,8 +10,9 @@ import app.revanced.integrations.swipecontrols.controller.AudioVolumeController
import app.revanced.integrations.swipecontrols.controller.ScreenBrightnessController
import app.revanced.integrations.swipecontrols.controller.SwipeZonesController
import app.revanced.integrations.swipecontrols.controller.VolumeKeysController
import app.revanced.integrations.swipecontrols.controller.gesture.NoPtSSwipeGestureController
import app.revanced.integrations.swipecontrols.controller.gesture.SwipeGestureController
import app.revanced.integrations.swipecontrols.controller.gesture.ClassicSwipeController
import app.revanced.integrations.swipecontrols.controller.gesture.PressToSwipeController
import app.revanced.integrations.swipecontrols.controller.gesture.core.GestureController
import app.revanced.integrations.swipecontrols.misc.Rectangle
import app.revanced.integrations.swipecontrols.views.SwipeControlsOverlayLayout
import app.revanced.integrations.utils.LogHelper
Expand Down Expand Up @@ -52,7 +53,7 @@ class SwipeControlsHostActivity : Activity() {
/**
* main gesture controller
*/
private lateinit var gesture: SwipeGestureController
private lateinit var gesture: GestureController

/**
* main volume keys controller
Expand All @@ -71,13 +72,12 @@ class SwipeControlsHostActivity : Activity() {
// create controllers
LogHelper.info(this.javaClass, "initializing swipe controls controllers")
config = SwipeControlsConfigurationProvider(this)
gesture = createGestureController()
keys = VolumeKeysController(this)
audio = createAudioController()
screen = createScreenController()

// create overlay
SwipeControlsOverlayLayout(this).let {
SwipeControlsOverlayLayout(this, config).let {
overlay = it
contentRoot.addView(it)
}
Expand All @@ -92,6 +92,9 @@ class SwipeControlsHostActivity : Activity() {
)
}

// create the gesture controller
gesture = createGestureController()

// listen for changes in the player type
PlayerType.onChange += this::onPlayerTypeChanged

Expand All @@ -109,13 +112,13 @@ class SwipeControlsHostActivity : Activity() {
}

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
return if ((ev != null) && gesture.onTouchEvent(ev)) true else {
return if ((ev != null) && gesture.submitTouchEvent(ev)) true else {
super.dispatchTouchEvent(ev)
}
}

override fun dispatchKeyEvent(ev: KeyEvent?): Boolean {
return if((ev != null) && keys.onKeyEvent(ev)) true else {
return if ((ev != null) && keys.onKeyEvent(ev)) true else {
super.dispatchKeyEvent(ev)
}
}
Expand Down Expand Up @@ -163,8 +166,8 @@ class SwipeControlsHostActivity : Activity() {
*/
private fun createGestureController() =
if (config.shouldEnablePressToSwipe)
SwipeGestureController(this)
else NoPtSSwipeGestureController(this)
PressToSwipeController(this)
else ClassicSwipeController(this)

companion object {
/**
Expand Down
@@ -0,0 +1,111 @@
package app.revanced.integrations.swipecontrols.controller.gesture

import android.view.MotionEvent
import app.revanced.integrations.shared.PlayerControlsVisibilityObserver
import app.revanced.integrations.shared.PlayerControlsVisibilityObserverImpl
import app.revanced.integrations.swipecontrols.SwipeControlsHostActivity
import app.revanced.integrations.swipecontrols.controller.gesture.core.BaseGestureController
import app.revanced.integrations.swipecontrols.controller.gesture.core.SwipeDetector
import app.revanced.integrations.swipecontrols.misc.contains
import app.revanced.integrations.swipecontrols.misc.toPoint

/**
* provides the classic swipe controls experience, as it was with 'XFenster'
*
* @param controller reference to the main swipe controller
*/
class ClassicSwipeController(
private val controller: SwipeControlsHostActivity
) : BaseGestureController(controller),
PlayerControlsVisibilityObserver by PlayerControlsVisibilityObserverImpl(controller) {
/**
* the last event captured in [onDown]
*/
private var lastOnDownEvent: MotionEvent? = null

override val shouldForceInterceptEvents: Boolean
get() = currentSwipe == SwipeDetector.SwipeDirection.VERTICAL

override fun isInSwipeZone(motionEvent: MotionEvent): Boolean {
val inVolumeZone = if (controller.config.enableVolumeControls)
(motionEvent.toPoint() in controller.zones.volume) else false
val inBrightnessZone = if (controller.config.enableBrightnessControl)
(motionEvent.toPoint() in controller.zones.brightness) else false

return inVolumeZone || inBrightnessZone
}

override fun shouldDropMotion(motionEvent: MotionEvent): Boolean {
// ignore gestures with more than one pointer
// when such a gesture is detected, dispatch the first event of the gesture to downstream
if (motionEvent.pointerCount > 1) {
lastOnDownEvent?.let {
controller.dispatchDownstreamTouchEvent(it)
it.recycle()
}
lastOnDownEvent = null
return true
}

// ignore gestures when player controls are visible
return arePlayerControlsVisible
}

override fun onDown(motionEvent: MotionEvent): Boolean {
// save the event for later
lastOnDownEvent?.recycle()
lastOnDownEvent = MotionEvent.obtain(motionEvent)

// must be inside swipe zone
return isInSwipeZone(motionEvent)
}

override fun onSingleTapUp(motionEvent: MotionEvent): Boolean {
MotionEvent.obtain(motionEvent).let {
it.action = MotionEvent.ACTION_DOWN
controller.dispatchDownstreamTouchEvent(it)
it.recycle()
}

return false
}

override fun onDoubleTapEvent(motionEvent: MotionEvent?): Boolean {
MotionEvent.obtain(motionEvent).let {
controller.dispatchDownstreamTouchEvent(it)
it.recycle()
}

return super.onDoubleTapEvent(motionEvent)
}

override fun onLongPress(motionEvent: MotionEvent?) {
MotionEvent.obtain(motionEvent).let {
controller.dispatchDownstreamTouchEvent(it)
it.recycle()
}

super.onLongPress(motionEvent)
}

override fun onSwipe(
from: MotionEvent,
to: MotionEvent,
distanceX: Double,
distanceY: Double
): Boolean {
// cancel if not vertical
if (currentSwipe != SwipeDetector.SwipeDirection.VERTICAL) return false
return when (from.toPoint()) {
in controller.zones.volume -> {
scrollVolume(distanceY)
true
}
in controller.zones.brightness -> {
scrollBrightness(distanceY)
true
}
else -> false
}
}
}

This file was deleted.

@@ -0,0 +1,72 @@
package app.revanced.integrations.swipecontrols.controller.gesture

import android.view.MotionEvent
import app.revanced.integrations.swipecontrols.SwipeControlsHostActivity
import app.revanced.integrations.swipecontrols.controller.gesture.core.BaseGestureController
import app.revanced.integrations.swipecontrols.controller.gesture.core.SwipeDetector
import app.revanced.integrations.swipecontrols.misc.contains
import app.revanced.integrations.swipecontrols.misc.toPoint

/**
* provides the press-to-swipe (PtS) swipe controls experience
*
* @param controller reference to the main swipe controller
*/
class PressToSwipeController(
private val controller: SwipeControlsHostActivity
) : BaseGestureController(controller) {
/**
* monitors if the user is currently in a swipe session.
*/
private var isInSwipeSession = false

override val shouldForceInterceptEvents: Boolean
get() = currentSwipe == SwipeDetector.SwipeDirection.VERTICAL && isInSwipeSession

override fun shouldDropMotion(motionEvent: MotionEvent): Boolean = false

override fun isInSwipeZone(motionEvent: MotionEvent): Boolean {
val inVolumeZone = if (controller.config.enableVolumeControls)
(motionEvent.toPoint() in controller.zones.volume) else false
val inBrightnessZone = if (controller.config.enableBrightnessControl)
(motionEvent.toPoint() in controller.zones.brightness) else false

return inVolumeZone || inBrightnessZone
}

override fun onUp(motionEvent: MotionEvent) {
super.onUp(motionEvent)
isInSwipeSession = false
}

override fun onLongPress(motionEvent: MotionEvent) {
// enter swipe session with feedback
isInSwipeSession = true
controller.overlay.onEnterSwipeSession()

// send GestureDetector a ACTION_CANCEL event so it will handle further events
motionEvent.action = MotionEvent.ACTION_CANCEL
detector.onTouchEvent(motionEvent)
}

override fun onSwipe(
from: MotionEvent,
to: MotionEvent,
distanceX: Double,
distanceY: Double
): Boolean {
// cancel if not in swipe session or vertical
if (!isInSwipeSession || currentSwipe != SwipeDetector.SwipeDirection.VERTICAL) return false
return when (from.toPoint()) {
in controller.zones.volume -> {
scrollVolume(distanceY)
true
}
in controller.zones.brightness -> {
scrollBrightness(distanceY)
true
}
else -> false
}
}
}

0 comments on commit 6d8c0a0

Please sign in to comment.