diff options
Diffstat (limited to 'app')
14 files changed, 373 insertions, 214 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java index 6741cbc86..ddce8b1e3 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java @@ -7,7 +7,6 @@ import android.preference.PreferenceManager; import androidx.annotation.StringRes; import androidx.test.filters.LargeTest; import androidx.test.rule.ActivityTestRule; -import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; @@ -31,7 +30,6 @@ import java.util.concurrent.TimeUnit; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.action.ViewActions.replaceText; -import static androidx.test.espresso.action.ViewActions.scrollTo; import static androidx.test.espresso.action.ViewActions.swipeDown; import static androidx.test.espresso.action.ViewActions.swipeUp; import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; @@ -46,7 +44,6 @@ import static de.test.antennapod.EspressoTestUtils.clickPreference; import static de.test.antennapod.EspressoTestUtils.waitForView; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static junit.framework.TestCase.assertTrue; -import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.not; @@ -235,12 +232,10 @@ public class PreferencesTest { @Test public void testPlaybackSpeeds() { clickPreference(R.string.playback_pref); - clickPreference(R.string.media_player); - onView(withText(R.string.media_player_exoplayer)).perform(click()); - clickPreference(R.string.pref_playback_speed_title); - onView(isRoot()).perform(waitForView(withText("0.50"), 1000)); - onView(withText("0.50")).check(matches(isDisplayed())); - onView(withText(R.string.cancel_label)).perform(click()); + clickPreference(R.string.playback_speed); + onView(isRoot()).perform(waitForView(withText("0.75"), 1000)); + onView(withText("0.75")).check(matches(isDisplayed())); + onView(withText(R.string.close_label)).perform(click()); } @Test diff --git a/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java index 79b8b29a4..ac5887069 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java @@ -25,6 +25,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -70,7 +71,7 @@ public class SpeedChangeTest { List<FeedItem> queue = DBReader.getQueue(); PlaybackPreferences.writeMediaPlaying(queue.get(0).getMedia(), PlayerStatus.PAUSED, false); availableSpeeds = new String[] {"1.00", "2.00", "3.00"}; - UserPreferences.setPlaybackSpeedArray(availableSpeeds); + UserPreferences.setPlaybackSpeedArray(Arrays.asList(1.0f, 2.0f, 3.0f)); EspressoTestUtils.tryKillPlaybackService(); activityRule.launchActivity(new Intent().putExtra(MainActivity.EXTRA_OPEN_PLAYER, true)); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java index 61cbcbb01..b03d1e5cd 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -376,8 +376,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements new SleepTimerDialog().show(getSupportFragmentManager(), "SleepTimerDialog"); break; case R.id.audio_controls: - boolean isPlayingVideo = controller.getMedia().getMediaType() == MediaType.VIDEO; - PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(isPlayingVideo); + PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(); dialog.show(getSupportFragmentManager(), "playback_controls"); break; case R.id.open_feed_item: diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java index d1ffdc148..e45533826 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java @@ -11,27 +11,21 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.SeekBar; import android.widget.TextView; -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.Converter; -import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.view.PlaybackSpeedSeekBar; import java.util.List; import java.util.Locale; public class PlaybackControlsDialog extends DialogFragment { - private static final String ARGUMENT_IS_PLAYING_VIDEO = "isPlayingVideo"; - private PlaybackController controller; private AlertDialog dialog; - private boolean isPlayingVideo; - public static PlaybackControlsDialog newInstance(boolean isPlayingVideo) { + public static PlaybackControlsDialog newInstance() { Bundle arguments = new Bundle(); - arguments.putBoolean(ARGUMENT_IS_PLAYING_VIDEO, isPlayingVideo); PlaybackControlsDialog dialog = new PlaybackControlsDialog(); dialog.setArguments(arguments); return dialog; @@ -65,8 +59,6 @@ public class PlaybackControlsDialog extends DialogFragment { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - isPlayingVideo = getArguments() != null && getArguments().getBoolean(ARGUMENT_IS_PLAYING_VIDEO); - dialog = new AlertDialog.Builder(getContext()) .setTitle(R.string.audio_controls) .setView(R.layout.audio_controls) @@ -79,68 +71,18 @@ public class PlaybackControlsDialog extends DialogFragment { } private void setupUi() { - final SeekBar barPlaybackSpeed = dialog.findViewById(R.id.playback_speed); - final TextView butDecSpeed = dialog.findViewById(R.id.butDecSpeed); - butDecSpeed.setOnClickListener(v -> { - if (controller != null && controller.canSetPlaybackSpeed()) { - barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2); - } else { - VariableSpeedDialog.showGetPluginDialog(getContext()); - } - }); - final TextView butIncSpeed = dialog.findViewById(R.id.butIncSpeed); - butIncSpeed.setOnClickListener(v -> { - if (controller != null && controller.canSetPlaybackSpeed()) { - barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2); - } else { - VariableSpeedDialog.showGetPluginDialog(getContext()); - } - }); - final TextView txtvPlaybackSpeed = dialog.findViewById(R.id.txtvPlaybackSpeed); - float currentSpeed = getCurrentSpeed(); - txtvPlaybackSpeed.setText(String.format(Locale.getDefault(), "%.2fx", currentSpeed)); - barPlaybackSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (controller != null && controller.canSetPlaybackSpeed()) { - float playbackSpeed = (progress + 10) / 20.0f; - controller.setPlaybackSpeed(playbackSpeed); - - PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(playbackSpeed); - if (isPlayingVideo) { - UserPreferences.setVideoPlaybackSpeed(playbackSpeed); - } else { - UserPreferences.setPlaybackSpeed(playbackSpeed); - } - - String speedStr = String.format(Locale.getDefault(), "%.2fx", playbackSpeed); - txtvPlaybackSpeed.setText(speedStr); - } else if (fromUser) { - float speed = getCurrentSpeed(); - barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress(Math.round((20 * speed) - 10))); - } - } + PlaybackSpeedSeekBar speedSeekBar = dialog.findViewById(R.id.speed_seek_bar); + speedSeekBar.setController(controller); + speedSeekBar.setProgressChangedListener(speed + -> txtvPlaybackSpeed.setText(String.format(Locale.getDefault(), "%.2fx", speed))); - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - if (controller != null && !controller.canSetPlaybackSpeed()) { - VariableSpeedDialog.showGetPluginDialog(getContext()); - } - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - barPlaybackSpeed.setProgress(Math.round((20 * currentSpeed) - 10)); - - final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left); + final SeekBar barLeftVolume = dialog.findViewById(R.id.volume_left); barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage()); - final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right); + final SeekBar barRightVolume = dialog.findViewById(R.id.volume_right); barRightVolume.setProgress(UserPreferences.getRightVolumePercentage()); - final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono); + final CheckBox stereoToMono = dialog.findViewById(R.id.stereo_to_mono); stereoToMono.setChecked(UserPreferences.stereoToMono()); if (controller != null && !controller.canDownmix()) { stereoToMono.setEnabled(false); @@ -220,13 +162,4 @@ public class PlaybackControlsDialog extends DialogFragment { new Handler().postDelayed(this::setupAudioTracks, 500); }); } - - private float getCurrentSpeed() { - Playable media = null; - if (controller != null) { - media = controller.getMedia(); - } - - return PlaybackSpeedUtils.getCurrentPlaybackSpeed(media); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index 3a6ba183f..ef8ed335d 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -1,97 +1,171 @@ package de.danoeh.antennapod.dialog; +import android.app.Dialog; import android.content.Context; -import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.chip.Chip; +import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.view.ItemOffsetDecoration; +import de.danoeh.antennapod.view.PlaybackSpeedSeekBar; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; -public class VariableSpeedDialog { +public class VariableSpeedDialog extends DialogFragment { + private SpeedSelectionAdapter adapter; + private final DecimalFormat speedFormat; + private PlaybackController controller; + private final List<Float> selectedSpeeds; + private PlaybackSpeedSeekBar speedSeekBar; + private Chip addCurrentSpeedChip; - private VariableSpeedDialog() { - } - - public static void showDialog(final Context context) { - if (UserPreferences.useSonic() - || UserPreferences.useExoplayer() - || Build.VERSION.SDK_INT >= 23) { - showSpeedSelectorDialog(context); - } else { - showGetPluginDialog(context, true); - } + public VariableSpeedDialog() { + DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US); + format.setDecimalSeparator('.'); + speedFormat = new DecimalFormat("0.00", format); + selectedSpeeds = new ArrayList<>(UserPreferences.getPlaybackSpeedArray()); } public static void showGetPluginDialog(final Context context) { - showGetPluginDialog(context, false); - } - - private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.no_playback_plugin_title); builder.setMessage(R.string.no_playback_plugin_or_sonic_msg); builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> { UserPreferences.enableSonic(); - if (showSpeedSelector) { - showSpeedSelectorDialog(context); - } }); builder.setNeutralButton(R.string.close_label, null); builder.show(); } - private static void showSpeedSelectorDialog(final Context context) { - DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US); - format.setDecimalSeparator('.'); - DecimalFormat speedFormat = new DecimalFormat("0.00", format); - - final String[] speedValues = context.getResources().getStringArray( - R.array.playback_speed_values); - // According to Java spec these get initialized to false on creation - final boolean[] speedChecked = new boolean[speedValues.length]; - - // Build the "isChecked" array so that multiChoice dialog is populated correctly - List<String> selectedSpeedList = new ArrayList<>(); - float[] selectedSpeeds = UserPreferences.getPlaybackSpeedArray(); - for (float speed : selectedSpeeds) { - selectedSpeedList.add(speedFormat.format(speed)); + @Override + public void onStart() { + super.onStart(); + controller = new PlaybackController(getActivity()) { + @Override + public void setupGUI() { + updateSpeed(); + } + + @Override + public void onPlaybackSpeedChange() { + updateSpeed(); + } + }; + controller.init(); + speedSeekBar.setController(controller); + } + + private void updateSpeed() { + speedSeekBar.updateSpeed(); + addCurrentSpeedChip.setText(speedFormat.format(controller.getCurrentPlaybackSpeedMultiplier())); + } + + @Override + public void onStop() { + super.onStop(); + controller.release(); + controller = null; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setPositiveButton(R.string.close_label, null); + + View root = View.inflate(getContext(), R.layout.speed_select_dialog, null); + speedSeekBar = root.findViewById(R.id.speed_seek_bar); + RecyclerView selectedSpeedsGrid = root.findViewById(R.id.selected_speeds_grid); + selectedSpeedsGrid.setLayoutManager(new GridLayoutManager(getContext(), 3)); + selectedSpeedsGrid.addItemDecoration(new ItemOffsetDecoration(getContext(), 4)); + adapter = new SpeedSelectionAdapter(); + adapter.setHasStableIds(true); + selectedSpeedsGrid.setAdapter(adapter); + + addCurrentSpeedChip = root.findViewById(R.id.add_current_speed_chip); + addCurrentSpeedChip.setCloseIconVisible(true); + addCurrentSpeedChip.setCloseIconResource(R.drawable.ic_add_black); + addCurrentSpeedChip.setOnCloseIconClickListener(v -> addCurrentSpeed()); + addCurrentSpeedChip.setOnClickListener(v -> addCurrentSpeed()); + + builder.setView(root); + return builder.create(); + } + + private void addCurrentSpeed() { + float newSpeed = controller.getCurrentPlaybackSpeedMultiplier(); + if (selectedSpeeds.contains(newSpeed)) { + Snackbar.make(addCurrentSpeedChip, + getString(R.string.preset_already_exists, newSpeed), Snackbar.LENGTH_LONG).show(); + } else { + selectedSpeeds.add(newSpeed); + Collections.sort(selectedSpeeds); + UserPreferences.setPlaybackSpeedArray(selectedSpeeds); + adapter.notifyDataSetChanged(); } + } - for (int i = 0; i < speedValues.length; i++) { - speedChecked[i] = selectedSpeedList.contains(speedValues[i]); + public class SpeedSelectionAdapter extends RecyclerView.Adapter<SpeedSelectionAdapter.ViewHolder> { + + @Override + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + Chip chip = new Chip(getContext()); + chip.setCloseIconVisible(true); + chip.setCloseIconResource(R.drawable.ic_delete_black); + return new ViewHolder(chip); } - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.set_playback_speed_label); - builder.setMultiChoiceItems(R.array.playback_speed_values, - speedChecked, (dialog, which, isChecked) -> speedChecked[which] = isChecked); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setPositiveButton(android.R.string.ok, - (dialog, which) -> { - int choiceCount = 0; - for (boolean checked : speedChecked) { - if (checked) { - choiceCount++; - } - } - String[] newSpeedValues = new String[choiceCount]; - int newSpeedIndex = 0; - for (int i = 0; i < speedChecked.length; i++) { - if (speedChecked[i]) { - newSpeedValues[newSpeedIndex++] = speedValues[i]; - } + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + float speed = selectedSpeeds.get(position); + + holder.chip.setText(speedFormat.format(speed)); + holder.chip.setOnCloseIconClickListener(v -> { + selectedSpeeds.remove(speed); + UserPreferences.setPlaybackSpeedArray(selectedSpeeds); + notifyDataSetChanged(); + }); + holder.chip.setOnClickListener(v -> { + if (controller != null) { + controller.setPlaybackSpeed(speed); + notifyDataSetChanged(); } + }); + } - UserPreferences.setPlaybackSpeedArray(newSpeedValues); + @Override + public int getItemCount() { + return selectedSpeeds.size(); + } - }); - builder.create().show(); - } + @Override + public long getItemId(int position) { + return selectedSpeeds.get(position).hashCode(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + Chip chip; + ViewHolder(Chip itemView) { + super(itemView); + chip = itemView; + } + } + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java index 249d1e9f6..9c2030c2f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java @@ -13,7 +13,6 @@ import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -21,17 +20,8 @@ import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; - import com.google.android.material.bottomsheet.BottomSheetBehavior; - import com.google.android.material.snackbar.Snackbar; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.text.DecimalFormat; -import java.text.NumberFormat; - import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.CastEnabledActivity; import de.danoeh.antennapod.activity.MainActivity; @@ -40,7 +30,6 @@ import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.Converter; @@ -60,6 +49,13 @@ import io.reactivex.Maybe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.List; /** * Shows the audio player. @@ -206,30 +202,29 @@ public class AudioPlayerFragment extends Fragment implements VariableSpeedDialog.showGetPluginDialog(getContext()); return; } - float[] availableSpeeds = UserPreferences.getPlaybackSpeedArray(); + List<Float> availableSpeeds = UserPreferences.getPlaybackSpeedArray(); float currentSpeed = controller.getCurrentPlaybackSpeedMultiplier(); int newSpeedIndex = 0; - while (newSpeedIndex < availableSpeeds.length && availableSpeeds[newSpeedIndex] < currentSpeed + EPSILON) { + while (newSpeedIndex < availableSpeeds.size() + && availableSpeeds.get(newSpeedIndex) < currentSpeed + EPSILON) { newSpeedIndex++; } float newSpeed; - if (availableSpeeds.length == 0) { + if (availableSpeeds.size() == 0) { newSpeed = 1.0f; - } else if (newSpeedIndex == availableSpeeds.length) { - newSpeed = availableSpeeds[0]; + } else if (newSpeedIndex == availableSpeeds.size()) { + newSpeed = availableSpeeds.get(0); } else { - newSpeed = availableSpeeds[newSpeedIndex]; + newSpeed = availableSpeeds.get(newSpeedIndex); } - PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(newSpeed); - UserPreferences.setPlaybackSpeed(newSpeed); controller.setPlaybackSpeed(newSpeed); loadMediaInfo(); }); butPlaybackSpeed.setOnLongClickListener(v -> { - VariableSpeedDialog.showDialog(getContext()); + new VariableSpeedDialog().show(getChildFragmentManager(), null); return true; }); butPlaybackSpeed.setVisibility(View.VISIBLE); @@ -494,7 +489,7 @@ public class AudioPlayerFragment extends Fragment implements new SleepTimerDialog().show(getChildFragmentManager(), "SleepTimerDialog"); return true; case R.id.audio_controls: - PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(false); + PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(); dialog.show(getChildFragmentManager(), "playback_controls"); return true; case R.id.open_feed_item: diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java index 18137f7f1..f07e59afe 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java @@ -45,7 +45,7 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { final Activity activity = getActivity(); findPreference(PREF_PLAYBACK_SPEED_LAUNCHER).setOnPreferenceClickListener(preference -> { - VariableSpeedDialog.showDialog(activity); + new VariableSpeedDialog().show(getChildFragmentManager(), null); return true; }); findPreference(PREF_PLAYBACK_REWIND_DELTA_LAUNCHER).setOnPreferenceClickListener(preference -> { diff --git a/app/src/main/java/de/danoeh/antennapod/view/ItemOffsetDecoration.java b/app/src/main/java/de/danoeh/antennapod/view/ItemOffsetDecoration.java new file mode 100644 index 000000000..4a1267d81 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/ItemOffsetDecoration.java @@ -0,0 +1,24 @@ +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.graphics.Rect; +import android.view.View; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Source: https://stackoverflow.com/a/30794046 + */ +public class ItemOffsetDecoration extends RecyclerView.ItemDecoration { + private final int itemOffset; + + public ItemOffsetDecoration(@NonNull Context context, int itemOffsetDp) { + itemOffset = (int) (itemOffsetDp * context.getResources().getDisplayMetrics().density); + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + outRect.set(itemOffset, itemOffset, itemOffset, itemOffset); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java new file mode 100644 index 000000000..47797e4a4 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java @@ -0,0 +1,86 @@ +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.SeekBar; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Consumer; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.util.playback.PlaybackController; +import de.danoeh.antennapod.dialog.VariableSpeedDialog; + +public class PlaybackSpeedSeekBar extends FrameLayout { + private SeekBar seekBar; + private PlaybackController controller; + private Consumer<Float> progressChangedListener; + + public PlaybackSpeedSeekBar(@NonNull Context context) { + super(context); + setup(); + } + + public PlaybackSpeedSeekBar(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + setup(); + } + + public PlaybackSpeedSeekBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(); + } + + private void setup() { + View.inflate(getContext(), R.layout.playback_speed_seek_bar, this); + seekBar = findViewById(R.id.playback_speed); + findViewById(R.id.butDecSpeed).setOnClickListener(v -> seekBar.setProgress(seekBar.getProgress() - 2)); + findViewById(R.id.butIncSpeed).setOnClickListener(v -> seekBar.setProgress(seekBar.getProgress() + 2)); + + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (controller != null && controller.canSetPlaybackSpeed()) { + float playbackSpeed = (progress + 10) / 20.0f; + controller.setPlaybackSpeed(playbackSpeed); + + if (progressChangedListener != null) { + progressChangedListener.accept(playbackSpeed); + } + } else if (fromUser) { + seekBar.post(() -> updateSpeed()); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + if (controller != null && !controller.canSetPlaybackSpeed()) { + VariableSpeedDialog.showGetPluginDialog(getContext()); + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + } + + public void updateSpeed() { + if (controller != null) { + seekBar.setProgress(Math.round((20 * controller.getCurrentPlaybackSpeedMultiplier()) - 10)); + } + } + + public void setController(PlaybackController controller) { + this.controller = controller; + updateSpeed(); + if (progressChangedListener != null && controller != null) { + progressChangedListener.accept(controller.getCurrentPlaybackSpeedMultiplier()); + } + } + + public void setProgressChangedListener(Consumer<Float> progressChangedListener) { + this.progressChangedListener = progressChangedListener; + } +} diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml index aa1bdf236..2c9665aad 100644 --- a/app/src/main/res/layout/audio_controls.xml +++ b/app/src/main/res/layout/audio_controls.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -38,47 +39,10 @@ </LinearLayout> - <LinearLayout + <de.danoeh.antennapod.view.PlaybackSpeedSeekBar + android:id="@+id/speed_seek_bar" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:gravity="center_vertical"> - - <TextView - android:id="@+id/butDecSpeed" - android:layout_width="32dp" - android:layout_height="32dp" - android:gravity="center" - android:text="-" - android:clickable="true" - android:focusable="true" - android:textStyle="bold" - android:textSize="24sp" - android:textColor="?attr/colorSecondary" - android:contentDescription="@string/decrease_speed" - android:background="?attr/selectableItemBackgroundBorderless"/> - - <SeekBar - android:id="@+id/playback_speed" - android:layout_width="0dp" - android:layout_height="32dp" - android:max="70" - android:layout_weight="1" /> - - <TextView - android:id="@+id/butIncSpeed" - android:layout_width="32dp" - android:layout_height="32dp" - android:gravity="center" - android:text="+" - android:clickable="true" - android:focusable="true" - android:textStyle="bold" - android:textSize="24sp" - android:textColor="?attr/colorSecondary" - android:contentDescription="@string/increase_speed" - android:background="?attr/selectableItemBackgroundBorderless" /> - </LinearLayout> + android:layout_height="wrap_content"/> <TextView android:layout_width="wrap_content" diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml index 5b4e26b69..1c2866a1b 100644 --- a/app/src/main/res/layout/audioplayer_fragment.xml +++ b/app/src/main/res/layout/audioplayer_fragment.xml @@ -184,7 +184,7 @@ android:layout_toStartOf="@id/butRev" android:layout_centerVertical="true" android:background="?attr/selectableItemBackgroundBorderless" - android:contentDescription="@string/set_playback_speed_label" + android:contentDescription="@string/playback_speed" tools:srcCompat="@drawable/ic_playback_speed_white"/> <TextView diff --git a/app/src/main/res/layout/playback_speed_seek_bar.xml b/app/src/main/res/layout/playback_speed_seek_bar.xml new file mode 100644 index 000000000..0d3985f5a --- /dev/null +++ b/app/src/main/res/layout/playback_speed_seek_bar.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + + <TextView + android:id="@+id/butDecSpeed" + android:layout_width="32dp" + android:layout_height="32dp" + android:gravity="center" + android:text="-" + android:clickable="true" + android:focusable="true" + android:textStyle="bold" + android:textSize="24sp" + android:textColor="?attr/colorSecondary" + android:contentDescription="@string/decrease_speed" + android:background="?attr/selectableItemBackgroundBorderless"/> + + <SeekBar + android:id="@+id/playback_speed" + android:layout_width="0dp" + android:layout_height="32dp" + android:max="70" + android:layout_weight="1" /> + + <TextView + android:id="@+id/butIncSpeed" + android:layout_width="32dp" + android:layout_height="32dp" + android:gravity="center" + android:text="+" + android:clickable="true" + android:focusable="true" + android:textStyle="bold" + android:textSize="24sp" + android:textColor="?attr/colorSecondary" + android:contentDescription="@string/increase_speed" + android:background="?attr/selectableItemBackgroundBorderless" /> +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/speed_select_dialog.xml b/app/src/main/res/layout/speed_select_dialog.xml new file mode 100644 index 000000000..e4d78c3fa --- /dev/null +++ b/app/src/main/res/layout/speed_select_dialog.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="16dp" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView + android:text="@string/playback_speed" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + style="@style/AntennaPod.TextView.ListItemPrimaryTitle"/> + + <com.google.android.material.chip.Chip + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/add_current_speed_chip"/> + </LinearLayout> + + <de.danoeh.antennapod.view.PlaybackSpeedSeekBar + android:id="@+id/speed_seek_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="8dp"> + </de.danoeh.antennapod.view.PlaybackSpeedSeekBar> + + <TextView + android:text="@string/speed_presets" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/AntennaPod.TextView.ListItemPrimaryTitle" + android:layout_marginBottom="8dp"/> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/selected_speeds_grid" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml index 32bf383d7..f8afa08ed 100644 --- a/app/src/main/res/xml/preferences_playback.xml +++ b/app/src/main/res/xml/preferences_playback.xml @@ -67,7 +67,7 @@ <Preference android:key="prefPlaybackSpeedLauncher" android:summary="@string/pref_playback_speed_sum" - android:title="@string/pref_playback_speed_title"/> + android:title="@string/playback_speed"/> <SwitchPreference android:defaultValue="false" android:key="prefPlaybackTimeRespectsSpeed" |