From 4b0653658273f456ddbc56a86b1695073d982895 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Fri, 4 Mar 2022 21:37:11 +0100 Subject: [PATCH] Reworked switching to semitones Using an expandable Tab-like component instead of a combobox --- .../schabi/newpipe/local/feed/FeedFragment.kt | 16 +- .../helper/PlaybackParameterDialog.java | 169 ++++++++++++++---- .../schabi/newpipe/util/DrawableResolver.kt | 26 +++ .../res/layout/dialog_playback_parameter.xml | 59 ++++-- app/src/main/res/values/strings.xml | 2 + 5 files changed, 209 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/DrawableResolver.kt diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index e97629f31..e8e78feda 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -25,7 +25,6 @@ import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.graphics.Typeface -import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.os.Parcelable @@ -37,7 +36,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.Button -import androidx.annotation.AttrRes import androidx.annotation.Nullable import androidx.appcompat.app.AlertDialog import androidx.appcompat.content.res.AppCompatResources @@ -77,6 +75,7 @@ import org.schabi.newpipe.local.feed.item.StreamItem import org.schabi.newpipe.local.feed.service.FeedLoadService import org.schabi.newpipe.local.subscription.SubscriptionManager import org.schabi.newpipe.util.DeviceUtils +import org.schabi.newpipe.util.DrawableResolver.Companion.resolveDrawable import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.NavigationHelper import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountStreams @@ -579,19 +578,6 @@ class FeedFragment : BaseStateFragment() { lastNewItemsCount = highlightCount } - private fun resolveDrawable(context: Context, @AttrRes attrResId: Int): Drawable? { - return androidx.core.content.ContextCompat.getDrawable( - context, - android.util.TypedValue().apply { - context.theme.resolveAttribute( - attrResId, - this, - true - ) - }.resourceId - ) - } - private fun showNewItemsLoaded() { tryGetNewItemsLoadedButton()?.clearAnimation() tryGetNewItemsLoadedButton() 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 eab64e483..902222cc5 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 @@ -1,10 +1,14 @@ package org.schabi.newpipe.player.helper; +import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; import static org.schabi.newpipe.player.Player.DEBUG; +import static org.schabi.newpipe.util.DrawableResolver.resolveDrawable; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import android.app.Dialog; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; @@ -22,8 +26,11 @@ import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.DialogPlaybackParameterBinding; +import org.schabi.newpipe.player.Player; import org.schabi.newpipe.util.SliderStrategy; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; import java.util.function.DoubleConsumer; @@ -40,6 +47,9 @@ public class PlaybackParameterDialog extends DialogFragment { private static final double MIN_PLAYBACK_VALUE = 0.10f; private static final double MAX_PLAYBACK_VALUE = 3.00f; + private static final boolean PITCH_CTRL_MODE_PERCENT = false; + private static final boolean PITCH_CTRL_MODE_SEMITONE = true; + private static final double STEP_1_PERCENT_VALUE = 0.01f; private static final double STEP_5_PERCENT_VALUE = 0.05f; private static final double STEP_10_PERCENT_VALUE = 0.10f; @@ -188,6 +198,22 @@ public class PlaybackParameterDialog extends DialogFragment { 1, this::onTempoSliderUpdated); + // Pitch + binding.pitchToogleControlModes.setOnClickListener(v -> { + final boolean isCurrentlyVisible = + binding.pitchControlModeTabs.getVisibility() == View.GONE; + binding.pitchControlModeTabs.setVisibility(isCurrentlyVisible + ? View.VISIBLE + : View.GONE); + animateRotation(binding.pitchToogleControlModes, + Player.DEFAULT_CONTROLS_DURATION, + isCurrentlyVisible ? 180 : 0); + }); + + getPitchControlModeComponentMappings() + .forEach(this::setupPitchControlModeTextView); + changePitchControlMode(isCurrentPitchControlModeSemitone()); + // Pitch - Percent setText(binding.pitchPercentMinimumText, PlayerHelper::formatPitch, MIN_PLAYBACK_VALUE); setText(binding.pitchPercentMaximumText, PlayerHelper::formatPitch, MAX_PLAYBACK_VALUE); @@ -249,13 +275,6 @@ public class PlaybackParameterDialog extends DialogFragment { skipSilence = isChecked; updateCallback(); }); - - bindCheckboxWithBoolPref( - binding.adjustBySemitonesCheckbox, - R.string.playback_adjust_by_semitones_key, - false, - this::showPitchSemitonesOrPercent - ); } private void setText( @@ -291,17 +310,114 @@ public class PlaybackParameterDialog extends DialogFragment { }); } - private void setupStepTextView( - final TextView textView, - final double stepSizeValue + private void setupPitchControlModeTextView( + final boolean semitones, + final TextView textView ) { - setText(textView, PlaybackParameterDialog::getPercentString, stepSizeValue) - .setOnClickListener(view -> setAndUpdateStepSize(stepSizeValue)); + textView.setOnClickListener(view -> { + PreferenceManager.getDefaultSharedPreferences(requireContext()) + .edit() + .putBoolean(getString(R.string.playback_adjust_by_semitones_key), semitones) + .apply(); + + changePitchControlMode(semitones); + }); } - private void setAndUpdateStepSize(final double newStepSize) { - this.stepSize = newStepSize; + private Map getPitchControlModeComponentMappings() { + final Map mappings = new HashMap<>(); + mappings.put(PITCH_CTRL_MODE_PERCENT, binding.pitchControlModePercent); + mappings.put(PITCH_CTRL_MODE_SEMITONE, binding.pitchControlModeSemitone); + return mappings; + } + private void changePitchControlMode(final boolean semitones) { + // Bring all textviews into a normal state + final Map pitchCtrlModeComponentMapping = + getPitchControlModeComponentMappings(); + pitchCtrlModeComponentMapping.forEach((v, textView) -> textView.setBackground( + resolveDrawable(requireContext(), R.attr.selectableItemBackground))); + + // Mark the selected textview + final TextView textView = pitchCtrlModeComponentMapping.get(semitones); + if (textView != null) { + textView.setBackground(new LayerDrawable(new Drawable[]{ + resolveDrawable(requireContext(), R.attr.dashed_border), + resolveDrawable(requireContext(), R.attr.selectableItemBackground) + })); + } + + // Show or hide component + binding.pitchPercentControl.setVisibility(semitones ? View.GONE : View.VISIBLE); + binding.pitchSemitoneControl.setVisibility(semitones ? View.VISIBLE : View.GONE); + + if (semitones) { + // Recalculate pitch percent when changing to semitone + // (as it could be an invalid semitone value) + final double newPitchPercent = calcValidPitch(pitchPercent); + + // If the values differ set the new pitch + if (this.pitchPercent != newPitchPercent) { + if (DEBUG) { + Log.d(TAG, "Bringing pitchPercent to correct corresponding semitone: " + + "currentPitchPercent = " + pitchPercent + ", " + + "newPitchPercent = " + newPitchPercent + ); + } + this.onPitchPercentSliderUpdated(newPitchPercent); + updateCallback(); + } + } + } + + private boolean isCurrentPitchControlModeSemitone() { + return PreferenceManager.getDefaultSharedPreferences(requireContext()) + .getBoolean( + getString(R.string.playback_adjust_by_semitones_key), + PITCH_CTRL_MODE_PERCENT); + } + + private void setupStepTextView( + final double stepSizeValue, + final TextView textView + ) { + setText(textView, PlaybackParameterDialog::getPercentString, stepSizeValue); + textView.setOnClickListener(view -> { + PreferenceManager.getDefaultSharedPreferences(requireContext()) + .edit() + .putFloat(getString(R.string.adjustment_step_key), (float) stepSizeValue) + .apply(); + + setStepSizeToUI(stepSizeValue); + }); + } + + private Map getStepSizeComponentMappings() { + final Map mappings = new HashMap<>(); + mappings.put(STEP_1_PERCENT_VALUE, binding.stepSizeOnePercent); + mappings.put(STEP_5_PERCENT_VALUE, binding.stepSizeFivePercent); + mappings.put(STEP_10_PERCENT_VALUE, binding.stepSizeTenPercent); + mappings.put(STEP_25_PERCENT_VALUE, binding.stepSizeTwentyFivePercent); + mappings.put(STEP_100_PERCENT_VALUE, binding.stepSizeOneHundredPercent); + return mappings; + } + + private void setStepSizeToUI(final double newStepSize) { + // Bring all textviews into a normal state + final Map stepSiteComponentMapping = getStepSizeComponentMappings(); + stepSiteComponentMapping.forEach((v, textView) -> textView.setBackground( + resolveDrawable(requireContext(), R.attr.selectableItemBackground))); + + // Mark the selected textview + final TextView textView = stepSiteComponentMapping.get(newStepSize); + if (textView != null) { + textView.setBackground(new LayerDrawable(new Drawable[]{ + resolveDrawable(requireContext(), R.attr.dashed_border), + resolveDrawable(requireContext(), R.attr.selectableItemBackground) + })); + } + + // Bind to the corresponding control components binding.tempoStepUp.setText(getStepUpPercentString(newStepSize)); binding.tempoStepDown.setText(getStepDownPercentString(newStepSize)); @@ -345,29 +461,6 @@ public class PlaybackParameterDialog extends DialogFragment { }); } - private void showPitchSemitonesOrPercent(final boolean semitones) { - binding.pitchPercentControl.setVisibility(semitones ? View.GONE : View.VISIBLE); - binding.pitchSemitoneControl.setVisibility(semitones ? View.VISIBLE : View.GONE); - - if (semitones) { - // Recalculate pitch percent when changing to semitone - // (as it could be an invalid semitone value) - final double newPitchPercent = calcValidPitch(pitchPercent); - - // If the values differ set the new pitch - if (this.pitchPercent != newPitchPercent) { - if (DEBUG) { - Log.d(TAG, "Bringing pitchPercent to correct corresponding semitone: " - + "currentPitchPercent = " + pitchPercent + ", " - + "newPitchPercent = " + newPitchPercent - ); - } - this.onPitchPercentSliderUpdated(newPitchPercent); - updateCallback(); - } - } - } - /*////////////////////////////////////////////////////////////////////////// // Sliders //////////////////////////////////////////////////////////////////////////*/ @@ -447,7 +540,7 @@ public class PlaybackParameterDialog extends DialogFragment { final double calcPitch = Math.max(MIN_PLAYBACK_VALUE, Math.min(MAX_PLAYBACK_VALUE, newPitch)); - if (!binding.adjustBySemitonesCheckbox.isChecked()) { + if (!isCurrentPitchControlModeSemitone()) { return calcPitch; } diff --git a/app/src/main/java/org/schabi/newpipe/util/DrawableResolver.kt b/app/src/main/java/org/schabi/newpipe/util/DrawableResolver.kt new file mode 100644 index 000000000..50f875257 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/DrawableResolver.kt @@ -0,0 +1,26 @@ +package org.schabi.newpipe.util + +import android.content.Context +import android.graphics.drawable.Drawable +import androidx.annotation.AttrRes + +/** + * Utility class for resolving [Drawables](Drawable) + */ +class DrawableResolver { + companion object { + @JvmStatic + fun resolveDrawable(context: Context, @AttrRes attrResId: Int): Drawable? { + return androidx.core.content.ContextCompat.getDrawable( + context, + android.util.TypedValue().apply { + context.theme.resolveAttribute( + attrResId, + this, + true + ) + }.resourceId + ) + } + } +} diff --git a/app/src/main/res/layout/dialog_playback_parameter.xml b/app/src/main/res/layout/dialog_playback_parameter.xml index 640475f39..e402f4fb1 100644 --- a/app/src/main/res/layout/dialog_playback_parameter.xml +++ b/app/src/main/res/layout/dialog_playback_parameter.xml @@ -146,11 +146,59 @@ android:textColor="?attr/colorAccent" android:textStyle="bold" /> + + + + + + + + + + - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 792e6414b..af2921cca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -501,6 +501,8 @@ Step Tempo step Reset + Percent + Semitone In order to comply with the European General Data Protection Regulation (GDPR), we hereby draw your attention to NewPipe\'s privacy policy. Please read it carefully. \nYou must accept it to send us the bug report.