11package org .schabi .newpipe .player .helper ;
22
3+ import static org .schabi .newpipe .ktx .ViewUtils .animateRotation ;
34import static org .schabi .newpipe .player .Player .DEBUG ;
5+ import static org .schabi .newpipe .util .DrawableResolver .resolveDrawable ;
46import static org .schabi .newpipe .util .Localization .assureCorrectAppLanguage ;
57
68import android .app .Dialog ;
79import android .content .Context ;
10+ import android .graphics .drawable .Drawable ;
11+ import android .graphics .drawable .LayerDrawable ;
812import android .os .Bundle ;
913import android .util .Log ;
1014import android .view .LayoutInflater ;
2226
2327import org .schabi .newpipe .R ;
2428import org .schabi .newpipe .databinding .DialogPlaybackParameterBinding ;
29+ import org .schabi .newpipe .player .Player ;
2530import org .schabi .newpipe .util .SliderStrategy ;
2631
32+ import java .util .HashMap ;
33+ import java .util .Map ;
2734import java .util .Objects ;
2835import java .util .function .Consumer ;
2936import java .util .function .DoubleConsumer ;
@@ -40,6 +47,9 @@ public class PlaybackParameterDialog extends DialogFragment {
4047 private static final double MIN_PLAYBACK_VALUE = 0.10f ;
4148 private static final double MAX_PLAYBACK_VALUE = 3.00f ;
4249
50+ private static final boolean PITCH_CTRL_MODE_PERCENT = false ;
51+ private static final boolean PITCH_CTRL_MODE_SEMITONE = true ;
52+
4353 private static final double STEP_1_PERCENT_VALUE = 0.01f ;
4454 private static final double STEP_5_PERCENT_VALUE = 0.05f ;
4555 private static final double STEP_10_PERCENT_VALUE = 0.10f ;
@@ -188,6 +198,22 @@ private void initUI() {
188198 1 ,
189199 this ::onTempoSliderUpdated );
190200
201+ // Pitch
202+ binding .pitchToogleControlModes .setOnClickListener (v -> {
203+ final boolean isCurrentlyVisible =
204+ binding .pitchControlModeTabs .getVisibility () == View .GONE ;
205+ binding .pitchControlModeTabs .setVisibility (isCurrentlyVisible
206+ ? View .VISIBLE
207+ : View .GONE );
208+ animateRotation (binding .pitchToogleControlModes ,
209+ Player .DEFAULT_CONTROLS_DURATION ,
210+ isCurrentlyVisible ? 180 : 0 );
211+ });
212+
213+ getPitchControlModeComponentMappings ()
214+ .forEach (this ::setupPitchControlModeTextView );
215+ changePitchControlMode (isCurrentPitchControlModeSemitone ());
216+
191217 // Pitch - Percent
192218 setText (binding .pitchPercentMinimumText , PlayerHelper ::formatPitch , MIN_PLAYBACK_VALUE );
193219 setText (binding .pitchPercentMaximumText , PlayerHelper ::formatPitch , MAX_PLAYBACK_VALUE );
@@ -249,13 +275,6 @@ private void initUI() {
249275 skipSilence = isChecked ;
250276 updateCallback ();
251277 });
252-
253- bindCheckboxWithBoolPref (
254- binding .adjustBySemitonesCheckbox ,
255- R .string .playback_adjust_by_semitones_key ,
256- false ,
257- this ::showPitchSemitonesOrPercent
258- );
259278 }
260279
261280 private void setText (
@@ -291,17 +310,114 @@ private void registerOnSemitoneStepClickListener(
291310 });
292311 }
293312
313+ private void setupPitchControlModeTextView (
314+ final boolean semitones ,
315+ final TextView textView
316+ ) {
317+ textView .setOnClickListener (view -> {
318+ PreferenceManager .getDefaultSharedPreferences (requireContext ())
319+ .edit ()
320+ .putBoolean (getString (R .string .playback_adjust_by_semitones_key ), semitones )
321+ .apply ();
322+
323+ changePitchControlMode (semitones );
324+ });
325+ }
326+
327+ private Map <Boolean , TextView > getPitchControlModeComponentMappings () {
328+ final Map <Boolean , TextView > mappings = new HashMap <>();
329+ mappings .put (PITCH_CTRL_MODE_PERCENT , binding .pitchControlModePercent );
330+ mappings .put (PITCH_CTRL_MODE_SEMITONE , binding .pitchControlModeSemitone );
331+ return mappings ;
332+ }
333+
334+ private void changePitchControlMode (final boolean semitones ) {
335+ // Bring all textviews into a normal state
336+ final Map <Boolean , TextView > pitchCtrlModeComponentMapping =
337+ getPitchControlModeComponentMappings ();
338+ pitchCtrlModeComponentMapping .forEach ((v , textView ) -> textView .setBackground (
339+ resolveDrawable (requireContext (), R .attr .selectableItemBackground )));
340+
341+ // Mark the selected textview
342+ final TextView textView = pitchCtrlModeComponentMapping .get (semitones );
343+ if (textView != null ) {
344+ textView .setBackground (new LayerDrawable (new Drawable []{
345+ resolveDrawable (requireContext (), R .attr .dashed_border ),
346+ resolveDrawable (requireContext (), R .attr .selectableItemBackground )
347+ }));
348+ }
349+
350+ // Show or hide component
351+ binding .pitchPercentControl .setVisibility (semitones ? View .GONE : View .VISIBLE );
352+ binding .pitchSemitoneControl .setVisibility (semitones ? View .VISIBLE : View .GONE );
353+
354+ if (semitones ) {
355+ // Recalculate pitch percent when changing to semitone
356+ // (as it could be an invalid semitone value)
357+ final double newPitchPercent = calcValidPitch (pitchPercent );
358+
359+ // If the values differ set the new pitch
360+ if (this .pitchPercent != newPitchPercent ) {
361+ if (DEBUG ) {
362+ Log .d (TAG , "Bringing pitchPercent to correct corresponding semitone: "
363+ + "currentPitchPercent = " + pitchPercent + ", "
364+ + "newPitchPercent = " + newPitchPercent
365+ );
366+ }
367+ this .onPitchPercentSliderUpdated (newPitchPercent );
368+ updateCallback ();
369+ }
370+ }
371+ }
372+
373+ private boolean isCurrentPitchControlModeSemitone () {
374+ return PreferenceManager .getDefaultSharedPreferences (requireContext ())
375+ .getBoolean (
376+ getString (R .string .playback_adjust_by_semitones_key ),
377+ PITCH_CTRL_MODE_PERCENT );
378+ }
379+
294380 private void setupStepTextView (
295- final TextView textView ,
296- final double stepSizeValue
381+ final double stepSizeValue ,
382+ final TextView textView
297383 ) {
298- setText (textView , PlaybackParameterDialog ::getPercentString , stepSizeValue )
299- .setOnClickListener (view -> setAndUpdateStepSize (stepSizeValue ));
384+ setText (textView , PlaybackParameterDialog ::getPercentString , stepSizeValue );
385+ textView .setOnClickListener (view -> {
386+ PreferenceManager .getDefaultSharedPreferences (requireContext ())
387+ .edit ()
388+ .putFloat (getString (R .string .adjustment_step_key ), (float ) stepSizeValue )
389+ .apply ();
390+
391+ setStepSizeToUI (stepSizeValue );
392+ });
300393 }
301394
302- private void setAndUpdateStepSize (final double newStepSize ) {
303- this .stepSize = newStepSize ;
395+ private Map <Double , TextView > getStepSizeComponentMappings () {
396+ final Map <Double , TextView > mappings = new HashMap <>();
397+ mappings .put (STEP_1_PERCENT_VALUE , binding .stepSizeOnePercent );
398+ mappings .put (STEP_5_PERCENT_VALUE , binding .stepSizeFivePercent );
399+ mappings .put (STEP_10_PERCENT_VALUE , binding .stepSizeTenPercent );
400+ mappings .put (STEP_25_PERCENT_VALUE , binding .stepSizeTwentyFivePercent );
401+ mappings .put (STEP_100_PERCENT_VALUE , binding .stepSizeOneHundredPercent );
402+ return mappings ;
403+ }
404+
405+ private void setStepSizeToUI (final double newStepSize ) {
406+ // Bring all textviews into a normal state
407+ final Map <Double , TextView > stepSiteComponentMapping = getStepSizeComponentMappings ();
408+ stepSiteComponentMapping .forEach ((v , textView ) -> textView .setBackground (
409+ resolveDrawable (requireContext (), R .attr .selectableItemBackground )));
410+
411+ // Mark the selected textview
412+ final TextView textView = stepSiteComponentMapping .get (newStepSize );
413+ if (textView != null ) {
414+ textView .setBackground (new LayerDrawable (new Drawable []{
415+ resolveDrawable (requireContext (), R .attr .dashed_border ),
416+ resolveDrawable (requireContext (), R .attr .selectableItemBackground )
417+ }));
418+ }
304419
420+ // Bind to the corresponding control components
305421 binding .tempoStepUp .setText (getStepUpPercentString (newStepSize ));
306422 binding .tempoStepDown .setText (getStepDownPercentString (newStepSize ));
307423
@@ -345,29 +461,6 @@ private void bindCheckboxWithBoolPref(
345461 });
346462 }
347463
348- private void showPitchSemitonesOrPercent (final boolean semitones ) {
349- binding .pitchPercentControl .setVisibility (semitones ? View .GONE : View .VISIBLE );
350- binding .pitchSemitoneControl .setVisibility (semitones ? View .VISIBLE : View .GONE );
351-
352- if (semitones ) {
353- // Recalculate pitch percent when changing to semitone
354- // (as it could be an invalid semitone value)
355- final double newPitchPercent = calcValidPitch (pitchPercent );
356-
357- // If the values differ set the new pitch
358- if (this .pitchPercent != newPitchPercent ) {
359- if (DEBUG ) {
360- Log .d (TAG , "Bringing pitchPercent to correct corresponding semitone: "
361- + "currentPitchPercent = " + pitchPercent + ", "
362- + "newPitchPercent = " + newPitchPercent
363- );
364- }
365- this .onPitchPercentSliderUpdated (newPitchPercent );
366- updateCallback ();
367- }
368- }
369- }
370-
371464 /*//////////////////////////////////////////////////////////////////////////
372465 // Sliders
373466 //////////////////////////////////////////////////////////////////////////*/
@@ -447,7 +540,7 @@ private double calcValidPitch(final double newPitch) {
447540 final double calcPitch =
448541 Math .max (MIN_PLAYBACK_VALUE , Math .min (MAX_PLAYBACK_VALUE , newPitch ));
449542
450- if (!binding . adjustBySemitonesCheckbox . isChecked ()) {
543+ if (!isCurrentPitchControlModeSemitone ()) {
451544 return calcPitch ;
452545 }
453546
0 commit comments