mirror of
https://github.com/CappielloAntonio/tempo.git
synced 2026-02-01 07:03:35 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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/6_screenshot.png" width=200>
|
||||||
<img src="mockup/feat/7_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/8_screenshot.png" width=200>
|
||||||
<img src="mockup/feat/9_screenshot.png" width=200>
|
|
||||||
</p>
|
</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
|
## 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.
|
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 {
|
tempo {
|
||||||
dimension "default"
|
dimension "default"
|
||||||
applicationId 'com.cappielloantonio.tempo'
|
applicationId 'com.cappielloantonio.tempo'
|
||||||
versionCode 16
|
versionCode 19
|
||||||
versionName '3.5.0'
|
versionName '3.5.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
notquitemy {
|
notquitemy {
|
||||||
@@ -72,8 +72,8 @@ dependencies {
|
|||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.0'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.1'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.7.0'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.7.1'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.1'
|
implementation 'androidx.recyclerview:recyclerview:1.3.1'
|
||||||
implementation 'androidx.room:room-runtime:2.5.2'
|
implementation 'androidx.room:room-runtime:2.5.2'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
@@ -83,8 +83,8 @@ dependencies {
|
|||||||
implementation 'com.google.android.material:material:1.9.0'
|
implementation 'com.google.android.material:material:1.9.0'
|
||||||
|
|
||||||
// Glide
|
// Glide
|
||||||
implementation 'com.github.bumptech.glide:glide:4.15.1'
|
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||||
implementation 'com.github.bumptech.glide:annotations:4.15.1'
|
implementation 'com.github.bumptech.glide:annotations:4.16.0'
|
||||||
|
|
||||||
// Media3
|
// Media3
|
||||||
implementation 'androidx.media3:media3-session:1.1.1'
|
implementation 'androidx.media3:media3-session:1.1.1'
|
||||||
@@ -93,7 +93,7 @@ dependencies {
|
|||||||
implementation 'androidx.media3:media3-ui:1.1.1'
|
implementation 'androidx.media3:media3-ui:1.1.1'
|
||||||
tempoImplementation 'androidx.media3:media3-cast: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'
|
annotationProcessor 'androidx.room:room-compiler:2.5.2'
|
||||||
|
|
||||||
// Retrofit
|
// Retrofit
|
||||||
|
|||||||
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
@@ -21,4 +21,5 @@
|
|||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
-keepattributes SourceFile, LineNumberTable
|
-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<>();
|
List<Child> tracks = new ArrayList<>();
|
||||||
|
|
||||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getAlbum() != null) {
|
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);
|
albumTracks.setValue(tracks);
|
||||||
|
|||||||
@@ -95,12 +95,29 @@ public class PlaylistRepository {
|
|||||||
.enqueue(new Callback<ApiResponse>() {
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
Log.d("PLAYLIST", response.toString());
|
Log.d("createPlaylist", "onResponse: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ public class MainActivity extends BaseActivity {
|
|||||||
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
||||||
|
|
||||||
setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||||
|
|
||||||
|
collapseBottomSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBottomSheetInPeek(Boolean isVisible) {
|
public void setBottomSheetInPeek(Boolean isVisible) {
|
||||||
|
|||||||
@@ -25,13 +25,15 @@ import java.util.List;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAdapter.ViewHolder> {
|
public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAdapter.ViewHolder> {
|
||||||
private final ClickCallback click;
|
private final ClickCallback click;
|
||||||
private final boolean isCoverVisible;
|
private final boolean showCoverArt;
|
||||||
|
private final boolean showAlbum;
|
||||||
|
|
||||||
private List<Child> songs;
|
private List<Child> songs;
|
||||||
|
|
||||||
public SongHorizontalAdapter(ClickCallback click, boolean isCoverVisible) {
|
public SongHorizontalAdapter(ClickCallback click, boolean showCoverArt, boolean showAlbum) {
|
||||||
this.click = click;
|
this.click = click;
|
||||||
this.isCoverVisible = isCoverVisible;
|
this.showCoverArt = showCoverArt;
|
||||||
|
this.showAlbum = showAlbum;
|
||||||
this.songs = Collections.emptyList();
|
this.songs = Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
Child song = songs.get(position);
|
Child song = songs.get(position);
|
||||||
|
|
||||||
holder.item.searchResultSongTitleTextView.setText(MusicUtil.getReadableString(song.getTitle()));
|
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()));
|
holder.item.trackNumberTextView.setText(MusicUtil.getReadableTrackNumber(holder.itemView.getContext(), song.getTrack()));
|
||||||
|
|
||||||
if (DownloadUtil.getDownloadTracker(holder.itemView.getContext()).isDownloaded(song.getId())) {
|
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);
|
holder.item.searchResultDowanloadIndicatorImageView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCoverVisible) CustomGlideRequest.Builder
|
if (showCoverArt) CustomGlideRequest.Builder
|
||||||
.from(holder.itemView.getContext(), song.getCoverArtId())
|
.from(holder.itemView.getContext(), song.getCoverArtId())
|
||||||
.build()
|
.build()
|
||||||
.into(holder.item.songCoverImageView);
|
.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);
|
holder.item.differentDiskDivider.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +116,7 @@ public class SongHorizontalAdapter extends RecyclerView.Adapter<SongHorizontalAd
|
|||||||
public void onClick() {
|
public void onClick() {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelableArrayList(Constants.TRACKS_OBJECT, new ArrayList<>(MusicUtil.limitPlayableMedia(songs, getBindingAdapterPosition())));
|
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);
|
click.onMediaClick(bundle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class PlaylistChooserDialog extends DialogFragment implements ClickCallba
|
|||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(Constants.TRACK_OBJECT, playlistChooserViewModel.getSongToAdd());
|
bundle.putParcelable(Constants.TRACK_OBJECT, playlistChooserViewModel.getSongToAdd());
|
||||||
|
|
||||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
|
PlaylistEditorDialog dialog = new PlaylistEditorDialog(null);
|
||||||
dialog.setArguments(bundle);
|
dialog.setArguments(bundle);
|
||||||
dialog.show(requireActivity().getSupportFragmentManager(), null);
|
dialog.show(requireActivity().getSupportFragmentManager(), null);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
|
|
||||||
import com.cappielloantonio.tempo.R;
|
import com.cappielloantonio.tempo.R;
|
||||||
import com.cappielloantonio.tempo.databinding.DialogPlaylistEditorBinding;
|
import com.cappielloantonio.tempo.databinding.DialogPlaylistEditorBinding;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogSongHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.PlaylistDialogSongHorizontalAdapter;
|
||||||
import com.cappielloantonio.tempo.util.Constants;
|
import com.cappielloantonio.tempo.util.Constants;
|
||||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||||
@@ -25,10 +26,15 @@ import java.util.Objects;
|
|||||||
public class PlaylistEditorDialog extends DialogFragment {
|
public class PlaylistEditorDialog extends DialogFragment {
|
||||||
private DialogPlaylistEditorBinding bind;
|
private DialogPlaylistEditorBinding bind;
|
||||||
private PlaylistEditorViewModel playlistEditorViewModel;
|
private PlaylistEditorViewModel playlistEditorViewModel;
|
||||||
|
private PlaylistCallback playlistCallback;
|
||||||
|
|
||||||
private String playlistName;
|
private String playlistName;
|
||||||
private PlaylistDialogSongHorizontalAdapter playlistDialogSongHorizontalAdapter;
|
private PlaylistDialogSongHorizontalAdapter playlistDialogSongHorizontalAdapter;
|
||||||
|
|
||||||
|
public PlaylistEditorDialog(PlaylistCallback playlistCallback) {
|
||||||
|
this.playlistCallback = playlistCallback;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
@@ -85,13 +91,13 @@ public class PlaylistEditorDialog extends DialogFragment {
|
|||||||
playlistEditorViewModel.updatePlaylist(playlistName);
|
playlistEditorViewModel.updatePlaylist(playlistName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Objects.requireNonNull(getDialog()).dismiss();
|
dialogDismiss();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||||
playlistEditorViewModel.deletePlaylist();
|
playlistEditorViewModel.deletePlaylist();
|
||||||
Objects.requireNonNull(getDialog()).dismiss();
|
dialogDismiss();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +108,9 @@ public class PlaylistEditorDialog extends DialogFragment {
|
|||||||
playlistDialogSongHorizontalAdapter = new PlaylistDialogSongHorizontalAdapter();
|
playlistDialogSongHorizontalAdapter = new PlaylistDialogSongHorizontalAdapter();
|
||||||
bind.playlistSongRecyclerView.setAdapter(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) {
|
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
|
||||||
int originalPosition = -1;
|
int originalPosition = -1;
|
||||||
@@ -157,4 +165,9 @@ public class PlaylistEditorDialog extends DialogFragment {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void dialogDismiss() {
|
||||||
|
Objects.requireNonNull(getDialog()).dismiss();
|
||||||
|
playlistCallback.onDismiss();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
|||||||
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.songRecyclerView.setHasFixedSize(true);
|
bind.songRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, false);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, false, false);
|
||||||
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
|
|
||||||
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
albumPageViewModel.getAlbumSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
|||||||
private void initTopSongsView() {
|
private void initTopSongsView() {
|
||||||
bind.mostStreamedSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.mostStreamedSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, true, true);
|
||||||
bind.mostStreamedSongRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.mostStreamedSongRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
artistPageViewModel.getArtistTopSongList().observe(getViewLifecycleOwner(), songs -> {
|
artistPageViewModel.getArtistTopSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (songs == null) {
|
if (songs == null) {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.os.Bundle;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -393,7 +392,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
|||||||
private void initStarredTracksView() {
|
private void initStarredTracksView() {
|
||||||
bind.starredTracksRecyclerView.setHasFixedSize(true);
|
bind.starredTracksRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
starredSongAdapter = new SongHorizontalAdapter(this, true);
|
starredSongAdapter = new SongHorizontalAdapter(this, true, false);
|
||||||
bind.starredTracksRecyclerView.setAdapter(starredSongAdapter);
|
bind.starredTracksRecyclerView.setAdapter(starredSongAdapter);
|
||||||
homeViewModel.getStarredTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
homeViewModel.getStarredTracks(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
||||||
if (songs == null) {
|
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() {
|
private void initializeMediaBrowser() {
|
||||||
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.cappielloantonio.tempo.ui.fragment;
|
package com.cappielloantonio.tempo.ui.fragment;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -18,6 +19,7 @@ import com.cappielloantonio.tempo.R;
|
|||||||
import com.cappielloantonio.tempo.databinding.FragmentLibraryBinding;
|
import com.cappielloantonio.tempo.databinding.FragmentLibraryBinding;
|
||||||
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
|
import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
|
||||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||||
|
import com.cappielloantonio.tempo.interfaces.PlaylistCallback;
|
||||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.AlbumAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.AlbumAdapter;
|
||||||
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
|
||||||
@@ -71,7 +73,7 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
initAlbumView();
|
initAlbumView();
|
||||||
initArtistView();
|
initArtistView();
|
||||||
initGenreView();
|
initGenreView();
|
||||||
initPlaylistSlideView();
|
initPlaylistView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,6 +82,12 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
activity.setBottomNavigationBarVisibility(true);
|
activity.setBottomNavigationBarVisibility(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
refreshPlaylistView();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
@@ -222,7 +230,7 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
genreSnapHelper.attachToRecyclerView(bind.genreRecyclerView);
|
genreSnapHelper.attachToRecyclerView(bind.genreRecyclerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initPlaylistSlideView() {
|
private void initPlaylistView() {
|
||||||
bind.playlistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.playlistRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.playlistRecyclerView.setHasFixedSize(true);
|
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
|
@Override
|
||||||
public void onAlbumClick(Bundle bundle) {
|
public void onAlbumClick(Bundle bundle) {
|
||||||
Navigation.findNavController(requireView()).navigate(R.id.albumPageFragment, bundle);
|
Navigation.findNavController(requireView()).navigate(R.id.albumPageFragment, bundle);
|
||||||
@@ -276,7 +290,13 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaylistLongClick(Bundle bundle) {
|
public void onPlaylistLongClick(Bundle bundle) {
|
||||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
|
PlaylistEditorDialog dialog = new PlaylistEditorDialog(new PlaylistCallback() {
|
||||||
|
@Override
|
||||||
|
public void onDismiss() {
|
||||||
|
refreshPlaylistView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
dialog.setArguments(bundle);
|
dialog.setArguments(bundle);
|
||||||
dialog.show(activity.getSupportFragmentManager(), null);
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ public class PlaylistCatalogueFragment extends Fragment implements ClickCallback
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaylistLongClick(Bundle bundle) {
|
public void onPlaylistLongClick(Bundle bundle) {
|
||||||
PlaylistEditorDialog dialog = new PlaylistEditorDialog();
|
PlaylistEditorDialog dialog = new PlaylistEditorDialog(null);
|
||||||
dialog.setArguments(bundle);
|
dialog.setArguments(bundle);
|
||||||
dialog.show(activity.getSupportFragmentManager(), null);
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
hideKeyboard(requireView());
|
hideKeyboard(requireView());
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ public class PlaylistPageFragment extends Fragment implements ClickCallback {
|
|||||||
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.songRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.songRecyclerView.setHasFixedSize(true);
|
bind.songRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
||||||
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.songRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
|
|
||||||
playlistPageViewModel.getPlaylistSongLiveList().observe(getViewLifecycleOwner(), songs -> songHorizontalAdapter.setItems(songs));
|
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.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.searchResultTracksRecyclerView.setHasFixedSize(true);
|
bind.searchResultTracksRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
||||||
bind.searchResultTracksRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.searchResultTracksRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
|||||||
bind.songListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
bind.songListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
bind.songListRecyclerView.setHasFixedSize(true);
|
bind.songListRecyclerView.setHasFixedSize(true);
|
||||||
|
|
||||||
songHorizontalAdapter = new SongHorizontalAdapter(this, true);
|
songHorizontalAdapter = new SongHorizontalAdapter(this, true, false);
|
||||||
bind.songListRecyclerView.setAdapter(songHorizontalAdapter);
|
bind.songListRecyclerView.setAdapter(songHorizontalAdapter);
|
||||||
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
|
songListPageViewModel.getSongList().observe(getViewLifecycleOwner(), songs -> {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import androidx.media3.common.MimeTypes;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
|
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||||
import com.cappielloantonio.tempo.model.Download;
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
import com.cappielloantonio.tempo.repository.DownloadRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
@@ -33,6 +34,7 @@ public class MappingUtil {
|
|||||||
|
|
||||||
public static MediaItem mapMediaItem(Child media) {
|
public static MediaItem mapMediaItem(Child media) {
|
||||||
Uri uri = getUri(media);
|
Uri uri = getUri(media);
|
||||||
|
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(media.getCoverArtId(), Preferences.getImageSize()));
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("id", media.getId());
|
bundle.putString("id", media.getId());
|
||||||
@@ -78,6 +80,7 @@ public class MappingUtil {
|
|||||||
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
.setReleaseYear(media.getYear() != null ? media.getYear() : 0)
|
||||||
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
.setAlbumTitle(MusicUtil.getReadableString(media.getAlbum()))
|
||||||
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
.setArtist(MusicUtil.getReadableString(media.getArtist()))
|
||||||
|
.setArtworkUri(artworkUri)
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@@ -157,6 +160,7 @@ public class MappingUtil {
|
|||||||
|
|
||||||
public static MediaItem mapMediaItem(PodcastEpisode podcastEpisode) {
|
public static MediaItem mapMediaItem(PodcastEpisode podcastEpisode) {
|
||||||
Uri uri = getUri(podcastEpisode);
|
Uri uri = getUri(podcastEpisode);
|
||||||
|
Uri artworkUri = Uri.parse(CustomGlideRequest.createUrl(podcastEpisode.getCoverArtId(), Preferences.getImageSize()));
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("id", podcastEpisode.getId());
|
bundle.putString("id", podcastEpisode.getId());
|
||||||
@@ -202,6 +206,7 @@ public class MappingUtil {
|
|||||||
.setReleaseYear(podcastEpisode.getYear() != null ? podcastEpisode.getYear() : 0)
|
.setReleaseYear(podcastEpisode.getYear() != null ? podcastEpisode.getYear() : 0)
|
||||||
.setAlbumTitle(MusicUtil.getReadableString(podcastEpisode.getAlbum()))
|
.setAlbumTitle(MusicUtil.getReadableString(podcastEpisode.getAlbum()))
|
||||||
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
.setArtist(MusicUtil.getReadableString(podcastEpisode.getArtist()))
|
||||||
|
.setArtworkUri(artworkUri)
|
||||||
.setExtras(bundle)
|
.setExtras(bundle)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@@ -223,9 +228,9 @@ public class MappingUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Uri getUri(PodcastEpisode podcastEpisode) {
|
private static Uri getUri(PodcastEpisode podcastEpisode) {
|
||||||
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(podcastEpisode.getId())
|
return DownloadUtil.getDownloadTracker(App.getContext()).isDownloaded(podcastEpisode.getStreamId())
|
||||||
? getDownloadUri(podcastEpisode.getId())
|
? getDownloadUri(podcastEpisode.getStreamId())
|
||||||
: MusicUtil.getStreamUri(podcastEpisode.getId());
|
: MusicUtil.getStreamUri(podcastEpisode.getStreamId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Uri getDownloadUri(String id) {
|
private static Uri getDownloadUri(String id) {
|
||||||
|
|||||||
@@ -271,8 +271,12 @@ public class MusicUtil {
|
|||||||
return toLimit;
|
return toLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getPlayableMediaPosition(int initialPosition) {
|
public static int getPlayableMediaPosition(List<Child> toLimit, int position) {
|
||||||
return Math.min(initialPosition, Constants.PRE_PLAYABLE_MEDIA);
|
if (!toLimit.isEmpty() && toLimit.size() > Constants.PLAYABLE_MEDIA_LIMIT) {
|
||||||
|
return Math.min(position, Constants.PRE_PLAYABLE_MEDIA);
|
||||||
|
}
|
||||||
|
|
||||||
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConnectivityManager getConnectivityManager() {
|
private static ConnectivityManager getConnectivityManager() {
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ public class PlaylistEditorViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updatePlaylist(String name) {
|
public void updatePlaylist(String name) {
|
||||||
playlistRepository.deletePlaylist(toEdit.getId());
|
playlistRepository.updatePlaylist(toEdit.getId(), name, getPlaylistSongIds());
|
||||||
playlistRepository.createPlaylist(toEdit.getId(), name, getPlaylistSongIds());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deletePlaylist() {
|
public void deletePlaylist() {
|
||||||
|
|||||||
@@ -71,11 +71,17 @@ public class SongListPageViewModel extends AndroidViewModel {
|
|||||||
public void getSongsByPage(LifecycleOwner owner) {
|
public void getSongsByPage(LifecycleOwner owner) {
|
||||||
switch (title) {
|
switch (title) {
|
||||||
case Constants.MEDIA_BY_GENRE:
|
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 -> {
|
songRepository.getSongsByGenre(genre.getGenre(), page).observe(owner, children -> {
|
||||||
List<Child> currentMedia = songList.getValue();
|
if (children != null && !children.isEmpty()) {
|
||||||
currentMedia.addAll(children);
|
List<Child> currentMedia = songList.getValue();
|
||||||
songList.setValue(currentMedia);
|
currentMedia.addAll(children);
|
||||||
|
songList.setValue(currentMedia);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Constants.MEDIA_BY_ARTIST:
|
case Constants.MEDIA_BY_ARTIST:
|
||||||
|
|||||||
@@ -296,7 +296,8 @@
|
|||||||
android:id="@+id/home_grid_tracks_sector"
|
android:id="@+id/home_grid_tracks_sector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/grid_tracks_pre_text_view"
|
android:id="@+id/grid_tracks_pre_text_view"
|
||||||
|
|||||||
Reference in New Issue
Block a user