feat: Add hold-to-fast-forward (2x speed) feature
- Add tap and hold gesture to increase playback speed to 2x while held - Add settings toggle in Video & Audio settings to enable/disable the feature - Add visual indicator overlay showing '2x Speed' when feature is active - Feature is enabled by default and respects user preference
This commit is contained in:
parent
1bca5f3d63
commit
6eba36700a
@ -9,6 +9,7 @@ import android.view.View
|
||||
import androidx.core.os.postDelayed
|
||||
import org.schabi.newpipe.databinding.PlayerBinding
|
||||
import org.schabi.newpipe.player.Player
|
||||
import org.schabi.newpipe.player.helper.PlayerHelper
|
||||
import org.schabi.newpipe.player.ui.VideoPlayerUi
|
||||
|
||||
/**
|
||||
@ -24,11 +25,87 @@ abstract class BasePlayerGestureListener(
|
||||
protected val player: Player = playerUi.player
|
||||
protected val binding: PlayerBinding = playerUi.binding
|
||||
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
// Hold to fast forward (2x speed)
|
||||
// ///////////////////////////////////////////////////////////////////
|
||||
|
||||
private var isHoldingForFastForward = false
|
||||
private var originalPlaybackSpeed = 1.0f
|
||||
private val fastForwardSpeed = 2.0f
|
||||
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
playerUi.gestureDetector.onTouchEvent(event)
|
||||
|
||||
// Handle touch up to restore original speed when hold-to-fast-forward is active
|
||||
if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
|
||||
if (isHoldingForFastForward) {
|
||||
stopHoldToFastForward()
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onLongPress(e: MotionEvent) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onLongPress called with e = [$e]")
|
||||
}
|
||||
|
||||
// Check if hold-to-fast-forward is enabled in settings
|
||||
if (!PlayerHelper.isHoldToFastForwardEnabled(player.context)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Only activate if player is playing and not in a popup menu
|
||||
if (player.currentState != Player.STATE_PLAYING || playerUi.isSomePopupMenuVisible) {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't activate during double tap mode
|
||||
if (isDoubleTapping) {
|
||||
return
|
||||
}
|
||||
|
||||
startHoldToFastForward()
|
||||
}
|
||||
|
||||
private fun startHoldToFastForward() {
|
||||
if (isHoldingForFastForward) {
|
||||
return
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "startHoldToFastForward: activating 2x speed")
|
||||
}
|
||||
|
||||
isHoldingForFastForward = true
|
||||
originalPlaybackSpeed = player.playbackSpeed
|
||||
|
||||
// Set playback speed to 2x
|
||||
player.setPlaybackSpeed(fastForwardSpeed)
|
||||
|
||||
// Show visual feedback
|
||||
playerUi.onHoldToFastForwardStart()
|
||||
}
|
||||
|
||||
private fun stopHoldToFastForward() {
|
||||
if (!isHoldingForFastForward) {
|
||||
return
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "stopHoldToFastForward: restoring original speed $originalPlaybackSpeed")
|
||||
}
|
||||
|
||||
isHoldingForFastForward = false
|
||||
|
||||
// Restore original playback speed
|
||||
player.setPlaybackSpeed(originalPlaybackSpeed)
|
||||
|
||||
// Hide visual feedback
|
||||
playerUi.onHoldToFastForwardEnd()
|
||||
}
|
||||
|
||||
private fun onDoubleTap(
|
||||
event: MotionEvent,
|
||||
portion: DisplayPortion
|
||||
|
||||
@ -231,6 +231,11 @@ public final class PlayerHelper {
|
||||
.getBoolean(context.getString(R.string.auto_queue_key), false);
|
||||
}
|
||||
|
||||
public static boolean isHoldToFastForwardEnabled(@NonNull final Context context) {
|
||||
return getPreferences(context)
|
||||
.getBoolean(context.getString(R.string.hold_to_fast_forward_key), true);
|
||||
}
|
||||
|
||||
public static boolean isClearingQueueConfirmationRequired(@NonNull final Context context) {
|
||||
return getPreferences(context)
|
||||
.getBoolean(context.getString(R.string.clear_queue_confirmation_key), false);
|
||||
|
||||
@ -441,6 +441,33 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa
|
||||
//endregion
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Hold to fast forward
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
//region Hold to fast forward
|
||||
|
||||
/**
|
||||
* Called when hold-to-fast-forward is activated (long press detected).
|
||||
* Shows the visual indicator overlay.
|
||||
*/
|
||||
public void onHoldToFastForwardStart() {
|
||||
animate(binding.holdToFastForwardOverlay, true, DEFAULT_CONTROLS_DURATION);
|
||||
// Hide controls while fast forwarding
|
||||
if (isControlsVisible()) {
|
||||
hideControls(DEFAULT_CONTROLS_DURATION, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when hold-to-fast-forward is deactivated (finger released).
|
||||
* Hides the visual indicator overlay.
|
||||
*/
|
||||
public void onHoldToFastForwardEnd() {
|
||||
animate(binding.holdToFastForwardOverlay, false, DEFAULT_CONTROLS_DURATION);
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Broadcast receiver
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
10
app/src/main/res/drawable/ic_fast_forward.xml
Normal file
10
app/src/main/res/drawable/ic_fast_forward.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="@color/defaultIconTint"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z" />
|
||||
</vector>
|
||||
@ -809,4 +809,40 @@
|
||||
android:alpha="0"
|
||||
android:visibility="invisible" /> <!-- Required for the first appearance fading correctly -->
|
||||
|
||||
<!-- Hold to fast forward indicator -->
|
||||
<LinearLayout
|
||||
android:id="@+id/holdToFastForwardOverlay"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="80dp"
|
||||
android:background="@drawable/background_oval_black_transparent"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:src="@drawable/ic_fast_forward"
|
||||
app:tint="@color/white"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<org.schabi.newpipe.views.NewPipeTextView
|
||||
android:id="@+id/holdToFastForwardText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hold_to_fast_forward_indicator"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@ -25,6 +25,8 @@
|
||||
<string name="clear_queue_confirmation_key">clear_queue_confirmation_key</string>
|
||||
<string name="ignore_hardware_media_buttons_key">ignore_hardware_media_buttons_key</string>
|
||||
|
||||
<string name="hold_to_fast_forward_key">hold_to_fast_forward_key</string>
|
||||
|
||||
<string name="popup_saved_width_key">popup_saved_width</string>
|
||||
<string name="popup_saved_x_key">popup_saved_x</string>
|
||||
<string name="popup_saved_y_key">popup_saved_y</string>
|
||||
|
||||
@ -91,6 +91,9 @@
|
||||
<string name="clear_queue_confirmation_description">The active player queue will be replaced</string>
|
||||
<string name="ignore_hardware_media_buttons_title">Ignore hardware media button events</string>
|
||||
<string name="ignore_hardware_media_buttons_summary">Useful, for instance, if you are using a headset with broken physical buttons</string>
|
||||
<string name="hold_to_fast_forward_title">Hold to fast forward</string>
|
||||
<string name="hold_to_fast_forward_summary">Tap and hold on the video to play at 2x speed while held</string>
|
||||
<string name="hold_to_fast_forward_indicator">2x Speed</string>
|
||||
<string name="show_comments_title">Show comments</string>
|
||||
<string name="show_comments_summary">Turn off to hide comments</string>
|
||||
<string name="show_next_and_similar_title">Show \'Next\' and \'Similar\' videos</string>
|
||||
|
||||
@ -249,5 +249,13 @@
|
||||
android:title="@string/ignore_hardware_media_buttons_title"
|
||||
app:singleLineTitle="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="@string/hold_to_fast_forward_key"
|
||||
android:summary="@string/hold_to_fast_forward_summary"
|
||||
android:title="@string/hold_to_fast_forward_title"
|
||||
app:singleLineTitle="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user