diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/TrackInfoDialog.java b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/TrackInfoDialog.java new file mode 100644 index 00000000..ccd4d601 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/dialog/TrackInfoDialog.java @@ -0,0 +1,132 @@ +package com.cappielloantonio.tempo.ui.dialog; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.media3.common.MediaMetadata; + +import com.cappielloantonio.tempo.R; +import com.cappielloantonio.tempo.databinding.DialogTrackInfoBinding; +import com.cappielloantonio.tempo.glide.CustomGlideRequest; +import com.cappielloantonio.tempo.util.Constants; +import com.cappielloantonio.tempo.util.MusicUtil; +import com.cappielloantonio.tempo.util.Preferences; + +public class TrackInfoDialog extends DialogFragment { + private static final String TAG = "TrackInfoDialog"; + + private DialogTrackInfoBinding bind; + private MediaMetadata mediaMetadata; + + public TrackInfoDialog(MediaMetadata mediaMetadata) { + this.mediaMetadata = mediaMetadata; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + bind = DialogTrackInfoBinding.inflate(getLayoutInflater()); + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + builder.setView(bind.getRoot()) + .setPositiveButton(R.string.track_info_dialog_positive_button, (dialog, id) -> dialog.cancel()); + + return builder.create(); + } + + @Override + public void onStart() { + super.onStart(); + + setTrackInfo(); + setTrackTranscodingInfo(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + bind = null; + } + + private void setTrackInfo() { + bind.trakTitleInfoTextView.setText(mediaMetadata.title); + bind.trakArtistInfoTextView.setText(mediaMetadata.artist); + + if (mediaMetadata.extras != null) { + CustomGlideRequest.Builder + .from(requireContext(), mediaMetadata.extras.getString("coverArtId", "")) + .build() + .into(bind.trackCoverInfoImageView); + + bind.titleValueSector.setText(mediaMetadata.extras.getString("title", getString(R.string.label_placeholder))); + bind.albumValueSector.setText(mediaMetadata.extras.getString("album", getString(R.string.label_placeholder))); + bind.artistValueSector.setText(mediaMetadata.extras.getString("artist", getString(R.string.label_placeholder))); + bind.trackNumberValueSector.setText(String.valueOf(mediaMetadata.extras.getInt("track", 0))); + bind.yearValueSector.setText(String.valueOf(mediaMetadata.extras.getInt("year", 0))); + bind.genreValueSector.setText(mediaMetadata.extras.getString("genre", getString(R.string.label_placeholder))); + bind.sizeValueSector.setText(MusicUtil.getReadableByteCount(mediaMetadata.extras.getLong("size", 0))); + bind.contentTypeValueSector.setText(mediaMetadata.extras.getString("contentType", getString(R.string.label_placeholder))); + bind.suffixValueSector.setText(mediaMetadata.extras.getString("suffix", getString(R.string.label_placeholder))); + bind.transcodedContentTypeValueSector.setText(mediaMetadata.extras.getString("transcodedContentType", getString(R.string.label_placeholder))); + bind.transcodedSuffixValueSector.setText(mediaMetadata.extras.getString("transcodedSuffix", getString(R.string.label_placeholder))); + bind.durationValueSector.setText(MusicUtil.getReadableDurationString(mediaMetadata.extras.getInt("duration", 0), false)); + bind.bitrateValueSector.setText(mediaMetadata.extras.getInt("bitrate", 0) + " kbps"); + bind.pathValueSector.setText(mediaMetadata.extras.getString("path", getString(R.string.label_placeholder))); + bind.discNumberValueSector.setText(String.valueOf(mediaMetadata.extras.getInt("discNumber", 0))); + } + } + + private void setTrackTranscodingInfo() { + StringBuilder info = new StringBuilder(); + + boolean prioritizeServerTranscoding = Preferences.isServerPrioritized(); + + String transcodingExtension = MusicUtil.getTranscodingFormatPreference(); + String transcodingBitrate = Integer.parseInt(MusicUtil.getBitratePreference()) != 0 ? Integer.parseInt(MusicUtil.getBitratePreference()) + "kbps" : "Original"; + + if (mediaMetadata.extras != null && mediaMetadata.extras.getString("uri", "").contains(Constants.DOWNLOAD_URI)) { + info.append(getString(R.string.track_info_summary_downloaded_file)); + + bind.trakTranscodingInfoTextView.setText(info); + return; + } + + if (prioritizeServerTranscoding) { + info.append(getString(R.string.track_info_summary_server_prioritized)); + + bind.trakTranscodingInfoTextView.setText(info); + return; + } + + if (!prioritizeServerTranscoding && transcodingExtension.equals("raw") && transcodingBitrate.equals("Original")) { + info.append(getString(R.string.track_info_summary_original_file)); + + bind.trakTranscodingInfoTextView.setText(info); + return; + } + + if (!prioritizeServerTranscoding && !transcodingExtension.equals("raw") && transcodingBitrate.equals("Original")) { + info.append(getString(R.string.track_info_summary_transcoding_codec, transcodingExtension)); + + bind.trakTranscodingInfoTextView.setText(info); + return; + } + + if (!prioritizeServerTranscoding && transcodingExtension.equals("raw") && !transcodingBitrate.equals("Original")) { + info.append(getString(R.string.track_info_summary_transcoding_bitrate, transcodingBitrate)); + + bind.trakTranscodingInfoTextView.setText(info); + return; + } + + if (!prioritizeServerTranscoding && !transcodingExtension.equals("raw") && !transcodingBitrate.equals("Original")) { + info.append(getString(R.string.track_info_summary_full_transcode, transcodingExtension, transcodingBitrate)); + + bind.trakTranscodingInfoTextView.setText(info); + } + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java index 53bece5b..fc60d9b0 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/fragment/PlayerControllerFragment.java @@ -7,9 +7,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageButton; -import android.widget.ImageView; import android.widget.TextView; -import android.widget.Toast; import android.widget.ToggleButton; import androidx.annotation.NonNull; @@ -31,6 +29,7 @@ import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerControllerBindi import com.cappielloantonio.tempo.service.MediaService; import com.cappielloantonio.tempo.ui.activity.MainActivity; import com.cappielloantonio.tempo.ui.dialog.RatingDialog; +import com.cappielloantonio.tempo.ui.dialog.TrackInfoDialog; import com.cappielloantonio.tempo.ui.fragment.pager.PlayerControllerHorizontalPager; import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.MusicUtil; @@ -54,12 +53,8 @@ public class PlayerControllerFragment extends Fragment { private ToggleButton skipSilenceToggleButton; private Chip playerMediaExtension; private TextView playerMediaBitrate; - private ImageView playerMediaTranscodingIcon; - private ImageView playerMediaTranscodingPriorityIcon; - private Chip playerMediaTranscodedExtension; - private TextView playerMediaTranscodedBitrate; private ConstraintLayout playerQuickActionView; - private ImageButton playerOpenQueueButton; + private ImageButton playerTrackInfo; private MainActivity activity; private PlayerBottomSheetViewModel playerBottomSheetViewModel; @@ -79,7 +74,6 @@ public class PlayerControllerFragment extends Fragment { initCoverLyricsSlideView(); initMediaListenable(); initArtistLabelButton(); - initTranscodingInfo(); return view; } @@ -112,18 +106,14 @@ public class PlayerControllerFragment extends Fragment { skipSilenceToggleButton = bind.getRoot().findViewById(R.id.player_skip_silence_toggle_button); playerMediaExtension = bind.getRoot().findViewById(R.id.player_media_extension); playerMediaBitrate = bind.getRoot().findViewById(R.id.player_media_bitrate); - playerMediaTranscodingIcon = bind.getRoot().findViewById(R.id.player_media_transcoding_audio); - playerMediaTranscodingPriorityIcon = bind.getRoot().findViewById(R.id.player_media_server_transcode_priority); - playerMediaTranscodedExtension = bind.getRoot().findViewById(R.id.player_media_transcoded_extension); - playerMediaTranscodedBitrate = bind.getRoot().findViewById(R.id.player_media_transcoded_bitrate); playerQuickActionView = bind.getRoot().findViewById(R.id.player_quick_action_view); - playerOpenQueueButton = bind.getRoot().findViewById(R.id.player_open_queue_button); + playerTrackInfo = bind.getRoot().findViewById(R.id.player_info_track); } private void initQuickActionView() { playerQuickActionView.setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 8)); - playerOpenQueueButton.setOnClickListener(view -> { + playerQuickActionView.setOnClickListener(view -> { PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) requireActivity().getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet"); if (playerBottomSheetFragment != null) { playerBottomSheetFragment.goToQueuePage(); @@ -191,36 +181,10 @@ public class PlayerControllerFragment extends Fragment { } } - String transcodingExtension = MusicUtil.getTranscodingFormatPreference(); - String transcodingBitrate = Integer.parseInt(MusicUtil.getBitratePreference()) != 0 ? Integer.parseInt(MusicUtil.getBitratePreference()) + "kbps" : "Original"; - - if (transcodingExtension.equals("raw") && transcodingBitrate.equals("Original")) { - playerMediaTranscodingPriorityIcon.setVisibility(View.GONE); - playerMediaTranscodingIcon.setVisibility(View.GONE); - playerMediaTranscodedBitrate.setVisibility(View.GONE); - playerMediaTranscodedExtension.setVisibility(View.GONE); - } else { - playerMediaTranscodingPriorityIcon.setVisibility(View.GONE); - playerMediaTranscodingIcon.setVisibility(View.VISIBLE); - playerMediaTranscodedBitrate.setVisibility(View.VISIBLE); - playerMediaTranscodedExtension.setVisibility(View.VISIBLE); - playerMediaTranscodedExtension.setText(transcodingExtension); - playerMediaTranscodedBitrate.setText(transcodingBitrate); - } - - if (mediaMetadata.extras != null && mediaMetadata.extras.getString("uri", "").contains(Constants.DOWNLOAD_URI)) { - playerMediaTranscodingPriorityIcon.setVisibility(View.GONE); - playerMediaTranscodingIcon.setVisibility(View.GONE); - playerMediaTranscodedBitrate.setVisibility(View.GONE); - playerMediaTranscodedExtension.setVisibility(View.GONE); - } - - if (Preferences.isServerPrioritized() && mediaMetadata.extras != null && !mediaMetadata.extras.getString("uri", "").contains(Constants.DOWNLOAD_URI)) { - playerMediaTranscodingPriorityIcon.setVisibility(View.VISIBLE); - playerMediaTranscodingIcon.setVisibility(View.GONE); - playerMediaTranscodedBitrate.setVisibility(View.GONE); - playerMediaTranscodedExtension.setVisibility(View.GONE); - } + playerTrackInfo.setOnClickListener(view -> { + TrackInfoDialog dialog = new TrackInfoDialog(mediaMetadata); + dialog.show(activity.getSupportFragmentManager(), null); + }); } private void setMediaControllerUI(MediaBrowser mediaBrowser) { @@ -320,13 +284,6 @@ public class PlayerControllerFragment extends Fragment { }); } - private void initTranscodingInfo() { - playerMediaTranscodingPriorityIcon.setOnLongClickListener(view -> { - Toast.makeText(requireActivity(), R.string.settings_audio_transcode_priority_toast, Toast.LENGTH_SHORT).show(); - return true; - }); - } - private void initPlaybackSpeedButton(MediaBrowser mediaBrowser) { playbackSpeedButton.setOnClickListener(view -> { float currentSpeed = Preferences.getPlaybackSpeed(); diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java b/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java index c98ddad2..5ae88785 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java +++ b/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java @@ -14,6 +14,8 @@ import com.cappielloantonio.tempo.model.Download; import com.cappielloantonio.tempo.repository.DownloadRepository; import com.cappielloantonio.tempo.subsonic.models.Child; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -211,6 +213,27 @@ public class MusicUtil { return readableStrings; } + public static String getReadableByteCount(long bytes) { + long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); + + if (absB < 1024) { + return bytes + " B"; + } + + long value = absB; + + CharacterIterator ci = new StringCharacterIterator("KMGTPE"); + + for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { + value >>= 10; + ci.next(); + } + + value *= Long.signum(bytes); + + return String.format("%.1f %ciB", value / 1024.0, ci.current()); + } + public static String passwordHexEncoding(String plainPassword) { return "enc:" + plainPassword.chars().mapToObj(Integer::toHexString).collect(Collectors.joining()); } diff --git a/app/src/main/res/drawable/ic_info_stream.xml b/app/src/main/res/drawable/ic_info_stream.xml new file mode 100644 index 00000000..5165a161 --- /dev/null +++ b/app/src/main/res/drawable/ic_info_stream.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_track_info.xml b/app/src/main/res/layout/dialog_track_info.xml new file mode 100644 index 00000000..ee6eb8b8 --- /dev/null +++ b/app/src/main/res/layout/dialog_track_info.xml @@ -0,0 +1,447 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 1ada38b3..e00badd5 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 @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:chipStrokeWidth="0dp" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/player_media_bitrate" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintHorizontal_chainStyle="packed"/> + app:layout_constraintTop_toTopOf="@id/player_media_extension" + app:layout_constraintBottom_toBottomOf="@id/player_media_extension" + app:layout_constraintStart_toEndOf="@id/player_media_extension" + app:layout_constraintEnd_toEndOf="parent"/> - - - - - + android:padding="16dp" + android:layout_marginEnd="8dp" + android:background="?attr/selectableItemBackgroundBorderless" + android:scaleType="fitCenter" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/player_media_extension" + app:layout_constraintBottom_toBottomOf="@id/player_media_extension" + app:srcCompat="@drawable/ic_info_stream" + app:tint="?attr/colorOnPrimaryContainer" /> - - + Artist Genre Year + OK + Track info + Title + Album + Artist + Track number + Year + Genre + Size + Content Type + Suffix + Transcoded content type + Transcoded suffix + Duration + Bitrate + Path + Disc number + The file has been downloaded using the Subsonic APIs. The codec and bitrate of the file remain unchanged from the source file. + The quality of the file to be played is left up to the server\'s decision. The app will not enforce the choice of codec and bitrate for any potential transcoding. + The application will only read the original file as provided by the server. The app will explicitly request the server for the untranscoded file with the bitrate of the original source. + The application will request the server to transcode the file. The requested codec by the user is %1$s, while the bitrate will be the same as the source file. The potential transcoding of the file into the chosen format is dependent on the server, as it may or may not support the operation. + The application will request the server to modify the bitrate of the file. The user requested a bitrate of %1$s, while the codec of the source file will remain the same. Any changes to the bitrate of the file in the chosen format will be done by the server, which may or may not support the operation. + The application will request the server to transcode the file and modify its bitrate. The user requested codec is %1$s, with a bitrate of %2$s. Any potential changes to the codec and bitrate of the file in the chosen format will be handled by the server, which may or may not support the operation. \ No newline at end of file