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" /> + + + + + + + Demnächst Vollständig angeschaut Teilweise angeschaut - Geste für die linke Hälfte des Player-Bildschirms auswählen - Geste für die rechte Hälfte des Player-Bildschirms auswählen + Geste für den linken Teil des Player-Bildschirms auswählen + Mittlere Gestenaktion + Geste für den rechten Teil des Player-Bildschirms auswählen Keine Rechte Gestenaktion Linke Gestenaktion @@ -826,4 +827,5 @@ \nMöchtest du wirklich fortfahren? Die Einstellungen in dem zu importierenden Export verwenden ein angreifbares Format, das seit NewPipe 0.27.0 veraltet ist. Stellen Sie sicher, dass der zu importierende Export aus einer vertrauenswürdigen Quelle stammt, und verwenden Sie in Zukunft nur noch Exporte, die aus NewPipe 0.27.0 oder neuer stammen. Die Unterstützung für den Import von Einstellungen in diesem angreifbaren Format wird bald vollständig entfernt werden, und dann werden alte Versionen von NewPipe nicht mehr in der Lage sein, Einstellungen von Exporten aus neuen Versionen zu importieren. Sekundär + Geste für den mittleren Teil des Player-Bildschirms auswählen diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 6858e5d62..693166d32 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -204,28 +204,48 @@ @string/brightness_control_key brightness_control volume_control + playback_speed_control none_control @string/brightness @string/volume + @string/playback_tempo @string/none @string/brightness_control_key @string/volume_control_key + @string/playback_speed_control_key + @string/none_control_key + + + middle_gesture_control + @string/playback_speed_control_key + + @string/brightness + @string/volume + @string/playback_tempo + @string/none + + + @string/brightness_control_key + @string/volume_control_key + @string/playback_speed_control_key @string/none_control_key right_gesture_control @string/volume_control_key - @string/volume @string/brightness + @string/volume + @string/playback_tempo @string/none - @string/volume_control_key @string/brightness_control_key + @string/volume_control_key + @string/playback_speed_control_key @string/none_control_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8022bc2f8..c46b8b1ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,9 +106,11 @@ Auto-enqueue next stream Continue ending (non-repeating) playback queue by appending a related stream Auto-enqueuing - Choose gesture for left half of player screen + Choose gesture for left part of player screen Left gesture action - Choose gesture for right half of player screen + Choose gesture for middle part of player screen + Middle gesture action + Choose gesture for right part of player screen Right gesture action Brightness Volume diff --git a/app/src/main/res/xml/video_audio_settings.xml b/app/src/main/res/xml/video_audio_settings.xml index 727ce4df4..3d13e0b71 100644 --- a/app/src/main/res/xml/video_audio_settings.xml +++ b/app/src/main/res/xml/video_audio_settings.xml @@ -198,6 +198,16 @@ app:singleLineTitle="false" app:iconSpaceReserved="false" /> + +