diff --git a/.idea/misc.xml b/.idea/misc.xml
index 04b54c4d..00ed8c15 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -18,6 +18,7 @@
+
@@ -34,6 +35,7 @@
+
@@ -60,6 +62,7 @@
+
@@ -68,6 +71,9 @@
+
+
+
@@ -110,7 +116,7 @@
-
+
diff --git a/app/src/main/java/com/cappielloantonio/play/model/Media.java b/app/src/main/java/com/cappielloantonio/play/model/Media.java
index 46a62a25..fdfac521 100644
--- a/app/src/main/java/com/cappielloantonio/play/model/Media.java
+++ b/app/src/main/java/com/cappielloantonio/play/model/Media.java
@@ -16,6 +16,13 @@ public class Media implements Parcelable {
public static final String MEDIA_TYPE_AUDIOBOOK = "audiobook";
public static final String MEDIA_TYPE_VIDEO = "video";
+ public static final float MEDIA_PLAYBACK_SPEED_080 = 0.8f;
+ public static final float MEDIA_PLAYBACK_SPEED_100 = 1.0f;
+ public static final float MEDIA_PLAYBACK_SPEED_125 = 1.25f;
+ public static final float MEDIA_PLAYBACK_SPEED_150 = 1.50f;
+ public static final float MEDIA_PLAYBACK_SPEED_175 = 1.75f;
+ public static final float MEDIA_PLAYBACK_SPEED_200 = 2.0f;
+
public static final String RECENTLY_PLAYED = "RECENTLY_PLAYED";
public static final String MOST_PLAYED = "MOST_PLAYED";
public static final String RECENTLY_ADDED = "RECENTLY_ADDED";
diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerBottomSheetFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerBottomSheetFragment.java
index a4fbb9c6..d37ca206 100644
--- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerBottomSheetFragment.java
+++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerBottomSheetFragment.java
@@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player;
+import androidx.media3.common.util.RepeatModeUtil;
import androidx.media3.session.MediaBrowser;
import androidx.media3.session.MediaController;
import androidx.media3.session.SessionToken;
@@ -23,6 +24,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.databinding.FragmentPlayerBottomSheetBinding;
import com.cappielloantonio.play.glide.CustomGlideRequest;
+import com.cappielloantonio.play.model.Media;
import com.cappielloantonio.play.service.MediaService;
import com.cappielloantonio.play.ui.fragment.pager.PlayerControllerVerticalPager;
import com.cappielloantonio.play.util.MusicUtil;
@@ -108,6 +110,7 @@ public class PlayerBottomSheetFragment extends Fragment {
@SuppressLint("UnsafeOptInUsageError")
private void setMediaControllerListener(MediaBrowser mediaBrowser) {
+ setMediaControllerUI(mediaBrowser);
setMetadata(mediaBrowser.getMediaMetadata());
setContentDuration(mediaBrowser.getContentDuration());
setPlayingState(mediaBrowser.isPlaying());
@@ -117,6 +120,7 @@ public class PlayerBottomSheetFragment extends Fragment {
mediaBrowser.addListener(new Player.Listener() {
@Override
public void onMediaMetadataChanged(@NonNull MediaMetadata mediaMetadata) {
+ setMediaControllerUI(mediaBrowser);
setMetadata(mediaMetadata);
setContentDuration(mediaBrowser.getContentDuration());
}
@@ -150,6 +154,25 @@ public class PlayerBottomSheetFragment extends Fragment {
.into(bind.playerHeaderLayout.playerHeaderMediaCoverImage);
}
+ @SuppressLint("UnsafeOptInUsageError")
+ private void setMediaControllerUI(MediaBrowser mediaBrowser) {
+ if (mediaBrowser.getMediaMetadata().extras != null) {
+ switch (mediaBrowser.getMediaMetadata().extras.getString("mediaType", Media.MEDIA_TYPE_MUSIC)) {
+ case Media.MEDIA_TYPE_PODCAST:
+ bind.playerHeaderLayout.playerHeaderFastForwardMediaButton.setVisibility(View.VISIBLE);
+ bind.playerHeaderLayout.playerHeaderRewindMediaButton.setVisibility(View.VISIBLE);
+ bind.playerHeaderLayout.playerHeaderNextMediaButton.setVisibility(View.GONE);
+ break;
+ case Media.MEDIA_TYPE_MUSIC:
+ default:
+ bind.playerHeaderLayout.playerHeaderFastForwardMediaButton.setVisibility(View.GONE);
+ bind.playerHeaderLayout.playerHeaderRewindMediaButton.setVisibility(View.GONE);
+ bind.playerHeaderLayout.playerHeaderNextMediaButton.setVisibility(View.VISIBLE);
+ break;
+ }
+ }
+ }
+
private void setContentDuration(long duration) {
bind.playerHeaderLayout.playerHeaderSeekBar.setMax((int) (duration / 1000));
}
@@ -169,6 +192,8 @@ public class PlayerBottomSheetFragment extends Fragment {
});
bind.playerHeaderLayout.playerHeaderNextMediaButton.setOnClickListener(view -> bind.getRoot().findViewById(R.id.exo_next).performClick());
+ bind.playerHeaderLayout.playerHeaderRewindMediaButton.setOnClickListener(view -> bind.getRoot().findViewById(R.id.exo_rew).performClick());
+ bind.playerHeaderLayout.playerHeaderFastForwardMediaButton.setOnClickListener(view -> bind.getRoot().findViewById(R.id.exo_ffwd).performClick());
}
private void setHeaderNextButtonState(boolean isEnabled) {
diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerControllerFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerControllerFragment.java
index 753e7e91..e9308a8c 100644
--- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerControllerFragment.java
+++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/PlayerControllerFragment.java
@@ -7,6 +7,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
@@ -15,7 +16,9 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.media3.common.MediaMetadata;
+import androidx.media3.common.PlaybackParameters;
import androidx.media3.common.Player;
+import androidx.media3.common.util.RepeatModeUtil;
import androidx.media3.session.MediaBrowser;
import androidx.media3.session.SessionToken;
import androidx.navigation.fragment.NavHostFragment;
@@ -23,11 +26,13 @@ import androidx.viewpager2.widget.ViewPager2;
import com.cappielloantonio.play.R;
import com.cappielloantonio.play.databinding.InnerFragmentPlayerControllerBinding;
+import com.cappielloantonio.play.model.Media;
import com.cappielloantonio.play.service.MediaService;
import com.cappielloantonio.play.ui.activity.MainActivity;
import com.cappielloantonio.play.ui.dialog.RatingDialog;
import com.cappielloantonio.play.ui.fragment.pager.PlayerControllerHorizontalPager;
import com.cappielloantonio.play.util.MusicUtil;
+import com.cappielloantonio.play.util.PreferenceUtil;
import com.cappielloantonio.play.viewmodel.PlayerBottomSheetViewModel;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
@@ -41,6 +46,8 @@ public class PlayerControllerFragment extends Fragment {
private ToggleButton buttonFavorite;
private TextView playerMediaTitleLabel;
private TextView playerArtistNameLabel;
+ private Button playbackSpeedButton;
+ private ToggleButton skipSilenceToggleButton;
private MainActivity activity;
private PlayerBottomSheetViewModel playerBottomSheetViewModel;
@@ -60,6 +67,7 @@ public class PlayerControllerFragment extends Fragment {
initMediaListenable();
initArtistLabelButton();
+
return view;
}
@@ -90,6 +98,8 @@ public class PlayerControllerFragment extends Fragment {
buttonFavorite = bind.getRoot().findViewById(R.id.button_favorite);
playerMediaTitleLabel = bind.getRoot().findViewById(R.id.player_media_title_label);
playerArtistNameLabel = bind.getRoot().findViewById(R.id.player_artist_name_label);
+ playbackSpeedButton = bind.getRoot().findViewById(R.id.player_playback_speed_button);
+ skipSilenceToggleButton = bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button);
playerMoveDownBottomSheet.setOnClickListener(view -> activity.collapseBottomSheet());
}
@@ -120,11 +130,13 @@ public class PlayerControllerFragment extends Fragment {
@SuppressLint("UnsafeOptInUsageError")
private void setMediaControllerListener(MediaBrowser mediaBrowser) {
+ setMediaControllerUI(mediaBrowser);
setMetadata(mediaBrowser.getMediaMetadata());
mediaBrowser.addListener(new Player.Listener() {
@Override
public void onMediaMetadataChanged(@NonNull MediaMetadata mediaMetadata) {
+ setMediaControllerUI(mediaBrowser);
setMetadata(mediaMetadata);
}
});
@@ -139,6 +151,39 @@ public class PlayerControllerFragment extends Fragment {
playerArtistNameLabel.setSelected(true);
}
+ @SuppressLint("UnsafeOptInUsageError")
+ private void setMediaControllerUI(MediaBrowser mediaBrowser) {
+ initPlaybackSpeedButton(mediaBrowser);
+
+ if (mediaBrowser.getMediaMetadata().extras != null) {
+ switch (mediaBrowser.getMediaMetadata().extras.getString("mediaType", Media.MEDIA_TYPE_MUSIC)) {
+ case Media.MEDIA_TYPE_PODCAST:
+ bind.getRoot().setShowShuffleButton(false);
+ bind.getRoot().setShowRewindButton(true);
+ bind.getRoot().setShowPreviousButton(false);
+ bind.getRoot().setShowNextButton(false);
+ bind.getRoot().setShowFastForwardButton(true);
+ bind.getRoot().setRepeatToggleModes(RepeatModeUtil.REPEAT_TOGGLE_MODE_NONE);
+ bind.getRoot().findViewById(R.id.player_playback_speed_button).setVisibility(View.VISIBLE);
+ bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button).setVisibility(View.VISIBLE);
+ setPlaybackParameters(mediaBrowser);
+ break;
+ case Media.MEDIA_TYPE_MUSIC:
+ default:
+ bind.getRoot().setShowShuffleButton(true);
+ bind.getRoot().setShowRewindButton(false);
+ bind.getRoot().setShowPreviousButton(true);
+ bind.getRoot().setShowNextButton(true);
+ bind.getRoot().setShowFastForwardButton(false);
+ 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_skip_silence_toggle_button).setVisibility(View.GONE);
+ resetPlaybackParameters(mediaBrowser);
+ break;
+ }
+ }
+ }
+
private void initCoverLyricsSlideView() {
playerMediaCoverViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
playerMediaCoverViewPager.setAdapter(new PlayerControllerHorizontalPager(this));
@@ -195,6 +240,43 @@ public class PlayerControllerFragment extends Fragment {
});
}
+ @SuppressLint("UnsafeOptInUsageError")
+ private void initPlaybackSpeedButton(MediaBrowser mediaBrowser) {
+ playbackSpeedButton.setOnClickListener(view -> {
+ float currentSpeed = PreferenceUtil.getInstance(requireContext()).getPlaybackSpeed();
+
+ if (currentSpeed == Media.MEDIA_PLAYBACK_SPEED_080) {
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(Media.MEDIA_PLAYBACK_SPEED_100));
+ playbackSpeedButton.setText(getString(R.string.player_playback_speed, Media.MEDIA_PLAYBACK_SPEED_100));
+ PreferenceUtil.getInstance(requireContext()).setPlaybackSpeed(Media.MEDIA_PLAYBACK_SPEED_100);
+ } else if (currentSpeed == Media.MEDIA_PLAYBACK_SPEED_100) {
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(Media.MEDIA_PLAYBACK_SPEED_125));
+ playbackSpeedButton.setText(getString(R.string.player_playback_speed, Media.MEDIA_PLAYBACK_SPEED_125));
+ PreferenceUtil.getInstance(requireContext()).setPlaybackSpeed(Media.MEDIA_PLAYBACK_SPEED_125);
+ } else if (currentSpeed == Media.MEDIA_PLAYBACK_SPEED_125) {
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(Media.MEDIA_PLAYBACK_SPEED_150));
+ playbackSpeedButton.setText(getString(R.string.player_playback_speed, Media.MEDIA_PLAYBACK_SPEED_150));
+ PreferenceUtil.getInstance(requireContext()).setPlaybackSpeed(Media.MEDIA_PLAYBACK_SPEED_150);
+ } else if (currentSpeed == Media.MEDIA_PLAYBACK_SPEED_150) {
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(Media.MEDIA_PLAYBACK_SPEED_175));
+ playbackSpeedButton.setText(getString(R.string.player_playback_speed, Media.MEDIA_PLAYBACK_SPEED_175));
+ PreferenceUtil.getInstance(requireContext()).setPlaybackSpeed(Media.MEDIA_PLAYBACK_SPEED_175);
+ } else if (currentSpeed == Media.MEDIA_PLAYBACK_SPEED_175) {
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(Media.MEDIA_PLAYBACK_SPEED_200));
+ playbackSpeedButton.setText(getString(R.string.player_playback_speed, Media.MEDIA_PLAYBACK_SPEED_200));
+ PreferenceUtil.getInstance(requireContext()).setPlaybackSpeed(Media.MEDIA_PLAYBACK_SPEED_200);
+ } else if (currentSpeed == Media.MEDIA_PLAYBACK_SPEED_200) {
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(Media.MEDIA_PLAYBACK_SPEED_080));
+ playbackSpeedButton.setText(getString(R.string.player_playback_speed, Media.MEDIA_PLAYBACK_SPEED_080));
+ PreferenceUtil.getInstance(requireContext()).setPlaybackSpeed(Media.MEDIA_PLAYBACK_SPEED_080);
+ }
+ });
+
+ skipSilenceToggleButton.setOnClickListener(view -> {
+ PreferenceUtil.getInstance(requireContext()).setSkipSilenceMode(!skipSilenceToggleButton.isChecked());
+ });
+ }
+
public void goToControllerPage() {
playerMediaCoverViewPager.setCurrentItem(0, false);
}
@@ -202,4 +284,23 @@ public class PlayerControllerFragment extends Fragment {
public void goToLyricsPage() {
playerMediaCoverViewPager.setCurrentItem(1, true);
}
+
+ @SuppressLint("UnsafeOptInUsageError")
+ private void setPlaybackParameters(MediaBrowser mediaBrowser) {
+ Button playbackSpeedButton = bind.getRoot().findViewById(R.id.player_playback_speed_button);
+ float currentSpeed = PreferenceUtil.getInstance(requireContext()).getPlaybackSpeed();
+ boolean skipSilence = PreferenceUtil.getInstance(requireContext()).isSkipSilenceMode();
+
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(currentSpeed));
+ playbackSpeedButton.setText(getString(R.string.player_playback_speed, currentSpeed));
+
+ // TODO Skippare il silenzio
+ skipSilenceToggleButton.setChecked(skipSilence);
+ }
+
+ @SuppressLint("UnsafeOptInUsageError")
+ private void resetPlaybackParameters(MediaBrowser mediaBrowser) {
+ mediaBrowser.setPlaybackParameters(new PlaybackParameters(Media.MEDIA_PLAYBACK_SPEED_100));
+ // TODO Resettare lo skip del silenzio
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/inner_fragment_player_controller.xml b/app/src/main/res/layout/inner_fragment_player_controller.xml
index 8e7214fd..9499e90d 100644
--- a/app/src/main/res/layout/inner_fragment_player_controller.xml
+++ b/app/src/main/res/layout/inner_fragment_player_controller.xml
@@ -7,4 +7,6 @@
app:controller_layout_id="@layout/inner_fragment_player_controller_layout"
app:repeat_toggle_modes="all|one"
app:show_shuffle_button="true"
+ app:show_rewind_button="false"
+ app:show_fastforward_button="false"
app:show_timeout="0" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/inner_fragment_player_controller_layout.xml b/app/src/main/res/layout/inner_fragment_player_controller_layout.xml
index 21faf51c..3b56be26 100644
--- a/app/src/main/res/layout/inner_fragment_player_controller_layout.xml
+++ b/app/src/main/res/layout/inner_fragment_player_controller_layout.xml
@@ -145,6 +145,25 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/exo_progress" />
+
+
+
+
+
+
+
+
+
+
+
+
@@ -222,12 +290,25 @@
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="@+id/player_play_pause_placeholder_view"
- app:layout_constraintEnd_toStartOf="@id/exo_repeat_toggle"
+ app:layout_constraintEnd_toStartOf="@id/placeholder_view_right"
app:layout_constraintStart_toEndOf="@+id/player_play_pause_placeholder_view"
app:layout_constraintTop_toTopOf="@+id/player_play_pause_placeholder_view"
app:srcCompat="@drawable/ic_skip_next"
app:tint="?attr/colorOnPrimaryContainer" />
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/player_header_bottom_sheet.xml b/app/src/main/res/layout/player_header_bottom_sheet.xml
index f01a1555..31697b36 100644
--- a/app/src/main/res/layout/player_header_bottom_sheet.xml
+++ b/app/src/main/res/layout/player_header_bottom_sheet.xml
@@ -26,7 +26,7 @@
android:maxLines="1"
android:paddingStart="8dp"
android:paddingEnd="8dp"
- app:layout_constraintEnd_toStartOf="@+id/player_header_button"
+ app:layout_constraintEnd_toStartOf="@+id/placeholder_left_view"
app:layout_constraintStart_toEndOf="@+id/player_header_media_cover_image"
app:layout_constraintTop_toTopOf="parent" />
@@ -39,10 +39,43 @@
android:maxLines="1"
android:paddingStart="8dp"
android:paddingEnd="8dp"
- app:layout_constraintEnd_toStartOf="@+id/player_header_button"
+ app:layout_constraintEnd_toStartOf="@+id/placeholder_left_view"
app:layout_constraintStart_toEndOf="@+id/player_header_media_cover_image"
app:layout_constraintTop_toBottomOf="@+id/player_header_media_title_label" />
+
+
+
+
+
+
+
+
Unpin
Now playing
No lyrics available
+ %1$.2fx
Playlist Catalogue
Browse Playlists
No playlists created