Merge pull request #4587 from vkay94/separate-player-gesture-logic-ui
Separate player gesture logic and UI
This commit is contained in:
commit
f1583b6e0c
4 changed files with 647 additions and 488 deletions
|
@ -0,0 +1,503 @@
|
|||
package org.schabi.newpipe.player.event
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import org.schabi.newpipe.player.BasePlayer
|
||||
import org.schabi.newpipe.player.MainPlayer
|
||||
import org.schabi.newpipe.player.VideoPlayerImpl
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper
|
||||
import org.schabi.newpipe.util.AnimationUtils
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.hypot
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Base gesture handling for [VideoPlayerImpl]
|
||||
*
|
||||
* This class contains the logic for the player gestures like View preparations
|
||||
* and provides some abstract methods to make it easier separating the logic from the UI.
|
||||
*/
|
||||
abstract class BasePlayerGestureListener(
|
||||
@JvmField
|
||||
protected val playerImpl: VideoPlayerImpl,
|
||||
@JvmField
|
||||
protected val service: MainPlayer
|
||||
) : GestureDetector.SimpleOnGestureListener(), View.OnTouchListener {
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// Abstract methods for VIDEO and POPUP
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
|
||||
abstract fun onDoubleTap(event: MotionEvent, portion: DisplayPortion)
|
||||
|
||||
abstract fun onSingleTap(playerType: MainPlayer.PlayerType)
|
||||
|
||||
abstract fun onScroll(
|
||||
playerType: MainPlayer.PlayerType,
|
||||
portion: DisplayPortion,
|
||||
initialEvent: MotionEvent,
|
||||
movingEvent: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
)
|
||||
|
||||
abstract fun onScrollEnd(playerType: MainPlayer.PlayerType, event: MotionEvent)
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// Abstract methods for POPUP (exclusive)
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
|
||||
abstract fun onPopupResizingStart()
|
||||
|
||||
abstract fun onPopupResizingEnd()
|
||||
|
||||
private var initialPopupX: Int = -1
|
||||
private var initialPopupY: Int = -1
|
||||
|
||||
private var isMovingInMain = false
|
||||
private var isMovingInPopup = false
|
||||
private var isResizing = false
|
||||
|
||||
private val tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service)
|
||||
|
||||
// [popup] initial coordinates and distance between fingers
|
||||
private var initPointerDistance = -1.0
|
||||
private var initFirstPointerX = -1f
|
||||
private var initFirstPointerY = -1f
|
||||
private var initSecPointerX = -1f
|
||||
private var initSecPointerY = -1f
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// onTouch implementation
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
return if (playerImpl.popupPlayerSelected()) {
|
||||
onTouchInPopup(v, event)
|
||||
} else {
|
||||
onTouchInMain(v, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onTouchInMain(v: View, event: MotionEvent): Boolean {
|
||||
playerImpl.gestureDetector.onTouchEvent(event)
|
||||
if (event.action == MotionEvent.ACTION_UP && isMovingInMain) {
|
||||
isMovingInMain = false
|
||||
onScrollEnd(MainPlayer.PlayerType.VIDEO, event)
|
||||
}
|
||||
return when (event.action) {
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
|
||||
v.parent.requestDisallowInterceptTouchEvent(playerImpl.isFullscreen)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
v.parent.requestDisallowInterceptTouchEvent(false)
|
||||
false
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun onTouchInPopup(v: View, event: MotionEvent): Boolean {
|
||||
if (playerImpl == null) {
|
||||
return false
|
||||
}
|
||||
playerImpl.gestureDetector.onTouchEvent(event)
|
||||
if (event.pointerCount == 2 && !isMovingInPopup && !isResizing) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.")
|
||||
}
|
||||
onPopupResizingStart()
|
||||
|
||||
// record coordinates of fingers
|
||||
initFirstPointerX = event.getX(0)
|
||||
initFirstPointerY = event.getY(0)
|
||||
initSecPointerX = event.getX(1)
|
||||
initSecPointerY = event.getY(1)
|
||||
// record distance between fingers
|
||||
initPointerDistance = Math.hypot(initFirstPointerX - initSecPointerX.toDouble(),
|
||||
initFirstPointerY - initSecPointerY.toDouble())
|
||||
|
||||
isResizing = true
|
||||
}
|
||||
if (event.action == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTouch() ACTION_MOVE > v = [$v], e1.getRaw = [${event.rawX}" +
|
||||
", ${event.rawY}]")
|
||||
}
|
||||
return handleMultiDrag(event)
|
||||
}
|
||||
if (event.action == MotionEvent.ACTION_UP) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTouch() ACTION_UP > v = [$v], e1.getRaw = [${event.rawX}" +
|
||||
", ${event.rawY}]")
|
||||
}
|
||||
if (isMovingInPopup) {
|
||||
isMovingInPopup = false
|
||||
onScrollEnd(MainPlayer.PlayerType.POPUP, event)
|
||||
}
|
||||
if (isResizing) {
|
||||
isResizing = false
|
||||
|
||||
initPointerDistance = (-1).toDouble()
|
||||
initFirstPointerX = (-1).toFloat()
|
||||
initFirstPointerY = (-1).toFloat()
|
||||
initSecPointerX = (-1).toFloat()
|
||||
initSecPointerY = (-1).toFloat()
|
||||
|
||||
onPopupResizingEnd()
|
||||
playerImpl.changeState(playerImpl.currentState)
|
||||
}
|
||||
if (!playerImpl.isPopupClosing) {
|
||||
playerImpl.savePositionAndSize()
|
||||
}
|
||||
}
|
||||
|
||||
v.performClick()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun handleMultiDrag(event: MotionEvent): Boolean {
|
||||
if (initPointerDistance != -1.0 && event.pointerCount == 2) {
|
||||
// get the movements of the fingers
|
||||
val firstPointerMove = hypot(event.getX(0) - initFirstPointerX.toDouble(),
|
||||
event.getY(0) - initFirstPointerY.toDouble())
|
||||
val secPointerMove = hypot(event.getX(1) - initSecPointerX.toDouble(),
|
||||
event.getY(1) - initSecPointerY.toDouble())
|
||||
|
||||
// minimum threshold beyond which pinch gesture will work
|
||||
val minimumMove = ViewConfiguration.get(service).scaledTouchSlop
|
||||
|
||||
if (max(firstPointerMove, secPointerMove) > minimumMove) {
|
||||
// calculate current distance between the pointers
|
||||
val currentPointerDistance = hypot(event.getX(0) - event.getX(1).toDouble(),
|
||||
event.getY(0) - event.getY(1).toDouble())
|
||||
|
||||
val popupWidth = playerImpl.popupWidth.toDouble()
|
||||
// change co-ordinates of popup so the center stays at the same position
|
||||
val newWidth = popupWidth * currentPointerDistance / initPointerDistance
|
||||
initPointerDistance = currentPointerDistance
|
||||
playerImpl.popupLayoutParams.x += ((popupWidth - newWidth) / 2.0).toInt()
|
||||
|
||||
playerImpl.checkPopupPositionBounds()
|
||||
playerImpl.updateScreenSize()
|
||||
|
||||
playerImpl.updatePopupSize(
|
||||
Math.min(playerImpl.screenWidth.toDouble(), newWidth).toInt(),
|
||||
-1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// Simple gestures
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onDown called with e = [$e]")
|
||||
|
||||
if (isDoubleTapping && isDoubleTapEnabled) {
|
||||
doubleTapControls?.onDoubleTapProgressDown(getDisplayPortion(e))
|
||||
return true
|
||||
}
|
||||
|
||||
return if (playerImpl.popupPlayerSelected())
|
||||
onDownInPopup(e)
|
||||
else
|
||||
true
|
||||
}
|
||||
|
||||
private fun onDownInPopup(e: MotionEvent): Boolean {
|
||||
// Fix popup position when the user touch it, it may have the wrong one
|
||||
// because the soft input is visible (the draggable area is currently resized).
|
||||
playerImpl.updateScreenSize()
|
||||
playerImpl.checkPopupPositionBounds()
|
||||
initialPopupX = playerImpl.popupLayoutParams.x
|
||||
initialPopupY = playerImpl.popupLayoutParams.y
|
||||
playerImpl.popupWidth = playerImpl.popupLayoutParams.width.toFloat()
|
||||
playerImpl.popupHeight = playerImpl.popupLayoutParams.height.toFloat()
|
||||
return super.onDown(e)
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onDoubleTap called with e = [$e]")
|
||||
|
||||
onDoubleTap(e, getDisplayPortion(e))
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "onSingleTapConfirmed() called with: e = [$e]")
|
||||
|
||||
if (isDoubleTapping)
|
||||
return true
|
||||
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
if (playerImpl.player == null)
|
||||
return false
|
||||
|
||||
onSingleTap(MainPlayer.PlayerType.POPUP)
|
||||
return true
|
||||
} else {
|
||||
super.onSingleTapConfirmed(e)
|
||||
if (playerImpl.currentState == BasePlayer.STATE_BLOCKED)
|
||||
return true
|
||||
|
||||
onSingleTap(MainPlayer.PlayerType.VIDEO)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onLongPress(e: MotionEvent?) {
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
playerImpl.updateScreenSize()
|
||||
playerImpl.checkPopupPositionBounds()
|
||||
playerImpl.updatePopupSize(playerImpl.screenWidth.toInt(), -1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
initialEvent: MotionEvent,
|
||||
movingEvent: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
): Boolean {
|
||||
return if (playerImpl.popupPlayerSelected()) {
|
||||
onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY)
|
||||
} else {
|
||||
onScrollInMain(initialEvent, movingEvent, distanceX, distanceY)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent?,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
return if (playerImpl.popupPlayerSelected()) {
|
||||
val absVelocityX = abs(velocityX)
|
||||
val absVelocityY = abs(velocityY)
|
||||
if (absVelocityX.coerceAtLeast(absVelocityY) > tossFlingVelocity) {
|
||||
if (absVelocityX > tossFlingVelocity) {
|
||||
playerImpl.popupLayoutParams.x = velocityX.toInt()
|
||||
}
|
||||
if (absVelocityY > tossFlingVelocity) {
|
||||
playerImpl.popupLayoutParams.y = velocityY.toInt()
|
||||
}
|
||||
playerImpl.checkPopupPositionBounds()
|
||||
playerImpl.windowManager
|
||||
.updateViewLayout(playerImpl.rootView, playerImpl.popupLayoutParams)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScrollInMain(
|
||||
initialEvent: MotionEvent,
|
||||
movingEvent: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
): Boolean {
|
||||
|
||||
if (!playerImpl.isFullscreen) {
|
||||
return false
|
||||
}
|
||||
|
||||
val isTouchingStatusBar: Boolean = initialEvent.y < getStatusBarHeight(service)
|
||||
val isTouchingNavigationBar: Boolean = (initialEvent.y
|
||||
> playerImpl.rootView.height - getNavigationBarHeight(service))
|
||||
if (isTouchingStatusBar || isTouchingNavigationBar) {
|
||||
return false
|
||||
}
|
||||
|
||||
val insideThreshold = abs(movingEvent.y - initialEvent.y) <= MOVEMENT_THRESHOLD
|
||||
if (!isMovingInMain && (insideThreshold || abs(distanceX) > abs(distanceY)) ||
|
||||
playerImpl.currentState == BasePlayer.STATE_COMPLETED) {
|
||||
return false
|
||||
}
|
||||
|
||||
isMovingInMain = true
|
||||
|
||||
onScroll(MainPlayer.PlayerType.VIDEO, getDisplayHalfPortion(initialEvent),
|
||||
initialEvent, movingEvent, distanceX, distanceY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onScrollInPopup(
|
||||
initialEvent: MotionEvent,
|
||||
movingEvent: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
): Boolean {
|
||||
|
||||
if (isResizing) {
|
||||
return super.onScroll(initialEvent, movingEvent, distanceX, distanceY)
|
||||
}
|
||||
|
||||
if (!isMovingInPopup) {
|
||||
AnimationUtils.animateView(playerImpl.closeOverlayButton, true, 200)
|
||||
}
|
||||
|
||||
isMovingInPopup = true
|
||||
|
||||
val diffX: Float = (movingEvent.rawX - initialEvent.rawX)
|
||||
var posX: Float = (initialPopupX + diffX)
|
||||
val diffY: Float = (movingEvent.rawY - initialEvent.rawY)
|
||||
var posY: Float = (initialPopupY + diffY)
|
||||
|
||||
if (posX > playerImpl.screenWidth - playerImpl.popupWidth) {
|
||||
posX = (playerImpl.screenWidth - playerImpl.popupWidth)
|
||||
} else if (posX < 0) {
|
||||
posX = 0f
|
||||
}
|
||||
|
||||
if (posY > playerImpl.screenHeight - playerImpl.popupHeight) {
|
||||
posY = (playerImpl.screenHeight - playerImpl.popupHeight)
|
||||
} else if (posY < 0) {
|
||||
posY = 0f
|
||||
}
|
||||
|
||||
playerImpl.popupLayoutParams.x = posX.toInt()
|
||||
playerImpl.popupLayoutParams.y = posY.toInt()
|
||||
|
||||
onScroll(MainPlayer.PlayerType.POPUP, getDisplayHalfPortion(initialEvent),
|
||||
initialEvent, movingEvent, distanceX, distanceY)
|
||||
|
||||
playerImpl.windowManager
|
||||
.updateViewLayout(playerImpl.rootView, playerImpl.popupLayoutParams)
|
||||
return true
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// Multi double tapping
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
|
||||
var doubleTapControls: DoubleTapListener? = null
|
||||
private set
|
||||
|
||||
val isDoubleTapEnabled: Boolean
|
||||
get() = doubleTapDelay > 0
|
||||
|
||||
var isDoubleTapping = false
|
||||
private set
|
||||
|
||||
fun doubleTapControls(listener: DoubleTapListener) = apply {
|
||||
doubleTapControls = listener
|
||||
}
|
||||
|
||||
private var doubleTapDelay = DOUBLE_TAP_DELAY
|
||||
private val doubleTapHandler: Handler = Handler()
|
||||
private val doubleTapRunnable = Runnable {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "doubleTapRunnable called")
|
||||
|
||||
isDoubleTapping = false
|
||||
doubleTapControls?.onDoubleTapFinished()
|
||||
}
|
||||
|
||||
fun startMultiDoubleTap(e: MotionEvent) {
|
||||
if (!isDoubleTapping) {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "startMultiDoubleTap called with e = [$e]")
|
||||
|
||||
keepInDoubleTapMode()
|
||||
doubleTapControls?.onDoubleTapStarted(getDisplayPortion(e))
|
||||
}
|
||||
}
|
||||
|
||||
fun keepInDoubleTapMode() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "keepInDoubleTapMode called")
|
||||
|
||||
isDoubleTapping = true
|
||||
doubleTapHandler.removeCallbacks(doubleTapRunnable)
|
||||
doubleTapHandler.postDelayed(doubleTapRunnable, doubleTapDelay)
|
||||
}
|
||||
|
||||
fun endMultiDoubleTap() {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "endMultiDoubleTap called")
|
||||
|
||||
isDoubleTapping = false
|
||||
doubleTapHandler.removeCallbacks(doubleTapRunnable)
|
||||
doubleTapControls?.onDoubleTapFinished()
|
||||
}
|
||||
|
||||
fun enableMultiDoubleTap(enable: Boolean) = apply {
|
||||
doubleTapDelay = if (enable) DOUBLE_TAP_DELAY else 0
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
|
||||
private fun getDisplayPortion(e: MotionEvent): DisplayPortion {
|
||||
return if (playerImpl.playerType == MainPlayer.PlayerType.POPUP) {
|
||||
when {
|
||||
e.x < playerImpl.popupWidth / 3.0 -> DisplayPortion.LEFT
|
||||
e.x > playerImpl.popupWidth * 2.0 / 3.0 -> DisplayPortion.RIGHT
|
||||
else -> DisplayPortion.MIDDLE
|
||||
}
|
||||
} else /* MainPlayer.PlayerType.VIDEO */ {
|
||||
when {
|
||||
e.x < playerImpl.rootView.width / 3.0 -> DisplayPortion.LEFT
|
||||
e.x > playerImpl.rootView.width * 2.0 / 3.0 -> DisplayPortion.RIGHT
|
||||
else -> DisplayPortion.MIDDLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Currently needed for scrolling since there is no action more the middle portion
|
||||
private fun getDisplayHalfPortion(e: MotionEvent): DisplayPortion {
|
||||
return if (playerImpl.playerType == MainPlayer.PlayerType.POPUP) {
|
||||
when {
|
||||
e.x < playerImpl.popupWidth / 2.0 -> DisplayPortion.LEFT_HALF
|
||||
else -> DisplayPortion.RIGHT_HALF
|
||||
}
|
||||
} else /* MainPlayer.PlayerType.VIDEO */ {
|
||||
when {
|
||||
e.x < playerImpl.rootView.width / 2.0 -> DisplayPortion.LEFT_HALF
|
||||
else -> DisplayPortion.RIGHT_HALF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNavigationBarHeight(context: Context): Int {
|
||||
val resId = context.resources
|
||||
.getIdentifier("navigation_bar_height", "dimen", "android")
|
||||
return if (resId > 0) {
|
||||
context.resources.getDimensionPixelSize(resId)
|
||||
} else 0
|
||||
}
|
||||
|
||||
private fun getStatusBarHeight(context: Context): Int {
|
||||
val resId = context.resources
|
||||
.getIdentifier("status_bar_height", "dimen", "android")
|
||||
return if (resId > 0) {
|
||||
context.resources.getDimensionPixelSize(resId)
|
||||
} else 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BasePlayerGestListener"
|
||||
private val DEBUG = BasePlayer.DEBUG
|
||||
|
||||
private const val DOUBLE_TAP_DELAY = 550L
|
||||
private const val MOVEMENT_THRESHOLD = 40
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.schabi.newpipe.player.event
|
||||
|
||||
enum class DisplayPortion {
|
||||
LEFT, MIDDLE, RIGHT, LEFT_HALF, RIGHT_HALF
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.schabi.newpipe.player.event
|
||||
|
||||
interface DoubleTapListener {
|
||||
fun onDoubleTapStarted(portion: DisplayPortion) {}
|
||||
fun onDoubleTapProgressDown(portion: DisplayPortion) {}
|
||||
fun onDoubleTapFinished() {}
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
package org.schabi.newpipe.player.event;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.player.BasePlayer;
|
||||
import org.schabi.newpipe.player.MainPlayer;
|
||||
|
@ -23,217 +23,116 @@ import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME;
|
|||
import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA;
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
/**
|
||||
* GestureListener for the player
|
||||
*
|
||||
* While {@link 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.
|
||||
*/
|
||||
public class PlayerGestureListener
|
||||
extends GestureDetector.SimpleOnGestureListener
|
||||
extends BasePlayerGestureListener
|
||||
implements View.OnTouchListener {
|
||||
private static final String TAG = ".PlayerGestureListener";
|
||||
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||
|
||||
private final VideoPlayerImpl playerImpl;
|
||||
private final MainPlayer service;
|
||||
|
||||
private int initialPopupX;
|
||||
private int initialPopupY;
|
||||
|
||||
private boolean isMovingInMain;
|
||||
private boolean isMovingInPopup;
|
||||
|
||||
private boolean isResizing;
|
||||
|
||||
private final int tossFlingVelocity;
|
||||
|
||||
private final boolean isVolumeGestureEnabled;
|
||||
private final boolean isBrightnessGestureEnabled;
|
||||
private final int maxVolume;
|
||||
private static final int MOVEMENT_THRESHOLD = 40;
|
||||
|
||||
// [popup] initial coordinates and distance between fingers
|
||||
private double initPointerDistance = -1;
|
||||
private float initFirstPointerX = -1;
|
||||
private float initFirstPointerY = -1;
|
||||
private float initSecPointerX = -1;
|
||||
private float initSecPointerY = -1;
|
||||
|
||||
|
||||
public PlayerGestureListener(final VideoPlayerImpl playerImpl, final MainPlayer service) {
|
||||
this.playerImpl = playerImpl;
|
||||
this.service = service;
|
||||
this.tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service);
|
||||
super(playerImpl, service);
|
||||
|
||||
isVolumeGestureEnabled = PlayerHelper.isVolumeGestureEnabled(service);
|
||||
isBrightnessGestureEnabled = PlayerHelper.isBrightnessGestureEnabled(service);
|
||||
maxVolume = playerImpl.getAudioReactor().getMaxVolume();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/*
|
||||
* Main and popup players' gesture listeners is too different.
|
||||
* So it will be better to have different implementations of them
|
||||
* */
|
||||
@Override
|
||||
public boolean onDoubleTap(final MotionEvent e) {
|
||||
public void onDoubleTap(@NotNull final MotionEvent event,
|
||||
@NotNull final DisplayPortion portion) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = "
|
||||
+ e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
|
||||
Log.d(TAG, "onDoubleTap called with playerType = ["
|
||||
+ playerImpl.getPlayerType() + "], portion = ["
|
||||
+ portion + "]");
|
||||
}
|
||||
if (playerImpl.isSomePopupMenuVisible()) {
|
||||
playerImpl.hideControls(0, 0);
|
||||
}
|
||||
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
return onDoubleTapInPopup(e);
|
||||
} else {
|
||||
return onDoubleTapInMain(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(final MotionEvent e) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
|
||||
}
|
||||
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
return onSingleTapConfirmedInPopup(e);
|
||||
} else {
|
||||
return onSingleTapConfirmedInMain(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(final MotionEvent e) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDown() called with: e = [" + e + "]");
|
||||
}
|
||||
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
return onDownInPopup(e);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(final MotionEvent e) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onLongPress() called with: e = [" + e + "]");
|
||||
}
|
||||
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
onLongPressInPopup(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(final MotionEvent initialEvent, final MotionEvent movingEvent,
|
||||
final float distanceX, final float distanceY) {
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
return onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY);
|
||||
} else {
|
||||
return onScrollInMain(initialEvent, movingEvent, distanceX, distanceY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(final MotionEvent e1, final MotionEvent e2,
|
||||
final float velocityX, final float velocityY) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onFling() called with velocity: dX=["
|
||||
+ velocityX + "], dY=[" + velocityY + "]");
|
||||
}
|
||||
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
return onFlingInPopup(e1, e2, velocityX, velocityY);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(final View v, final MotionEvent event) {
|
||||
/*if (DEBUG && false) {
|
||||
Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]");
|
||||
}*/
|
||||
|
||||
if (playerImpl.popupPlayerSelected()) {
|
||||
return onTouchInPopup(v, event);
|
||||
} else {
|
||||
return onTouchInMain(v, event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Main player listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private boolean onDoubleTapInMain(final MotionEvent e) {
|
||||
if (e.getX() > playerImpl.getRootView().getWidth() * 2.0 / 3.0) {
|
||||
playerImpl.onFastForward();
|
||||
} else if (e.getX() < playerImpl.getRootView().getWidth() / 3.0) {
|
||||
if (portion == DisplayPortion.LEFT) {
|
||||
playerImpl.onFastRewind();
|
||||
} else {
|
||||
playerImpl.getPlayPauseButton().performClick();
|
||||
} else if (portion == DisplayPortion.MIDDLE) {
|
||||
playerImpl.onPlayPause();
|
||||
} else if (portion == DisplayPortion.RIGHT) {
|
||||
playerImpl.onFastForward();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean onSingleTapConfirmedInMain(final MotionEvent e) {
|
||||
@Override
|
||||
public void onSingleTap(@NotNull final MainPlayer.PlayerType playerType) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
|
||||
Log.d(TAG, "onSingleTap called with playerType = ["
|
||||
+ playerImpl.getPlayerType() + "]");
|
||||
}
|
||||
if (playerType == MainPlayer.PlayerType.POPUP) {
|
||||
|
||||
if (playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (playerImpl.isControlsVisible()) {
|
||||
playerImpl.hideControls(150, 0);
|
||||
} else {
|
||||
if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) {
|
||||
playerImpl.showControls(0);
|
||||
if (playerImpl.isControlsVisible()) {
|
||||
playerImpl.hideControls(100, 100);
|
||||
} else {
|
||||
playerImpl.getPlayPauseButton().requestFocus();
|
||||
playerImpl.showControlsThenHide();
|
||||
}
|
||||
|
||||
} else /* playerType == MainPlayer.PlayerType.VIDEO */ {
|
||||
|
||||
if (playerImpl.isControlsVisible()) {
|
||||
playerImpl.hideControls(150, 0);
|
||||
} else {
|
||||
if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) {
|
||||
playerImpl.showControls(0);
|
||||
} else {
|
||||
playerImpl.showControlsThenHide();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onScrollInMain(final MotionEvent initialEvent, final MotionEvent movingEvent,
|
||||
final float distanceX, final float distanceY) {
|
||||
if ((!isVolumeGestureEnabled && !isBrightnessGestureEnabled)
|
||||
|| !playerImpl.isFullscreen()) {
|
||||
return false;
|
||||
@Override
|
||||
public void onScroll(@NotNull final MainPlayer.PlayerType playerType,
|
||||
@NotNull final DisplayPortion portion,
|
||||
@NotNull final MotionEvent initialEvent,
|
||||
@NotNull final MotionEvent movingEvent,
|
||||
final float distanceX, final float distanceY) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onScroll called with playerType = ["
|
||||
+ playerImpl.getPlayerType() + "], portion = ["
|
||||
+ portion + "]");
|
||||
}
|
||||
if (playerType == MainPlayer.PlayerType.VIDEO) {
|
||||
if (portion == DisplayPortion.LEFT_HALF) {
|
||||
onScrollMainVolume(distanceX, distanceY);
|
||||
|
||||
final boolean isTouchingStatusBar = initialEvent.getY() < getStatusBarHeight(service);
|
||||
final boolean isTouchingNavigationBar = initialEvent.getY()
|
||||
> playerImpl.getRootView().getHeight() - getNavigationBarHeight(service);
|
||||
if (isTouchingStatusBar || isTouchingNavigationBar) {
|
||||
return false;
|
||||
} else /* DisplayPortion.RIGHT_HALF */ {
|
||||
onScrollMainBrightness(distanceX, distanceY);
|
||||
}
|
||||
|
||||
} else /* MainPlayer.PlayerType.POPUP */ {
|
||||
final View closingOverlayView = playerImpl.getClosingOverlayView();
|
||||
if (playerImpl.isInsideClosingRadius(movingEvent)) {
|
||||
if (closingOverlayView.getVisibility() == View.GONE) {
|
||||
animateView(closingOverlayView, true, 250);
|
||||
}
|
||||
} else {
|
||||
if (closingOverlayView.getVisibility() == View.VISIBLE) {
|
||||
animateView(closingOverlayView, false, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if (DEBUG && false) Log.d(TAG, "onScrollInMain = " +
|
||||
", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" +
|
||||
", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" +
|
||||
", distanceXy = [" + distanceX + ", " + distanceY + "]");*/
|
||||
|
||||
final boolean insideThreshold =
|
||||
Math.abs(movingEvent.getY() - initialEvent.getY()) <= MOVEMENT_THRESHOLD;
|
||||
if (!isMovingInMain && (insideThreshold || Math.abs(distanceX) > Math.abs(distanceY))
|
||||
|| playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isMovingInMain = true;
|
||||
|
||||
final boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled;
|
||||
final boolean acceptVolumeArea = acceptAnyArea
|
||||
|| initialEvent.getX() > playerImpl.getRootView().getWidth() / 2.0;
|
||||
|
||||
if (isVolumeGestureEnabled && acceptVolumeArea) {
|
||||
private void onScrollMainVolume(final float distanceX, final float distanceY) {
|
||||
if (isVolumeGestureEnabled) {
|
||||
playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY);
|
||||
final float currentProgressPercent = (float) playerImpl
|
||||
.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength();
|
||||
|
@ -258,10 +157,14 @@ public class PlayerGestureListener
|
|||
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
|
||||
playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
private void onScrollMainBrightness(final float distanceX, final float distanceY) {
|
||||
if (isBrightnessGestureEnabled) {
|
||||
final Activity parent = playerImpl.getParentActivity();
|
||||
if (parent == null) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
final Window window = parent.getWindow();
|
||||
|
@ -299,330 +202,71 @@ public class PlayerGestureListener
|
|||
playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onScrollEndInMain() {
|
||||
@Override
|
||||
public void onScrollEnd(@NotNull final MainPlayer.PlayerType playerType,
|
||||
@NotNull final MotionEvent event) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onScrollEnd() called");
|
||||
Log.d(TAG, "onScrollEnd called with playerType = ["
|
||||
+ playerImpl.getPlayerType() + "]");
|
||||
}
|
||||
if (playerType == MainPlayer.PlayerType.VIDEO) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onScrollEnd() called");
|
||||
}
|
||||
|
||||
if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
|
||||
animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
|
||||
}
|
||||
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
|
||||
animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200);
|
||||
}
|
||||
if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) {
|
||||
animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA,
|
||||
false, 200, 200);
|
||||
}
|
||||
if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) {
|
||||
animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA,
|
||||
false, 200, 200);
|
||||
}
|
||||
|
||||
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
|
||||
playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
|
||||
playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
}
|
||||
} else {
|
||||
if (playerImpl == null) {
|
||||
return;
|
||||
}
|
||||
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
|
||||
playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
}
|
||||
|
||||
if (playerImpl.isInsideClosingRadius(event)) {
|
||||
playerImpl.closePopup();
|
||||
} else {
|
||||
animateView(playerImpl.getClosingOverlayView(), false, 0);
|
||||
|
||||
if (!playerImpl.isPopupClosing) {
|
||||
animateView(playerImpl.getCloseOverlayButton(), false, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onTouchInMain(final View v, final MotionEvent event) {
|
||||
playerImpl.getGestureDetector().onTouchEvent(event);
|
||||
if (event.getAction() == MotionEvent.ACTION_UP && isMovingInMain) {
|
||||
isMovingInMain = false;
|
||||
onScrollEndInMain();
|
||||
}
|
||||
// This hack allows to stop receiving touch events on appbar
|
||||
// while touching video player's view
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
v.getParent().requestDisallowInterceptTouchEvent(playerImpl.isFullscreen());
|
||||
return true;
|
||||
case MotionEvent.ACTION_UP:
|
||||
v.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Popup player listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private boolean onDoubleTapInPopup(final MotionEvent e) {
|
||||
if (playerImpl == null || !playerImpl.isPlaying()) {
|
||||
return false;
|
||||
@Override
|
||||
public void onPopupResizingStart() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPopupResizingStart called");
|
||||
}
|
||||
playerImpl.showAndAnimateControl(-1, true);
|
||||
playerImpl.getLoadingPanel().setVisibility(View.GONE);
|
||||
|
||||
playerImpl.hideControls(0, 0);
|
||||
|
||||
if (e.getX() > playerImpl.getPopupWidth() / 2) {
|
||||
playerImpl.onFastForward();
|
||||
} else {
|
||||
playerImpl.onFastRewind();
|
||||
}
|
||||
|
||||
return true;
|
||||
animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0);
|
||||
animateView(playerImpl.getResizingIndicator(), true, 200, 0);
|
||||
}
|
||||
|
||||
private boolean onSingleTapConfirmedInPopup(final MotionEvent e) {
|
||||
if (playerImpl == null || playerImpl.getPlayer() == null) {
|
||||
return false;
|
||||
@Override
|
||||
public void onPopupResizingEnd() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPopupResizingEnd called");
|
||||
}
|
||||
if (playerImpl.isControlsVisible()) {
|
||||
playerImpl.hideControls(100, 100);
|
||||
} else {
|
||||
playerImpl.getPlayPauseButton().requestFocus();
|
||||
playerImpl.showControlsThenHide();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onDownInPopup(final MotionEvent e) {
|
||||
// Fix popup position when the user touch it, it may have the wrong one
|
||||
// because the soft input is visible (the draggable area is currently resized).
|
||||
playerImpl.updateScreenSize();
|
||||
playerImpl.checkPopupPositionBounds();
|
||||
|
||||
initialPopupX = playerImpl.getPopupLayoutParams().x;
|
||||
initialPopupY = playerImpl.getPopupLayoutParams().y;
|
||||
playerImpl.setPopupWidth(playerImpl.getPopupLayoutParams().width);
|
||||
playerImpl.setPopupHeight(playerImpl.getPopupLayoutParams().height);
|
||||
return super.onDown(e);
|
||||
}
|
||||
|
||||
private void onLongPressInPopup(final MotionEvent e) {
|
||||
playerImpl.updateScreenSize();
|
||||
playerImpl.checkPopupPositionBounds();
|
||||
playerImpl.updatePopupSize((int) playerImpl.getScreenWidth(), -1);
|
||||
}
|
||||
|
||||
private boolean onScrollInPopup(final MotionEvent initialEvent,
|
||||
final MotionEvent movingEvent,
|
||||
final float distanceX,
|
||||
final float distanceY) {
|
||||
if (isResizing || playerImpl == null) {
|
||||
return super.onScroll(initialEvent, movingEvent, distanceX, distanceY);
|
||||
}
|
||||
|
||||
if (!isMovingInPopup) {
|
||||
animateView(playerImpl.getCloseOverlayButton(), true, 200);
|
||||
}
|
||||
|
||||
isMovingInPopup = true;
|
||||
|
||||
final float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX());
|
||||
float posX = (int) (initialPopupX + diffX);
|
||||
final float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY());
|
||||
float posY = (int) (initialPopupY + diffY);
|
||||
|
||||
if (posX > (playerImpl.getScreenWidth() - playerImpl.getPopupWidth())) {
|
||||
posX = (int) (playerImpl.getScreenWidth() - playerImpl.getPopupWidth());
|
||||
} else if (posX < 0) {
|
||||
posX = 0;
|
||||
}
|
||||
|
||||
if (posY > (playerImpl.getScreenHeight() - playerImpl.getPopupHeight())) {
|
||||
posY = (int) (playerImpl.getScreenHeight() - playerImpl.getPopupHeight());
|
||||
} else if (posY < 0) {
|
||||
posY = 0;
|
||||
}
|
||||
|
||||
playerImpl.getPopupLayoutParams().x = (int) posX;
|
||||
playerImpl.getPopupLayoutParams().y = (int) posY;
|
||||
|
||||
final View closingOverlayView = playerImpl.getClosingOverlayView();
|
||||
if (playerImpl.isInsideClosingRadius(movingEvent)) {
|
||||
if (closingOverlayView.getVisibility() == View.GONE) {
|
||||
animateView(closingOverlayView, true, 250);
|
||||
}
|
||||
} else {
|
||||
if (closingOverlayView.getVisibility() == View.VISIBLE) {
|
||||
animateView(closingOverlayView, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// if (DEBUG) {
|
||||
// Log.d(TAG, "onScrollInPopup = "
|
||||
// + "e1.getRaw = [" + initialEvent.getRawX() + ", "
|
||||
// + initialEvent.getRawY() + "], "
|
||||
// + "e1.getX,Y = [" + initialEvent.getX() + ", "
|
||||
// + initialEvent.getY() + "], "
|
||||
// + "e2.getRaw = [" + movingEvent.getRawX() + ", "
|
||||
// + movingEvent.getRawY() + "], "
|
||||
// + "e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "], "
|
||||
// + "distanceX,Y = [" + distanceX + ", " + distanceY + "], "
|
||||
// + "posX,Y = [" + posX + ", " + posY + "], "
|
||||
// + "popupW,H = [" + popupWidth + " x " + popupHeight + "]");
|
||||
// }
|
||||
playerImpl.windowManager
|
||||
.updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams());
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onScrollEndInPopup(final MotionEvent event) {
|
||||
if (playerImpl == null) {
|
||||
return;
|
||||
}
|
||||
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) {
|
||||
playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME);
|
||||
}
|
||||
|
||||
if (playerImpl.isInsideClosingRadius(event)) {
|
||||
playerImpl.closePopup();
|
||||
} else {
|
||||
animateView(playerImpl.getClosingOverlayView(), false, 0);
|
||||
|
||||
if (!playerImpl.isPopupClosing) {
|
||||
animateView(playerImpl.getCloseOverlayButton(), false, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onFlingInPopup(final MotionEvent e1,
|
||||
final MotionEvent e2,
|
||||
final float velocityX,
|
||||
final float velocityY) {
|
||||
if (playerImpl == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final float absVelocityX = Math.abs(velocityX);
|
||||
final float absVelocityY = Math.abs(velocityY);
|
||||
if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) {
|
||||
if (absVelocityX > tossFlingVelocity) {
|
||||
playerImpl.getPopupLayoutParams().x = (int) velocityX;
|
||||
}
|
||||
if (absVelocityY > tossFlingVelocity) {
|
||||
playerImpl.getPopupLayoutParams().y = (int) velocityY;
|
||||
}
|
||||
playerImpl.checkPopupPositionBounds();
|
||||
playerImpl.windowManager
|
||||
.updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onTouchInPopup(final View v, final MotionEvent event) {
|
||||
if (playerImpl == null) {
|
||||
return false;
|
||||
}
|
||||
playerImpl.getGestureDetector().onTouchEvent(event);
|
||||
|
||||
if (event.getPointerCount() == 2 && !isMovingInPopup && !isResizing) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing.");
|
||||
}
|
||||
playerImpl.showAndAnimateControl(-1, true);
|
||||
playerImpl.getLoadingPanel().setVisibility(View.GONE);
|
||||
|
||||
playerImpl.hideControls(0, 0);
|
||||
animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0);
|
||||
animateView(playerImpl.getResizingIndicator(), true, 200, 0);
|
||||
//record coordinates of fingers
|
||||
initFirstPointerX = event.getX(0);
|
||||
initFirstPointerY = event.getY(0);
|
||||
initSecPointerX = event.getX(1);
|
||||
initSecPointerY = event.getY(1);
|
||||
//record distance between fingers
|
||||
initPointerDistance = Math.hypot(initFirstPointerX - initSecPointerX,
|
||||
initFirstPointerY - initSecPointerY);
|
||||
|
||||
isResizing = true;
|
||||
}
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "], "
|
||||
+ "e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
|
||||
}
|
||||
return handleMultiDrag(event);
|
||||
}
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "], "
|
||||
+ "e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]");
|
||||
}
|
||||
if (isMovingInPopup) {
|
||||
isMovingInPopup = false;
|
||||
onScrollEndInPopup(event);
|
||||
}
|
||||
|
||||
if (isResizing) {
|
||||
isResizing = false;
|
||||
|
||||
initPointerDistance = -1;
|
||||
initFirstPointerX = -1;
|
||||
initFirstPointerY = -1;
|
||||
initSecPointerX = -1;
|
||||
initSecPointerY = -1;
|
||||
|
||||
animateView(playerImpl.getResizingIndicator(), false, 100, 0);
|
||||
playerImpl.changeState(playerImpl.getCurrentState());
|
||||
}
|
||||
|
||||
if (!playerImpl.isPopupClosing) {
|
||||
playerImpl.savePositionAndSize();
|
||||
}
|
||||
}
|
||||
|
||||
v.performClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleMultiDrag(final MotionEvent event) {
|
||||
if (initPointerDistance != -1 && event.getPointerCount() == 2) {
|
||||
// get the movements of the fingers
|
||||
final double firstPointerMove = Math.hypot(event.getX(0) - initFirstPointerX,
|
||||
event.getY(0) - initFirstPointerY);
|
||||
final double secPointerMove = Math.hypot(event.getX(1) - initSecPointerX,
|
||||
event.getY(1) - initSecPointerY);
|
||||
|
||||
// minimum threshold beyond which pinch gesture will work
|
||||
final int minimumMove = ViewConfiguration.get(service).getScaledTouchSlop();
|
||||
|
||||
if (Math.max(firstPointerMove, secPointerMove) > minimumMove) {
|
||||
// calculate current distance between the pointers
|
||||
final double currentPointerDistance =
|
||||
Math.hypot(event.getX(0) - event.getX(1),
|
||||
event.getY(0) - event.getY(1));
|
||||
|
||||
final double popupWidth = playerImpl.getPopupWidth();
|
||||
// change co-ordinates of popup so the center stays at the same position
|
||||
final double newWidth = (popupWidth * currentPointerDistance / initPointerDistance);
|
||||
initPointerDistance = currentPointerDistance;
|
||||
playerImpl.getPopupLayoutParams().x += (popupWidth - newWidth) / 2;
|
||||
|
||||
playerImpl.checkPopupPositionBounds();
|
||||
playerImpl.updateScreenSize();
|
||||
|
||||
playerImpl.updatePopupSize(
|
||||
(int) Math.min(playerImpl.getScreenWidth(), newWidth),
|
||||
-1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Utils
|
||||
* */
|
||||
|
||||
private int getNavigationBarHeight(final Context context) {
|
||||
final int resId = context.getResources()
|
||||
.getIdentifier("navigation_bar_height", "dimen", "android");
|
||||
if (resId > 0) {
|
||||
return context.getResources().getDimensionPixelSize(resId);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int getStatusBarHeight(final Context context) {
|
||||
final int resId = context.getResources()
|
||||
.getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resId > 0) {
|
||||
return context.getResources().getDimensionPixelSize(resId);
|
||||
}
|
||||
return 0;
|
||||
animateView(playerImpl.getResizingIndicator(), false, 100, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue