mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-01-30 22:32:07 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c19db362d9 | ||
|
|
6a505eea4e | ||
|
|
99be2764d0 | ||
|
|
b656ad9e7f | ||
|
|
13d8bbf877 | ||
|
|
68527b2c91 | ||
|
|
347c074368 | ||
|
|
2a74f51f8d | ||
|
|
1a75369591 | ||
|
|
8f6e775ca9 | ||
|
|
bad0fa6c23 | ||
|
|
d4caa6f209 | ||
|
|
723bdf9771 | ||
|
|
49fbd85bb4 | ||
|
|
f040fbf0cf | ||
|
|
efb2213ab7 | ||
|
|
742ac6b17d | ||
|
|
ae7761cb96 | ||
|
|
c977982d64 | ||
|
|
28fc3dca36 | ||
|
|
f1cf65a371 | ||
|
|
beb1d29e8f | ||
|
|
1eda7cef9e | ||
|
|
1af92ad949 | ||
|
|
3fc9b35fe4 | ||
|
|
56b48dbd4d | ||
|
|
1cb371dc5a | ||
|
|
499001a269 | ||
|
|
4c9e47379d | ||
|
|
a0dbb5c81f | ||
|
|
dab53c6bbf | ||
|
|
c0a665c00a | ||
|
|
41b5c57240 | ||
|
|
1d65a79c20 |
@@ -33,12 +33,8 @@ Tempo does not rely on magic algorithms to decide what you should listen to. Ins
|
||||
<img src="mockup/feat/6_screenshot.png" width=200>
|
||||
<img src="mockup/feat/7_screenshot.png" width=200>
|
||||
<img src="mockup/feat/8_screenshot.png" width=200>
|
||||
<img src="mockup/feat/9_screenshot.png" width=200>
|
||||
</p>
|
||||
|
||||
## Disclaimer
|
||||
Tempo is currently under active development and is in alpha state. This means that the app may contain stability issues, bugs, or incomplete features. While we strive to provide a smooth and reliable experience, please be aware that using Tempo in its current state may not be as stable as a fully released version. I appreciate your understanding and patience as I work towards improving the app.
|
||||
|
||||
## Sponsors
|
||||
Tempo is an open-source project developed and maintained solely by me. I would like to express my heartfelt thanks to all the users who have shown their love and support for Tempo. Your contributions and encouragement mean a lot to me, and they help drive the development and improvement of the app.
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ android {
|
||||
tempo {
|
||||
dimension "default"
|
||||
applicationId 'com.cappielloantonio.tempo'
|
||||
versionCode 16
|
||||
versionName '3.5.0'
|
||||
versionCode 20
|
||||
versionName '3.5.5'
|
||||
}
|
||||
|
||||
notquitemy {
|
||||
@@ -72,8 +72,8 @@ dependencies {
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.0'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.0'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.1'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.1'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.1'
|
||||
implementation 'androidx.room:room-runtime:2.5.2'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
@@ -83,8 +83,8 @@ dependencies {
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
|
||||
// Glide
|
||||
implementation 'com.github.bumptech.glide:glide:4.15.1'
|
||||
implementation 'com.github.bumptech.glide:annotations:4.15.1'
|
||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||
|
||||
// Media3
|
||||
implementation 'androidx.media3:media3-session:1.1.1'
|
||||
@@ -93,7 +93,7 @@ dependencies {
|
||||
implementation 'androidx.media3:media3-ui:1.1.1'
|
||||
tempoImplementation 'androidx.media3:media3-cast:1.1.1'
|
||||
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
||||
|
||||
// Retrofit
|
||||
|
||||
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
@@ -21,4 +21,5 @@
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-keepattributes SourceFile, LineNumberTable
|
||||
-keep public class * extends java.lang.Exception
|
||||
-keep public class * extends java.lang.Exception
|
||||
-keep class retrofit2.** { *; }
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.cappielloantonio.tempo.interfaces;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
public interface PlaylistCallback {
|
||||
default void onDismiss() {}
|
||||
}
|
||||
@@ -105,7 +105,9 @@ public class AlbumRepository {
|
||||
List<Child> tracks = new ArrayList<>();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) {
|
||||
tracks.addAll(response.body().getSubsonicResponse().getAlbum().getSongs());
|
||||
if (response.body().getSubsonicResponse().getAlbum().getSongs() != null) {
|
||||
tracks.addAll(response.body().getSubsonicResponse().getAlbum().getSongs());
|
||||
}
|
||||
}
|
||||
|
||||
albumTracks.setValue(tracks);
|
||||
|
||||
@@ -63,9 +63,11 @@ public class ArtistRepository {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
List<ArtistID3> artists = new ArrayList<>();
|
||||
|
||||
if(response.body().getSubsonicResponse().getArtists() != null) {
|
||||
if(response.body().getSubsonicResponse().getArtists() != null && response.body().getSubsonicResponse().getArtists().getIndices() != null) {
|
||||
for (IndexID3 index : response.body().getSubsonicResponse().getArtists().getIndices()) {
|
||||
artists.addAll(index.getArtists());
|
||||
if(index != null && index.getArtists() != null) {
|
||||
artists.addAll(index.getArtists());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,12 +95,29 @@ public class PlaylistRepository {
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
Log.d("PLAYLIST", response.toString());
|
||||
Log.d("createPlaylist", "onResponse: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
Log.d("PLAYLIST", t.toString());
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updatePlaylist(String playlistId, String name, ArrayList<String> songsId) {
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getPlaylistClient()
|
||||
.deletePlaylist(playlistId)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
createPlaylist(null, name, songsId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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(() -> {
|
||||
|
||||
@@ -119,6 +119,8 @@ public class MainActivity extends BaseActivity {
|
||||
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
||||
|
||||
setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||
|
||||
collapseBottomSheet();
|
||||
}
|
||||
|
||||
public void setBottomSheetInPeek(Boolean isVisible) {
|
||||
|
||||
@@ -48,8 +48,8 @@ public class MusicDirectoryAdapter extends RecyclerView.Adapter<MusicDirectoryAd
|
||||
.build()
|
||||
.into(holder.item.musicDirectoryCoverImageView);
|
||||
|
||||
holder.item.musicDirectoryMoreButton.setVisibility(child.isDir() ? View.VISIBLE : View.GONE);
|
||||
holder.item.musicDirectoryPlayButton.setVisibility(child.isDir() ? View.GONE : View.VISIBLE);
|
||||
holder.item.musicDirectoryMoreButton.setVisibility(child.isDir() ? View.VISIBLE : View.INVISIBLE);
|
||||
holder.item.musicDirectoryPlayButton.setVisibility(child.isDir() ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -60,7 +60,7 @@ public class MusicIndexAdapter extends RecyclerView.Adapter<MusicIndexAdapter.Vi
|
||||
|
||||
@Override
|
||||
public String getTextToShowInBubble(int pos) {
|
||||
return Character.toString(Objects.requireNonNull(artists.get(pos).getName().toUpperCase()).charAt(0));
|
||||
return artists != null && !artists.isEmpty() ? Character.toString(Objects.requireNonNull(artists.get(pos).getName().toUpperCase()).charAt(0)) : null;
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@@ -25,13 +25,15 @@ import java.util.List;
|
||||
@UnstableApi
|
||||
public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAdapter.ViewHolder> {
|
||||
private final ClickCallback click;
|
||||
private final boolean isCoverVisible;
|
||||
private final boolean showCoverArt;
|
||||
private final boolean showAlbum;
|
||||
|
||||
private List<Child> songs;
|
||||
|
||||
public SongHorizontalAdapter(ClickCallback click, boolean isCoverVisible) {
|
||||
public SongHorizontalAdapter(ClickCallback click, boolean showCoverArt, boolean showAlbum) {
|
||||
this.click = click;
|
||||
this.isCoverVisible = isCoverVisible;
|
||||
this.showCoverArt = showCoverArt;
|
||||
this.showAlbum = showAlbum;
|
||||
this.songs = Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -47,7 +49,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
Child song = songs.get(position);
|
||||
|
||||
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration() != null ? song.getDuration() : 0, false)));
|
||||
holder.item.searchResultSongSubtitleTextView.setText(holder.itemView.getContext().getString(R.string.song_subtitle_formatter, MusicUtil.getReadableString(this.showAlbum ? song.getAlbum() : song.getArtist()), MusicUtil.getReadableDurationString(song.getDuration() != null ? song.getDuration() : 0, false)));
|
||||
holder.item.trackNumberTextView.setText(MusicUtil.getReadableTrackNumber(holder.itemView.getContext(), song.getTrack()));
|
||||
|
||||
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
|
||||
@@ -56,16 +58,16 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (isCoverVisible) CustomGlideRequest.Builder
|
||||
if (showCoverArt) CustomGlideRequest.Builder
|
||||
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||
.build()
|
||||
.into(holder.item.songCoverImageView);
|
||||
|
||||
if (isCoverVisible) holder.item.trackNumberTextView.setVisibility(View.INVISIBLE);
|
||||
if (showCoverArt) holder.item.trackNumberTextView.setVisibility(View.INVISIBLE);
|
||||
|
||||
if (!isCoverVisible) holder.item.songCoverImageView.setVisibility(View.INVISIBLE);
|
||||
if (!showCoverArt) holder.item.songCoverImageView.setVisibility(View.INVISIBLE);
|
||||
|
||||
if (!isCoverVisible && (position > 0 && songs.get(position - 1) != null && songs.get(position - 1).getDiscNumber() != null && songs.get(position).getDiscNumber() != null && songs.get(position - 1).getDiscNumber() < songs.get(position).getDiscNumber())) {
|
||||
if (!showCoverArt && (position > 0 && songs.get(position - 1) != null && songs.get(position - 1).getDiscNumber() != null && songs.get(position).getDiscNumber() != null && songs.get(position - 1).getDiscNumber() < songs.get(position).getDiscNumber())) {
|
||||
holder.item.differentDiskDivider.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -114,7 +116,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
||||
public void onClick() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(MusicUtil.limitPlayableMedia(songs, getBindingAdapterPosition())));
|
||||
bundle.putInt(Constants.ITEM_POSITION, MusicUtil.getPlayableMediaPosition(getBindingAdapterPosition()));
|
||||
bundle.putInt(Constants.ITEM_POSITION, MusicUtil.getPlayableMediaPosition(songs, getBindingAdapterPosition()));
|
||||
|
||||
click.onMediaClick(bundle);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(Constants.TRACK_OBJECT, playlistChooserViewModel.getSongToAdd());
|
||||
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog(null);
|
||||
dialog.setArguments(bundle);
|
||||
dialog.show(requireActivity().getSupportFragmentManager(), null);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.DialogPlaylistEditorBinding;
|
||||
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
|
||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogSongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
@@ -25,10 +26,15 @@ import java.util.Objects;
|
||||
public class PlaylistEditorDialog extends DialogFragment {
|
||||
private DialogPlaylistEditorBinding bind;
|
||||
private PlaylistEditorViewModel playlistEditorViewModel;
|
||||
private PlaylistCallback playlistCallback;
|
||||
|
||||
private String playlistName;
|
||||
private PlaylistDialogSongHorizontalAdapter playlistDialogSongHorizontalAdapter;
|
||||
|
||||
public PlaylistEditorDialog(PlaylistCallback playlistCallback) {
|
||||
this.playlistCallback = playlistCallback;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
@@ -85,13 +91,13 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
playlistEditorViewModel.updatePlaylist(playlistName);
|
||||
}
|
||||
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
dialogDismiss();
|
||||
}
|
||||
});
|
||||
|
||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
playlistEditorViewModel.deletePlaylist();
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
dialogDismiss();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,7 +108,9 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
playlistDialogSongHorizontalAdapter = new PlaylistDialogSongHorizontalAdapter();
|
||||
bind.playlistSongRecyclerView.setAdapter(playlistDialogSongHorizontalAdapter);
|
||||
|
||||
playlistEditorViewModel.getPlaylistSongLiveList().observe(requireActivity(), songs -> playlistDialogSongHorizontalAdapter.setItems(songs));
|
||||
playlistEditorViewModel.getPlaylistSongLiveList().observe(requireActivity(), songs -> {
|
||||
if (songs != null) playlistDialogSongHorizontalAdapter.setItems(songs);
|
||||
});
|
||||
|
||||
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
|
||||
int originalPosition = -1;
|
||||
@@ -157,4 +165,9 @@ public class PlaylistEditorDialog extends DialogFragment {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void dialogDismiss() {
|
||||
Objects.requireNonNull(getDialog()).dismiss();
|
||||
playlistCallback.onDismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
||||
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.songRecyclerView.setHasFixedSize(true);
|
||||
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, false);
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, false, false);
|
||||
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
||||
|
||||
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
||||
|
||||
@@ -164,7 +164,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
private void initTopSongsView() {
|
||||
bind.mostStreamedSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true, true);
|
||||
bind.mostStreamedSongRecyclerView.setAdapter(songHorizontalAdapter);
|
||||
artistPageViewModel.getArtistTopSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||
if (songs == null) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -393,7 +392,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
private void initStarredTracksView() {
|
||||
bind.starredTracksRecyclerView.setHasFixedSize(true);
|
||||
|
||||
starredSongAdapter = new SongHorizontalAdapter(this, true);
|
||||
starredSongAdapter = new SongHorizontalAdapter(this, true, false);
|
||||
bind.starredTracksRecyclerView.setAdapter(starredSongAdapter);
|
||||
homeViewModel.getStarredTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
||||
if (songs == null) {
|
||||
@@ -619,25 +618,6 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
});
|
||||
}
|
||||
|
||||
public void reorder() {
|
||||
if (bind != null) {
|
||||
// bind.homeLinearLayoutContainer.removeAllViews();
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeDiscoverSector);
|
||||
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeSimilarTracksSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeRadioArtistSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeGridTracksSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.starredTracksSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.starredAlbumsSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.starredArtistsSector);
|
||||
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeRecentlyAddedAlbumsSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeFlashbackSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeMostPlayedAlbumsSector);
|
||||
// bind.homeLinearLayoutContainer.addView(bind.homeRecentlyPlayedAlbumsSector);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeMediaBrowser() {
|
||||
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||
}
|
||||
|
||||
@@ -85,13 +85,15 @@ public class IndexFragment extends Fragment implements ClickCallback {
|
||||
}
|
||||
|
||||
private void initDirectoryListView() {
|
||||
MusicFolder musicFolder = getArguments().getParcelable(Constants.MUSIC_FOLDER_OBJECT);
|
||||
|
||||
bind.indexRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.indexRecyclerView.setHasFixedSize(true);
|
||||
|
||||
musicIndexAdapter = new MusicIndexAdapter(this);
|
||||
bind.indexRecyclerView.setAdapter(musicIndexAdapter);
|
||||
|
||||
indexViewModel.getIndexes().observe(getViewLifecycleOwner(), indexes -> {
|
||||
indexViewModel.getIndexes(musicFolder != null ? musicFolder.getId() : null).observe(getViewLifecycleOwner(), indexes -> {
|
||||
if (indexes != null) {
|
||||
musicIndexAdapter.setItems(IndexUtil.getArtist(indexes));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -18,6 +19,7 @@ import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.databinding.FragmentLibraryBinding;
|
||||
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
|
||||
@@ -71,7 +73,7 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||
initAlbumView();
|
||||
initArtistView();
|
||||
initGenreView();
|
||||
initPlaylistSlideView();
|
||||
initPlaylistView();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,6 +82,12 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||
activity.setBottomNavigationBarVisibility(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
refreshPlaylistView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
@@ -222,7 +230,7 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||
genreSnapHelper.attachToRecyclerView(bind.genreRecyclerView);
|
||||
}
|
||||
|
||||
private void initPlaylistSlideView() {
|
||||
private void initPlaylistView() {
|
||||
bind.playlistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.playlistRecyclerView.setHasFixedSize(true);
|
||||
|
||||
@@ -244,6 +252,12 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshPlaylistView() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> libraryViewModel.refreshPlaylistSample(getViewLifecycleOwner());
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.albumPageFragment, bundle);
|
||||
@@ -276,7 +290,13 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||
|
||||
@Override
|
||||
public void onPlaylistLongClick(Bundle bundle) {
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog(new PlaylistCallback() {
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
refreshPlaylistView();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.setArguments(bundle);
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ public class PlaylistCatalogueFragment extends Fragment implements ClickCallback
|
||||
|
||||
@Override
|
||||
public void onPlaylistLongClick(Bundle bundle) {
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
|
||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog(null);
|
||||
dialog.setArguments(bundle);
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
hideKeyboard(requireView());
|
||||
|
||||
@@ -200,7 +200,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
||||
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.songRecyclerView.setHasFixedSize(true);
|
||||
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
||||
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
||||
|
||||
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
||||
|
||||
@@ -112,7 +112,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
|
||||
bind.searchResultTracksRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.searchResultTracksRecyclerView.setHasFixedSize(true);
|
||||
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
||||
bind.searchResultTracksRecyclerView.setAdapter(songHorizontalAdapter);
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
bind.songListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.songListRecyclerView.setHasFixedSize(true);
|
||||
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
||||
bind.songListRecyclerView.setAdapter(songHorizontalAdapter);
|
||||
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||
isLoading = false;
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.model.Download;
|
||||
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
@@ -33,6 +34,7 @@ public class MappingUtil {
|
||||
|
||||
public static MediaItem mapMediaItem(Child media) {
|
||||
Uri uri = getUri(media);
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(media.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("id", media.getId());
|
||||
@@ -78,6 +80,7 @@ public class MappingUtil {
|
||||
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
||||
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||
.setArtworkUri(artworkUri)
|
||||
.setExtras(bundle)
|
||||
.build()
|
||||
)
|
||||
@@ -157,6 +160,7 @@ public class MappingUtil {
|
||||
|
||||
public static MediaItem mapMediaItem(PodcastEpisode podcastEpisode) {
|
||||
Uri uri = getUri(podcastEpisode);
|
||||
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(podcastEpisode.getCoverArtId(), Preferences.getImageSize()));
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("id", podcastEpisode.getId());
|
||||
@@ -202,6 +206,7 @@ public class MappingUtil {
|
||||
.setReleaseYear(podcastEpisode.getYear() != null ? podcastEpisode.getYear() : 0)
|
||||
.setAlbumTitle(MusicUtil.getReadableString(podcastEpisode.getAlbum()))
|
||||
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
||||
.setArtworkUri(artworkUri)
|
||||
.setExtras(bundle)
|
||||
.build()
|
||||
)
|
||||
@@ -223,9 +228,9 @@ public class MappingUtil {
|
||||
}
|
||||
|
||||
private static Uri getUri(PodcastEpisode podcastEpisode) {
|
||||
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(podcastEpisode.getId())
|
||||
? getDownloadUri(podcastEpisode.getId())
|
||||
: MusicUtil.getStreamUri(podcastEpisode.getId());
|
||||
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(podcastEpisode.getStreamId())
|
||||
? getDownloadUri(podcastEpisode.getStreamId())
|
||||
: MusicUtil.getStreamUri(podcastEpisode.getStreamId());
|
||||
}
|
||||
|
||||
private static Uri getDownloadUri(String id) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -271,8 +294,12 @@ public class MusicUtil {
|
||||
return toLimit;
|
||||
}
|
||||
|
||||
public static int getPlayableMediaPosition(int initialPosition) {
|
||||
return Math.min(initialPosition, Constants.PRE_PLAYABLE_MEDIA);
|
||||
public static int getPlayableMediaPosition(List<Child> toLimit, int position) {
|
||||
if (!toLimit.isEmpty() && toLimit.size() > Constants.PLAYABLE_MEDIA_LIMIT) {
|
||||
return Math.min(position, Constants.PRE_PLAYABLE_MEDIA);
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
private static ConnectivityManager getConnectivityManager() {
|
||||
|
||||
@@ -23,8 +23,8 @@ public class IndexViewModel extends AndroidViewModel {
|
||||
directoryRepository = new DirectoryRepository();
|
||||
}
|
||||
|
||||
public MutableLiveData<Indexes> getIndexes() {
|
||||
return directoryRepository.getIndexes(null, null);
|
||||
public MutableLiveData<Indexes> getIndexes(String musicFolderId) {
|
||||
return directoryRepository.getIndexes(musicFolderId, null);
|
||||
}
|
||||
|
||||
public String getMusicFolderName() {
|
||||
|
||||
@@ -37,8 +37,7 @@ public class PlaylistEditorViewModel extends AndroidViewModel {
|
||||
}
|
||||
|
||||
public void updatePlaylist(String name) {
|
||||
playlistRepository.deletePlaylist(toEdit.getId());
|
||||
playlistRepository.createPlaylist(toEdit.getId(), name, getPlaylistSongIds());
|
||||
playlistRepository.updatePlaylist(toEdit.getId(), name, getPlaylistSongIds());
|
||||
}
|
||||
|
||||
public void deletePlaylist() {
|
||||
|
||||
@@ -71,11 +71,17 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||
public void getSongsByPage(LifecycleOwner owner) {
|
||||
switch (title) {
|
||||
case Constants.MEDIA_BY_GENRE:
|
||||
int page = (songList.getValue() != null ? songList.getValue().size() : 0) / 100;
|
||||
int songCount = songList.getValue() != null ? songList.getValue().size() : 0;
|
||||
|
||||
if (songCount > 0 && songCount % 100 != 0) return;
|
||||
|
||||
int page = songCount / 100;
|
||||
songRepository.getSongsByGenre(genre.getGenre(), page).observe(owner, children -> {
|
||||
List<Child> currentMedia = songList.getValue();
|
||||
currentMedia.addAll(children);
|
||||
songList.setValue(currentMedia);
|
||||
if (children != null && !children.isEmpty()) {
|
||||
List<Child> currentMedia = songList.getValue();
|
||||
currentMedia.addAll(children);
|
||||
songList.setValue(currentMedia);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case Constants.MEDIA_BY_ARTIST:
|
||||
|
||||
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>
|
||||
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>
|
||||
|
||||
@@ -296,7 +296,8 @@
|
||||
android:id="@+id/home_grid_tracks_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/grid_tracks_pre_text_view"
|
||||
|
||||
@@ -49,9 +49,9 @@
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="8dp" />
|
||||
android:paddingBottom="4dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
|
||||
@@ -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>
|
||||
@@ -6,8 +6,8 @@
|
||||
android:clipChildren="false"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp">
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/music_directory_cover_image_view"
|
||||
@@ -50,10 +50,10 @@
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/ic_navigate_next"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@id/music_directory_title_text_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view"
|
||||
android:visibility="gone"/>
|
||||
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/music_directory_play_button"
|
||||
@@ -63,8 +63,8 @@
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/ic_play"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@id/music_directory_title_text_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view"
|
||||
android:visibility="gone"/>
|
||||
app:layout_constraintTop_toTopOf="@+id/music_directory_title_text_view" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -3,7 +3,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/music_folder_cover_image_view"
|
||||
@@ -29,7 +31,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingEnd="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintBottom_toBottomOf="@id/music_folder_cover_image_view"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -300,4 +300,27 @@
|
||||
<string name="menu_group_by_artist">Artist</string>
|
||||
<string name="menu_group_by_genre">Genre</string>
|
||||
<string name="menu_group_by_year">Year</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>
|
||||
Reference in New Issue
Block a user