mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 17:43:32 +00:00
Merge branch 'main' into main
This commit is contained in:
commit
223954e9ca
21 changed files with 900 additions and 114 deletions
|
|
@ -28,8 +28,8 @@ android {
|
|||
tempo {
|
||||
dimension "default"
|
||||
applicationId 'com.cappielloantonio.tempo'
|
||||
versionCode 19
|
||||
versionName '3.5.4'
|
||||
versionCode 20
|
||||
versionName '3.5.5'
|
||||
}
|
||||
|
||||
notquitemy {
|
||||
|
|
|
|||
|
|
@ -204,6 +204,22 @@ public class MediaManager {
|
|||
}
|
||||
}
|
||||
|
||||
public static void shuffle(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, List<Child> media, int startIndex, int endIndex) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
if (mediaBrowserListenableFuture.isDone()) {
|
||||
mediaBrowserListenableFuture.get().removeMediaItems(startIndex, endIndex + 1);
|
||||
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1));
|
||||
swapDatabase(media);
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
public static void swap(ListenableFuture<MediaBrowser> mediaBrowserListenableFuture, List<Child> media, int from, int to) {
|
||||
if (mediaBrowserListenableFuture != null) {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.PopupMenu;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
|
@ -37,6 +38,8 @@ import java.util.Objects;
|
|||
|
||||
@UnstableApi
|
||||
public class DownloadFragment extends Fragment implements ClickCallback {
|
||||
private static final String TAG = "DownloadFragment";
|
||||
|
||||
private FragmentDownloadBinding bind;
|
||||
private MainActivity activity;
|
||||
private DownloadViewModel downloadViewModel;
|
||||
|
|
@ -160,6 +163,23 @@ public class DownloadFragment extends Fragment implements ClickCallback {
|
|||
}
|
||||
|
||||
bind.downloadedGoBackImageView.setVisibility(stack.size() > 1 ? View.VISIBLE : View.GONE);
|
||||
|
||||
setupBackPressing(stack.size());
|
||||
});
|
||||
}
|
||||
|
||||
private void setupBackPressing(int stackSize) {
|
||||
requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
if (stackSize > 1) {
|
||||
downloadViewModel.popViewStack();
|
||||
} else {
|
||||
activity.navController.navigateUp();
|
||||
}
|
||||
|
||||
remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -240,6 +240,10 @@ public class PlayerBottomSheetFragment extends Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
public void goToQueuePage() {
|
||||
bind.playerBodyLayout.playerBodyBottomSheetViewPager.setCurrentItem(1, true);
|
||||
}
|
||||
|
||||
private void defineProgressBarHandler(MediaBrowser mediaBrowser) {
|
||||
progressBarHandler = new Handler();
|
||||
progressBarRunnable = () -> {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
|
|
@ -29,12 +29,14 @@ 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;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.elevation.SurfaceColors;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
|
|
@ -51,10 +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 playerTrackInfo;
|
||||
|
||||
private MainActivity activity;
|
||||
private PlayerBottomSheetViewModel playerBottomSheetViewModel;
|
||||
|
|
@ -70,10 +70,10 @@ public class PlayerControllerFragment extends Fragment {
|
|||
playerBottomSheetViewModel = new ViewModelProvider(requireActivity()).get(PlayerBottomSheetViewModel.class);
|
||||
|
||||
init();
|
||||
initQuickActionView();
|
||||
initCoverLyricsSlideView();
|
||||
initMediaListenable();
|
||||
initArtistLabelButton();
|
||||
initTranscodingInfo();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
|
@ -106,10 +106,19 @@ 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);
|
||||
playerTrackInfo = bind.getRoot().findViewById(R.id.player_info_track);
|
||||
}
|
||||
|
||||
private void initQuickActionView() {
|
||||
playerQuickActionView.setBackgroundColor(SurfaceColors.getColorForElevation(requireContext(), 8));
|
||||
|
||||
playerQuickActionView.setOnClickListener(view -> {
|
||||
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) requireActivity().getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
|
||||
if (playerBottomSheetFragment != null) {
|
||||
playerBottomSheetFragment.goToQueuePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeBrowser() {
|
||||
|
|
@ -160,9 +169,7 @@ public class PlayerControllerFragment extends Fragment {
|
|||
private void setMediaInfo(MediaMetadata mediaMetadata) {
|
||||
if (mediaMetadata.extras != null) {
|
||||
String extension = mediaMetadata.extras.getString("suffix", "Unknown format");
|
||||
String bitrate = mediaMetadata.extras.getInt("bitrate", 0) != 0
|
||||
? mediaMetadata.extras.getInt("bitrate", 0) + "kbps"
|
||||
: "Original";
|
||||
String bitrate = mediaMetadata.extras.getInt("bitrate", 0) != 0 ? mediaMetadata.extras.getInt("bitrate", 0) + "kbps" : "Original";
|
||||
|
||||
playerMediaExtension.setText(extension);
|
||||
|
||||
|
|
@ -174,38 +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) {
|
||||
|
|
@ -305,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();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.fragment;
|
|||
|
||||
import android.content.ComponentName;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.transition.Fade;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionManager;
|
||||
|
|
@ -39,6 +40,8 @@ public class PlayerCoverFragment extends Fragment {
|
|||
private InnerFragmentPlayerCoverBinding bind;
|
||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||
|
||||
private final Handler handler = new Handler();
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
bind = InnerFragmentPlayerCoverBinding.inflate(inflater, container, false);
|
||||
|
|
@ -72,9 +75,19 @@ public class PlayerCoverFragment extends Fragment {
|
|||
bind = null;
|
||||
}
|
||||
|
||||
private void initTapButtonHideTransition() {
|
||||
bind.nowPlayingTapButton.setVisibility(View.VISIBLE);
|
||||
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
|
||||
final Runnable runnable = () -> bind.nowPlayingTapButton.setVisibility(View.GONE);
|
||||
handler.postDelayed(runnable, 10000);
|
||||
}
|
||||
|
||||
private void initOverlay() {
|
||||
bind.nowPlayingSongCoverImageView.setOnClickListener(view -> toggleOverlayVisibility(true));
|
||||
bind.nowPlayingSongCoverButtonGroup.setOnClickListener(view -> toggleOverlayVisibility(false));
|
||||
bind.nowPlayingTapButton.setOnClickListener(view -> toggleOverlayVisibility(true));
|
||||
}
|
||||
|
||||
private void toggleOverlayVisibility(boolean isVisible) {
|
||||
|
|
@ -84,9 +97,12 @@ public class PlayerCoverFragment extends Fragment {
|
|||
|
||||
TransitionManager.beginDelayedTransition(bind.getRoot(), transition);
|
||||
bind.nowPlayingSongCoverButtonGroup.setVisibility(isVisible ? View.VISIBLE : View.GONE);
|
||||
bind.nowPlayingTapButton.setVisibility(isVisible ? View.GONE : View.VISIBLE);
|
||||
|
||||
bind.innerButtonBottomRight.setVisibility(Preferences.isSyncronizationEnabled() ? View.VISIBLE : View.GONE);
|
||||
bind.innerButtonBottomRightAlternative.setVisibility(Preferences.isSyncronizationEnabled() ? View.GONE : View.VISIBLE);
|
||||
|
||||
if (!isVisible) initTapButtonHideTransition();
|
||||
}
|
||||
|
||||
private void initInnerButton() {
|
||||
|
|
|
|||
|
|
@ -25,12 +25,16 @@ import com.cappielloantonio.tempo.ui.adapter.PlayerSongQueueAdapter;
|
|||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlayerBottomSheetViewModel;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@UnstableApi
|
||||
public class PlayerQueueFragment extends Fragment implements ClickCallback {
|
||||
private static final String TAG = "PlayerQueueFragment";
|
||||
|
||||
private InnerFragmentPlayerQueueBinding bind;
|
||||
|
||||
private PlayerBottomSheetViewModel playerBottomSheetViewModel;
|
||||
|
|
@ -54,6 +58,7 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
initializeBrowser();
|
||||
bindMediaController();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -83,6 +88,17 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
|
|||
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||
}
|
||||
|
||||
private void bindMediaController() {
|
||||
mediaBrowserListenableFuture.addListener(() -> {
|
||||
try {
|
||||
MediaBrowser mediaBrowser = mediaBrowserListenableFuture.get();
|
||||
initShuffleButton(mediaBrowser);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
private void setMediaBrowserListenableFuture() {
|
||||
playerSongQueueAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
|
||||
}
|
||||
|
|
@ -151,6 +167,36 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
|
|||
}).attachToRecyclerView(bind.playerQueueRecyclerView);
|
||||
}
|
||||
|
||||
private void initShuffleButton(MediaBrowser mediaBrowser) {
|
||||
bind.playerShuffleQueueFab.setOnClickListener(view -> {
|
||||
int startPosition = mediaBrowser.getCurrentMediaItemIndex() + 1;
|
||||
int endPosition = playerSongQueueAdapter.getItems().size() - 1;
|
||||
|
||||
if (startPosition < endPosition) {
|
||||
ArrayList<Integer> pool = new ArrayList<>();
|
||||
|
||||
for (int i = startPosition; i <= endPosition; i++) {
|
||||
pool.add(i);
|
||||
}
|
||||
|
||||
while (pool.size() >= 2) {
|
||||
int fromPosition = (int) (Math.random() * (pool.size()));
|
||||
int positionA = pool.get(fromPosition);
|
||||
pool.remove(fromPosition);
|
||||
|
||||
int toPosition = (int) (Math.random() * (pool.size()));
|
||||
int positionB = pool.get(toPosition);
|
||||
pool.remove(toPosition);
|
||||
|
||||
Collections.swap(playerSongQueueAdapter.getItems(), positionA, positionB);
|
||||
bind.playerQueueRecyclerView.getAdapter().notifyItemMoved(positionA, positionB);
|
||||
}
|
||||
|
||||
MediaManager.shuffle(mediaBrowserListenableFuture, playerSongQueueAdapter.getItems(), startPosition, endPosition);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateNowPlayingItem() {
|
||||
playerSongQueueAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,8 +147,6 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||
}));
|
||||
|
||||
TextView downloadAll = view.findViewById(R.id.download_all_text_view);
|
||||
TextView removeAll = view.findViewById(R.id.remove_all_text_view);
|
||||
|
||||
albumBottomSheetViewModel.getAlbumTracks().observe(getViewLifecycleOwner(), songs -> {
|
||||
List<MediaItem> mediaItems = MappingUtil.mapDownloads(songs);
|
||||
List<Download> downloads = songs.stream().map(Download::new).collect(Collectors.toList());
|
||||
|
|
@ -157,17 +155,21 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||
DownloadUtil.getDownloadTracker(requireContext()).download(mediaItems, downloads);
|
||||
dismissBottomSheet();
|
||||
});
|
||||
|
||||
if (DownloadUtil.getDownloadTracker(requireContext()).areDownloaded(mediaItems)) {
|
||||
removeAll.setOnClickListener(v -> {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).remove(mediaItems, downloads);
|
||||
dismissBottomSheet();
|
||||
});
|
||||
} else {
|
||||
removeAll.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
TextView removeAll = view.findViewById(R.id.remove_all_text_view);
|
||||
albumBottomSheetViewModel.getAlbumTracks().observe(getViewLifecycleOwner(), songs -> {
|
||||
List<MediaItem> mediaItems = MappingUtil.mapDownloads(songs);
|
||||
List<Download> downloads = songs.stream().map(Download::new).collect(Collectors.toList());
|
||||
|
||||
removeAll.setOnClickListener(v -> {
|
||||
DownloadUtil.getDownloadTracker(requireContext()).remove(mediaItems, downloads);
|
||||
dismissBottomSheet();
|
||||
});
|
||||
});
|
||||
|
||||
initDownloadUI(removeAll);
|
||||
|
||||
TextView goToArtist = view.findViewById(R.id.go_to_artist_text_view);
|
||||
goToArtist.setOnClickListener(v -> albumBottomSheetViewModel.getArtist().observe(getViewLifecycleOwner(), artist -> {
|
||||
if (artist != null) {
|
||||
|
|
@ -191,6 +193,16 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
|||
dismiss();
|
||||
}
|
||||
|
||||
private void initDownloadUI(TextView removeAll) {
|
||||
albumBottomSheetViewModel.getAlbumTracks().observe(getViewLifecycleOwner(), songs -> {
|
||||
List<MediaItem> mediaItems = MappingUtil.mapDownloads(songs);
|
||||
|
||||
if (DownloadUtil.getDownloadTracker(requireContext()).areDownloaded(mediaItems)) {
|
||||
removeAll.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeMediaBrowser() {
|
||||
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.cappielloantonio.tempo.viewmodel;
|
||||
|
||||
import android.app.Application;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
|
|
@ -11,7 +12,6 @@ import androidx.lifecycle.MutableLiveData;
|
|||
import com.cappielloantonio.tempo.model.DownloadStack;
|
||||
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
9
app/src/main/res/drawable/ic_info_stream.xml
Normal file
9
app/src/main/res/drawable/ic_info_stream.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@color/titleTextColor"
|
||||
android:pathData="M440,680L520,680L520,440L440,440L440,680ZM480,360Q497,360 508.5,348.5Q520,337 520,320Q520,303 508.5,291.5Q497,280 480,280Q463,280 451.5,291.5Q440,303 440,320Q440,337 451.5,348.5Q463,360 480,360ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_queue.xml
Normal file
10
app/src/main/res/drawable/ic_queue.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:autoMirrored="true">
|
||||
<path
|
||||
android:fillColor="@color/titleTextColor"
|
||||
android:pathData="M624,720Q574,720 539,685Q504,650 504,600Q504,550 539,515Q574,480 624,480Q636,480 648.5,482.5Q661,485 672,490L672,192L864,192L864,264L744,264L744,600Q744,650 709,685Q674,720 624,720ZM144,552L144,480L432,480L432,552L144,552ZM144,408L144,336L576,336L576,408L144,408ZM144,264L144,192L576,192L576,264L144,264Z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_tap.xml
Normal file
9
app/src/main/res/drawable/ic_tap.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@color/titleTextColor"
|
||||
android:pathData="M419,880Q391,880 366.5,868Q342,856 325,834L107,557L126,537Q146,516 174,512Q202,508 226,523L300,568L300,240Q300,223 311.5,211.5Q323,200 340,200Q357,200 369,211.5Q381,223 381,240L381,712L284,652L388,785Q394,792 402,796Q410,800 419,800L640,800Q673,800 696.5,776.5Q720,753 720,720L720,560Q720,543 708.5,531.5Q697,520 680,520L461,520L461,440L680,440Q730,440 765,475Q800,510 800,560L800,720Q800,786 753,833Q706,880 640,880L419,880ZM167,340Q154,318 147,292.5Q140,267 140,240Q140,157 198.5,98.5Q257,40 340,40Q423,40 481.5,98.5Q540,157 540,240Q540,267 533,292.5Q526,318 513,340L444,300Q452,286 456,271.5Q460,257 460,240Q460,190 425,155Q390,120 340,120Q290,120 255,155Q220,190 220,240Q220,257 224,271.5Q228,286 236,300L167,340ZM502,620L502,620L502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620L502,620L502,620Z"/>
|
||||
</vector>
|
||||
|
|
@ -157,6 +157,7 @@
|
|||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/album_bottom_sheet_remove_all" />
|
||||
|
||||
<TextView
|
||||
|
|
|
|||
447
app/src/main/res/layout/dialog_track_info.xml
Normal file
447
app/src/main/res/layout/dialog_track_info.xml
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/track_cover_info_image_view"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/trak_title_info_text_view"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trak_title_info_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toTopOf="@+id/trak_artist_info_text_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/track_cover_info_image_view"
|
||||
app:layout_constraintTop_toTopOf="@+id/track_cover_info_image_view"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trak_artist_info_text_view"
|
||||
style="@style/LabelSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/track_cover_info_image_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@+id/trak_title_info_text_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/trak_title_info_text_view" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trak_transcoding_info_text_view"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/title_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/album_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_album" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/artist_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/artist_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_artist" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/artist_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/track_number_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_number_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_track_number" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_number_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/year_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/year_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_year" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/year_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/genre_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/genre_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_genre" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/genre_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/size_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/size_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_size" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/size_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content_type_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content_type_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_content_type" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content_type_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/suffix_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/suffix_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_suffix" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/suffix_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/transcoded_content_type_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transcoded_content_type_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_transcoded_content_type" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transcoded_content_type_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/transcoded_suffix_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transcoded_suffix_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_transcoded_suffix" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transcoded_suffix_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/duration_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duration_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_duration" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duration_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bitrate_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bitrate_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_bitrate" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bitrate_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/path_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/path_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_path" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/path_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
style="@style/Divider"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/disc_number_info_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/disc_number_key_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="4"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/track_info_disc_number" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/disc_number_value_sector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="7"
|
||||
android:text="@string/label_placeholder" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/player_media_quality_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -24,48 +24,38 @@
|
|||
android:checked="true"
|
||||
android:clickable="false"
|
||||
android:text="Unknown"
|
||||
app:chipStrokeWidth="0dp" />
|
||||
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"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_media_bitrate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:visibility="gone" />
|
||||
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"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/player_media_transcoding_audio"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_margin="12dp"
|
||||
android:src="@drawable/ic_transcode"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/player_media_server_transcode_priority"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@drawable/ic_server_transcode_priority"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/player_media_transcoded_extension"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
<ImageButton
|
||||
android:id="@+id/player_info_track"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:clickable="false"
|
||||
android:text="Unknown"
|
||||
android:visibility="gone"
|
||||
app:chipStrokeWidth="0dp" />
|
||||
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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_media_transcoded_bitrate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/player_media_cover_view_pager"
|
||||
|
|
@ -83,7 +73,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.60" />
|
||||
app:layout_constraintGuide_percent="0.575" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_media_title_label"
|
||||
|
|
@ -194,11 +184,11 @@
|
|||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_marginTop="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/player_quick_action_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exo_progress"
|
||||
app:layout_constraintVertical_bias=".60" />
|
||||
app:layout_constraintVertical_bias=".45" />
|
||||
|
||||
<View
|
||||
android:id="@+id/placeholder_view_middle_right"
|
||||
|
|
@ -282,11 +272,11 @@
|
|||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginTop="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/player_quick_action_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/exo_progress"
|
||||
app:layout_constraintVertical_bias=".60"
|
||||
app:layout_constraintVertical_bias=".45"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<ImageButton
|
||||
|
|
@ -343,4 +333,25 @@
|
|||
app:layout_constraintStart_toEndOf="@+id/placeholder_view_middle_right"
|
||||
app:layout_constraintTop_toTopOf="@+id/placeholder_view_middle_right"
|
||||
app:tint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/player_quick_action_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/now_playing_bottom_peek_height"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/player_open_queue_button"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:srcCompat="@drawable/ic_queue" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -15,6 +15,22 @@
|
|||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/now_playing_tap_button"
|
||||
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_margin="16dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
app:cornerRadius="64dp"
|
||||
android:alpha="0.7"
|
||||
app:icon="@drawable/ic_tap"
|
||||
app:layout_constraintEnd_toEndOf="@+id/now_playing_song_cover_image_view"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/now_playing_song_cover_image_view" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/now_playing_song_cover_button_group"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,32 @@
|
|||
<com.cappielloantonio.tempo.helper.recyclerview.NestedScrollableHost xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/player_queue_recycler_view"
|
||||
<com.cappielloantonio.tempo.helper.recyclerview.NestedScrollableHost
|
||||
android:id="@+id/player_queue_nested_scrollable_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/player_queue_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom" />
|
||||
</com.cappielloantonio.tempo.helper.recyclerview.NestedScrollableHost>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/player_shuffle_queue_fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
|
||||
app:srcCompat="@drawable/ic_shuffle"/>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
</com.cappielloantonio.tempo.helper.recyclerview.NestedScrollableHost>
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
<dimen name="activity_margin_content">24dp</dimen>
|
||||
<dimen name="bottom_sheet_behavior_peek_height">136dp</dimen>
|
||||
<dimen name="bottom_sheet_peek_height">56dp</dimen>
|
||||
<dimen name="now_playing_bottom_peek_height">64dp</dimen>
|
||||
<dimen name="global_padding_bottom">164dp</dimen>
|
||||
<dimen name="radius">2dp</dimen>
|
||||
<dimen name="dots_height">2dp</dimen>
|
||||
|
|
|
|||
|
|
@ -304,4 +304,27 @@
|
|||
<string name="downloaded_bottom_sheet_play_next">Play next</string>
|
||||
<string name="downloaded_bottom_sheet_add_to_queue">Add to queue</string>
|
||||
<string name="downloaded_bottom_sheet_remove_all">Remove all</string>
|
||||
<string name="track_info_dialog_positive_button">OK</string>
|
||||
<string name="track_info_dialog_title">Track info</string>
|
||||
<string name="track_info_title">Title</string>
|
||||
<string name="track_info_album">Album</string>
|
||||
<string name="track_info_artist">Artist</string>
|
||||
<string name="track_info_track_number">Track number</string>
|
||||
<string name="track_info_year">Year</string>
|
||||
<string name="track_info_genre">Genre</string>
|
||||
<string name="track_info_size">Size</string>
|
||||
<string name="track_info_content_type">Content Type</string>
|
||||
<string name="track_info_suffix">Suffix</string>
|
||||
<string name="track_info_transcoded_content_type">Transcoded content type</string>
|
||||
<string name="track_info_transcoded_suffix">Transcoded suffix</string>
|
||||
<string name="track_info_duration">Duration</string>
|
||||
<string name="track_info_bitrate">Bitrate</string>
|
||||
<string name="track_info_path">Path</string>
|
||||
<string name="track_info_disc_number">Disc number</string>
|
||||
<string name="track_info_summary_downloaded_file">The file has been downloaded using the Subsonic APIs. The codec and bitrate of the file remain unchanged from the source file.</string>
|
||||
<string name="track_info_summary_server_prioritized">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.</string>
|
||||
<string name="track_info_summary_original_file">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.</string>
|
||||
<string name="track_info_summary_transcoding_codec">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.</string>
|
||||
<string name="track_info_summary_transcoding_bitrate">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.</string>
|
||||
<string name="track_info_summary_full_transcode">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.</string>
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue