feat: Playback speed controls for music (#376)

Enable playback speed controls for music

Button is moved to the top left, next to bit rate, because it would
overlap with the "shuffle" button.
The speed rotation logic was cleaned up to 0.25x increments without all
the hard-coded constants and code duplication.

Co-authored-by: eddyizm <eddyizm@gmail.com>
This commit is contained in:
Pascal Grittmann 2026-01-24 17:39:57 +01:00 committed by GitHub
parent 07b507691c
commit d16a9c234f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 26 additions and 53 deletions

View file

@ -354,7 +354,7 @@ public class MainActivity extends BaseActivity {
// TODO Enter all settings to be reset // TODO Enter all settings to be reset
Preferences.setOpenSubsonic(false); Preferences.setOpenSubsonic(false);
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_100); Preferences.setPlaybackSpeed(1.0f);
Preferences.setSkipSilenceMode(false); Preferences.setSkipSilenceMode(false);
Preferences.setDataSavingMode(false); Preferences.setDataSavingMode(false);
Preferences.setStarredSyncEnabled(false); Preferences.setStarredSyncEnabled(false);

View file

@ -413,10 +413,10 @@ public class PlayerControllerFragment extends Fragment {
bind.getRoot().setShowNextButton(true); bind.getRoot().setShowNextButton(true);
bind.getRoot().setShowFastForwardButton(false); bind.getRoot().setShowFastForwardButton(false);
bind.getRoot().setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL | RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE); bind.getRoot().setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_ALL | RepeatModeUtil.REPEAT_TOGGLE_MODE_ONE);
bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.GONE); bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.VISIBLE);
bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button).setVisibility(View.GONE); bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button).setVisibility(View.GONE);
bind.getRoot().findViewById(R.id.button_favorite).setVisibility(View.VISIBLE); bind.getRoot().findViewById(R.id.button_favorite).setVisibility(View.VISIBLE);
resetPlaybackParameters(mediaBrowser); setPlaybackParameters(mediaBrowser);
break; break;
} }
} }
@ -524,31 +524,11 @@ public class PlayerControllerFragment extends Fragment {
playbackSpeedButton.setOnClickListener(view -> { playbackSpeedButton.setOnClickListener(view -> {
float currentSpeed = Preferences.getPlaybackSpeed(); float currentSpeed = Preferences.getPlaybackSpeed();
if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_080) { currentSpeed += 0.25f;
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_100)); if (currentSpeed > 2.0f) currentSpeed = 0.5f;
playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_100)); mediaBrowser.setPlaybackParameters(new PlaybackParameters(currentSpeed));
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_100); playbackSpeedButton.setText(getString(R.string.player_playback_speed, currentSpeed));
} else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_100) { Preferences.setPlaybackSpeed(currentSpeed);
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_125));
playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_125));
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_125);
} else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_125) {
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_150));
playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_150));
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_150);
} else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_150) {
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_175));
playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_175));
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_175);
} else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_175) {
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_200));
playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_200));
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_200);
} else if (currentSpeed == Constants.MEDIA_PLAYBACK_SPEED_200) {
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_080));
playbackSpeedButton.setText(getString(R.string.player_playback_speed, Constants.MEDIA_PLAYBACK_SPEED_080));
Preferences.setPlaybackSpeed(Constants.MEDIA_PLAYBACK_SPEED_080);
}
}); });
skipSilenceToggleButton.setOnClickListener(view -> { skipSilenceToggleButton.setOnClickListener(view -> {
@ -600,7 +580,7 @@ public class PlayerControllerFragment extends Fragment {
} }
private void resetPlaybackParameters(MediaBrowser mediaBrowser) { private void resetPlaybackParameters(MediaBrowser mediaBrowser) {
mediaBrowser.setPlaybackParameters(new PlaybackParameters(Constants.MEDIA_PLAYBACK_SPEED_100)); mediaBrowser.setPlaybackParameters(new PlaybackParameters(1.0f));
// TODO Resettare lo skip del silenzio // TODO Resettare lo skip del silenzio
} }

View file

@ -61,13 +61,6 @@ object Constants {
const val MEDIA_TYPE_VIDEO = "video" const val MEDIA_TYPE_VIDEO = "video"
const val MEDIA_TYPE_RADIO = "radio" const val MEDIA_TYPE_RADIO = "radio"
const val MEDIA_PLAYBACK_SPEED_080 = 0.8f
const val MEDIA_PLAYBACK_SPEED_100 = 1.0f
const val MEDIA_PLAYBACK_SPEED_125 = 1.25f
const val MEDIA_PLAYBACK_SPEED_150 = 1.50f
const val MEDIA_PLAYBACK_SPEED_175 = 1.75f
const val MEDIA_PLAYBACK_SPEED_200 = 2.0f
const val MEDIA_RECENTLY_PLAYED = "MEDIA_RECENTLY_PLAYED" const val MEDIA_RECENTLY_PLAYED = "MEDIA_RECENTLY_PLAYED"
const val MEDIA_MOST_PLAYED = "MEDIA_MOST_PLAYED" const val MEDIA_MOST_PLAYED = "MEDIA_MOST_PLAYED"
const val MEDIA_RECENTLY_ADDED = "MEDIA_RECENTLY_ADDED" const val MEDIA_RECENTLY_ADDED = "MEDIA_RECENTLY_ADDED"

View file

@ -16,6 +16,23 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/player_playback_speed_button"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="80dp"
android:layout_height="48dp"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
app:cornerRadius="30dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tint="?attr/colorOnPrimaryContainer" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/player_media_extension" android:id="@+id/player_media_extension"
style="@style/Widget.Material3.Chip.Suggestion" style="@style/Widget.Material3.Chip.Suggestion"
@ -253,23 +270,6 @@
app:layout_constraintStart_toEndOf="@+id/placeholder_view_middle_right" app:layout_constraintStart_toEndOf="@+id/placeholder_view_middle_right"
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_right" /> app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_right" />
<Button
android:id="@+id/player_playback_speed_button"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="24dp"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
app:cornerRadius="30dp"
app:layout_constraintBottom_toBottomOf="@+id/placeholder_view_middle_left"
app:layout_constraintEnd_toStartOf="@+id/placeholder_view_middle_left"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_left"
app:tint="?attr/colorOnPrimaryContainer" />
<ImageButton <ImageButton
android:id="@+id/exo_shuffle" android:id="@+id/exo_shuffle"
android:layout_width="32dp" android:layout_width="32dp"