diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index edb768524..ac6c9f7bc 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -893,6 +893,10 @@ public float getPlaybackPitch() { return getPlaybackParameters().pitch; } + public void setPlaybackPitch(final float pitch) { + setPlaybackParameters(getPlaybackSpeed(), pitch, getPlaybackSkipSilence()); + } + public boolean getPlaybackSkipSilence() { return !exoPlayerIsNull() && simpleExoPlayer.getSkipSilenceEnabled(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt b/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt index ff0bb269d..f96713633 100644 --- a/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt +++ b/app/src/main/java/org/schabi/newpipe/player/gesture/MainPlayerGestureListener.kt @@ -14,10 +14,13 @@ import org.schabi.newpipe.ktx.AnimationType import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.player.Player import org.schabi.newpipe.player.helper.AudioReactor +import org.schabi.newpipe.player.helper.PlaybackParameterDialog import org.schabi.newpipe.player.helper.PlayerHelper +import org.schabi.newpipe.player.helper.PlayerSemitoneHelper import org.schabi.newpipe.player.ui.MainPlayerUi import org.schabi.newpipe.util.ThemeHelper.getAndroidDimenPx import kotlin.math.abs +import kotlin.math.roundToInt /** * GestureListener for the player @@ -102,6 +105,7 @@ class MainPlayerGestureListener( binding.volumeRelativeLayout.animate(true, 200, AnimationType.SCALE_AND_ALPHA) } binding.brightnessRelativeLayout.isVisible = false + binding.playbackSpeedRelativeLayout.isVisible = false } private fun onScrollBrightness(distanceY: Float) { @@ -147,6 +151,50 @@ class MainPlayerGestureListener( binding.brightnessRelativeLayout.animate(true, 200, AnimationType.SCALE_AND_ALPHA) } binding.volumeRelativeLayout.isVisible = false + binding.playbackSpeedRelativeLayout.isVisible = false + } + + private fun onScrollPlaybackSpeed(distanceY: Float) { + val bar: ProgressBar = binding.playbackSpeedProgressBar + val maxPlaybackSpeed: Float = PlaybackParameterDialog.getMaxPitchOrSpeed() + val minPlaybackSpeed: Float = PlaybackParameterDialog.getMinPitchOrSpeed() + val playbackSpeedStep: Float = PlaybackParameterDialog.getCurrentStepSize(player.context) / maxPlaybackSpeed + + // If we just started sliding, change the progress bar to match the current playback speed + if (!binding.playbackSpeedRelativeLayout.isVisible) { + val playbackSpeedPercent: Float = player.playbackSpeed / maxPlaybackSpeed + bar.progress = (playbackSpeedPercent * bar.max).toInt() + } + + // Update progress bar + bar.incrementProgressBy(distanceY.toInt()) + + // Update playback speed + val currentProgressPercent: Float = (bar.progress / bar.max.toFloat() / playbackSpeedStep).roundToInt() * playbackSpeedStep + val currentPlaybackSpeed: Float = (currentProgressPercent * maxPlaybackSpeed).coerceIn(minPlaybackSpeed, maxPlaybackSpeed) + + player.playbackSpeed = currentPlaybackSpeed + if (!PlaybackParameterDialog.getPlaybackUnhooked(player.context)) { + if (!PlaybackParameterDialog.getPitchControlModeSemitone(player.context)) { + player.playbackPitch = currentPlaybackSpeed + } else { + player.playbackPitch = PlayerSemitoneHelper.semitonesToPercent(PlayerSemitoneHelper.percentToSemitones(currentPlaybackSpeed.toDouble())).toFloat() + } + } + + if (DEBUG) { + Log.d(TAG, "onScroll().playbackSpeedControl, currentPlaybackSpeed = $currentPlaybackSpeed") + } + + // Update player center image + binding.playbackSpeedTextView.text = PlayerHelper.formatSpeed(currentPlaybackSpeed.toDouble()) + + // Make sure the correct layout is visible + if (!binding.playbackSpeedRelativeLayout.isVisible) { + binding.playbackSpeedRelativeLayout.animate(true, 200, AnimationType.SCALE_AND_ALPHA) + } + binding.brightnessRelativeLayout.isVisible = false + binding.volumeRelativeLayout.isVisible = false } override fun onScrollEnd(event: MotionEvent) { @@ -157,6 +205,9 @@ class MainPlayerGestureListener( if (binding.brightnessRelativeLayout.isVisible) { binding.brightnessRelativeLayout.animate(false, 200, AnimationType.SCALE_AND_ALPHA, 200) } + if (binding.playbackSpeedRelativeLayout.isVisible) { + binding.playbackSpeedRelativeLayout.animate(false, 200, AnimationType.SCALE_AND_ALPHA, 200) + } } override fun onScroll( @@ -190,30 +241,42 @@ class MainPlayerGestureListener( isMoving = true - // -- Brightness and Volume control -- - if (getDisplayHalfPortion(initialEvent) == DisplayPortion.RIGHT_HALF) { + // -- Brightness Volume and Tempo control -- + if (getDisplayPortion(initialEvent) == DisplayPortion.RIGHT) { when (PlayerHelper.getActionForRightGestureSide(player.context)) { player.context.getString(R.string.volume_control_key) -> onScrollVolume(distanceY) player.context.getString(R.string.brightness_control_key) -> onScrollBrightness(distanceY) + player.context.getString(R.string.playback_speed_control_key) -> + onScrollPlaybackSpeed(distanceY) } - } else { + } else if (getDisplayPortion(initialEvent) == DisplayPortion.LEFT) { when (PlayerHelper.getActionForLeftGestureSide(player.context)) { player.context.getString(R.string.volume_control_key) -> onScrollVolume(distanceY) player.context.getString(R.string.brightness_control_key) -> onScrollBrightness(distanceY) + player.context.getString(R.string.playback_speed_control_key) -> + onScrollPlaybackSpeed(distanceY) + } + } else { + when (PlayerHelper.getActionForMiddleGestureSide(player.context)) { + player.context.getString(R.string.volume_control_key) -> + onScrollVolume(distanceY) + player.context.getString(R.string.brightness_control_key) -> + onScrollBrightness(distanceY) + player.context.getString(R.string.playback_speed_control_key) -> + onScrollPlaybackSpeed(distanceY) } } - 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 + e.x < binding.root.width * 0.3 -> DisplayPortion.LEFT + e.x > binding.root.width * 0.7 -> DisplayPortion.RIGHT else -> DisplayPortion.MIDDLE } } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java index 7e74c3848..06fc64074 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java @@ -60,7 +60,7 @@ public class PlaybackParameterDialog extends DialogFragment { private static final double DEFAULT_PITCH_PERCENT = 1.00f; private static final double DEFAULT_STEP = STEP_25_PERCENT_VALUE; private static final boolean DEFAULT_SKIP_SILENCE = false; - + private static final boolean DEFAULT_PLAYBACK_UNHOOK = false; private static final SliderStrategy QUADRATIC_STRATEGY = new SliderStrategy.Quadratic( MIN_PITCH_OR_SPEED, MAX_PITCH_OR_SPEED, @@ -261,7 +261,7 @@ private void initUI() { bindCheckboxWithBoolPref( binding.unhookCheckbox, R.string.playback_unhook_key, - true, + DEFAULT_PLAYBACK_UNHOOK, isChecked -> { if (!isChecked) { // when unchecked, slide back to the minimum of current tempo or pitch @@ -590,6 +590,32 @@ private static String getPercentString(final double percent) { return PlayerHelper.formatPitch(percent); } + + public static boolean getPlaybackUnhooked(final Context context) { + return PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(context.getString(R.string.playback_unhook_key), + DEFAULT_PLAYBACK_UNHOOK); + } + + public static boolean getPitchControlModeSemitone(final Context context) { + return PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(context.getString(R.string.playback_adjust_by_semitones_key), + PITCH_CTRL_MODE_PERCENT); + } + + public static float getCurrentStepSize(final Context context) { + return PreferenceManager.getDefaultSharedPreferences(context) + .getFloat(context.getString(R.string.adjustment_step_key), (float) DEFAULT_STEP); + } + + public static float getMinPitchOrSpeed() { + return (float) MIN_PITCH_OR_SPEED; + } + + public static float getMaxPitchOrSpeed() { + return (float) MAX_PITCH_OR_SPEED; + } + public interface Callback { void onPlaybackParameterChanged(float playbackTempo, float playbackPitch, boolean playbackSkipSilence); diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index a110a80d6..6a51bdbea 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -234,6 +234,12 @@ public static String getActionForRightGestureSide(@NonNull final Context context context.getString(R.string.default_right_gesture_control_value)); } + public static String getActionForMiddleGestureSide(@NonNull final Context context) { + return getPreferences(context) + .getString(context.getString(R.string.middle_gesture_control_key), + context.getString(R.string.default_middle_gesture_control_value)); + } + public static String getActionForLeftGestureSide(@NonNull final Context context) { return getPreferences(context) .getString(context.getString(R.string.left_gesture_control_key), diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java index d8efb30df..065f019a9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java @@ -551,6 +551,7 @@ public void onLayoutChange(final View view, final int l, final int t, final int binding.volumeProgressBar.setMax(maxGestureLength); binding.brightnessProgressBar.setMax(maxGestureLength); + binding.playbackSpeedProgressBar.setMax(maxGestureLength); setInitialGestureValues(); binding.itemsListPanel.getLayoutParams().height = diff --git a/app/src/main/res/layout/player.xml b/app/src/main/res/layout/player.xml index bad22dd1e..d491d03a2 100644 --- a/app/src/main/res/layout/player.xml +++ b/app/src/main/res/layout/player.xml @@ -758,6 +758,34 @@ tools:src="@drawable/ic_brightness_high" /> + + + + + + +