Merge branch 'development' into main

This commit is contained in:
eddyizm 2025-12-20 10:06:03 -08:00 committed by GitHub
commit bca2e8fcae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 79 additions and 31 deletions

View file

@ -23,6 +23,7 @@ import com.cappielloantonio.tempo.service.MediaManager;
import com.cappielloantonio.tempo.subsonic.models.Child; import com.cappielloantonio.tempo.subsonic.models.Child;
import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.DownloadUtil;
import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.ExternalAudioReader;
import com.cappielloantonio.tempo.util.MusicUtil; import com.cappielloantonio.tempo.util.MusicUtil;
import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.util.Preferences;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -31,7 +32,9 @@ import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueueAdapter.ViewHolder> { public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueueAdapter.ViewHolder> {
private static final String TAG = "PlayerSongQueueAdapter"; private static final String TAG = "PlayerSongQueueAdapter";
@ -39,7 +42,7 @@ public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueue
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture; private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
private List<Child> songs; private List<Child> songs;
private final Map<String, Boolean> downloadStatusCache = new ConcurrentHashMap<>();
private String currentPlayingId; private String currentPlayingId;
private boolean isPlaying; private boolean isPlaying;
private List<Integer> currentPlayingPositions = Collections.emptyList(); private List<Integer> currentPlayingPositions = Collections.emptyList();
@ -80,7 +83,6 @@ public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueue
.build() .build()
.thumbnail(thumbnail) .thumbnail(thumbnail)
.into(holder.item.queueSongCoverImageView); .into(holder.item.queueSongCoverImageView);
MediaManager.getCurrentIndex(mediaBrowserListenableFuture, new MediaIndexCallback() { MediaManager.getCurrentIndex(mediaBrowserListenableFuture, new MediaIndexCallback() {
@Override @Override
public void onRecovery(int index) { public void onRecovery(int index) {
@ -96,16 +98,19 @@ public class PlayerSongQueueAdapter extends RecyclerView.Adapter<PlayerSongQueue
} }
}); });
DownloaderManager downloaderManager = DownloadUtil.getDownloadTracker(holder.itemView.getContext()); boolean isDownloaded = false;
if (downloaderManager != null) { if (Preferences.getDownloadDirectoryUri() == null) {
boolean isDownloaded = downloaderManager.isDownloaded(song.getId()); DownloaderManager downloaderManager = DownloadUtil.getDownloadTracker(holder.itemView.getContext());
if (downloaderManager != null) {
if (isDownloaded) { isDownloaded = downloaderManager.isDownloaded(song.getId());
holder.item.downloadIndicatorIcon.setVisibility(View.VISIBLE);
} else {
holder.item.downloadIndicatorIcon.setVisibility(View.GONE);
} }
} else {
isDownloaded = ExternalAudioReader.getUri(song) != null;
}
if (isDownloaded) {
holder.item.downloadIndicatorIcon.setVisibility(View.VISIBLE);
} else { } else {
holder.item.downloadIndicatorIcon.setVisibility(View.GONE); holder.item.downloadIndicatorIcon.setVisibility(View.GONE);
} }

View file

@ -21,6 +21,7 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.tempo.R;
import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerQueueBinding; import com.cappielloantonio.tempo.databinding.InnerFragmentPlayerQueueBinding;
import com.cappielloantonio.tempo.interfaces.ClickCallback; import com.cappielloantonio.tempo.interfaces.ClickCallback;
import com.cappielloantonio.tempo.service.DownloaderManager; import com.cappielloantonio.tempo.service.DownloaderManager;
@ -32,6 +33,8 @@ import com.cappielloantonio.tempo.ui.adapter.PlayerSongQueueAdapter;
import com.cappielloantonio.tempo.ui.dialog.PlaylistChooserDialog; import com.cappielloantonio.tempo.ui.dialog.PlaylistChooserDialog;
import com.cappielloantonio.tempo.util.Constants; import com.cappielloantonio.tempo.util.Constants;
import com.cappielloantonio.tempo.util.DownloadUtil; import com.cappielloantonio.tempo.util.DownloadUtil;
import com.cappielloantonio.tempo.util.ExternalAudioReader;
import com.cappielloantonio.tempo.util.ExternalAudioWriter;
import com.cappielloantonio.tempo.util.MappingUtil; import com.cappielloantonio.tempo.util.MappingUtil;
import com.cappielloantonio.tempo.util.Preferences; import com.cappielloantonio.tempo.util.Preferences;
import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel; import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel;
@ -384,28 +387,62 @@ public class PlayerQueueFragment extends Fragment implements ClickCallback {
return; return;
} }
List<MediaItem> mediaItemsToDownload = MappingUtil.mapMediaItems(queueSongs); int downloadCount = 0;
List<com.cappielloantonio.tempo.model.Download> downloadModels = new ArrayList<>(); if (Preferences.getDownloadDirectoryUri() == null) {
List<MediaItem> mediaItemsToDownload = MappingUtil.mapMediaItems(queueSongs);
List<com.cappielloantonio.tempo.model.Download> downloadModels = new ArrayList<>();
for (Child child : queueSongs) { for (Child child : queueSongs) {
com.cappielloantonio.tempo.model.Download downloadModel = com.cappielloantonio.tempo.model.Download downloadModel =
new com.cappielloantonio.tempo.model.Download(child); new com.cappielloantonio.tempo.model.Download(child);
downloadModel.setArtist(child.getArtist()); downloadModel.setArtist(child.getArtist());
downloadModel.setAlbum(child.getAlbum()); downloadModel.setAlbum(child.getAlbum());
downloadModel.setCoverArtId(child.getCoverArtId()); downloadModel.setCoverArtId(child.getCoverArtId());
downloadModels.add(downloadModel); downloadModels.add(downloadModel);
} }
DownloaderManager downloaderManager = DownloadUtil.getDownloadTracker(requireContext()); DownloaderManager downloaderManager = DownloadUtil.getDownloadTracker(requireContext());
if (downloaderManager != null) { if (downloaderManager != null) {
downloaderManager.download(mediaItemsToDownload, downloadModels); downloaderManager.download(mediaItemsToDownload, downloadModels);
Toast.makeText(requireContext(), "Starting download of " + queueSongs.size() + " songs in the background.", Toast.LENGTH_SHORT).show(); downloadCount = queueSongs.size();
Toast.makeText(requireContext(),
getResources().getQuantityString(R.plurals.songs_download_started, downloadCount, downloadCount),
Toast.LENGTH_SHORT).show();
new Handler().postDelayed(() -> {
if (playerSongQueueAdapter != null) {
playerSongQueueAdapter.notifyDataSetChanged();
}
}, 1000);
} else {
Log.e(TAG, "DownloaderManager not initialized. Check DownloadUtil.");
Toast.makeText(requireContext(), "Download service unavailable.", Toast.LENGTH_SHORT).show();
}
} else { } else {
Log.e(TAG, "DownloaderManager not initialized. Check DownloadUtil."); for (Child song : queueSongs) {
Toast.makeText(requireContext(), "Download service unavailable.", Toast.LENGTH_SHORT).show(); if (ExternalAudioReader.getUri(song) == null) {
ExternalAudioWriter.downloadToUserDirectory(requireContext(), song);
downloadCount++;
}
}
if (downloadCount > 0) {
Toast.makeText(requireContext(),
getResources().getQuantityString(R.plurals.songs_download_started, downloadCount, downloadCount),
Toast.LENGTH_SHORT).show();
new Handler().postDelayed(() -> {
if (playerSongQueueAdapter != null) {
playerSongQueueAdapter.notifyDataSetChanged();
}
}, 2000);
} else {
Toast.makeText(requireContext(), "All songs already downloaded", Toast.LENGTH_SHORT).show();
}
} }
toggleFabMenu(); toggleFabMenu();
} }

View file

@ -61,7 +61,7 @@
<string name="delete_download_storage_dialog_positive_button">Continua</string> <string name="delete_download_storage_dialog_positive_button">Continua</string>
<string name="delete_download_storage_dialog_summary">Attenzione, procedendo questa azione eliminerà definitivamente tutti gli elementi scaricati da tutti i server.</string> <string name="delete_download_storage_dialog_summary">Attenzione, procedendo questa azione eliminerà definitivamente tutti gli elementi scaricati da tutti i server.</string>
<string name="delete_download_storage_dialog_title">Elimina elementi salvati</string> <string name="delete_download_storage_dialog_title">Elimina elementi salvati</string>
<string name="description_empty_title">Descrizione non disponibile</string> <string name="description_empty_title">Testo non disponibile</string>
<string name="disc_titlefull">Disco %1$s - %2$s</string> <string name="disc_titlefull">Disco %1$s - %2$s</string>
<string name="disc_titleless">Disco %1$s</string> <string name="disc_titleless">Disco %1$s</string>
<string name="download_directory_dialog_negative_button">Annulla</string> <string name="download_directory_dialog_negative_button">Annulla</string>

View file

@ -61,7 +61,7 @@
<string name="delete_download_storage_dialog_positive_button">Kontynuuj</string> <string name="delete_download_storage_dialog_positive_button">Kontynuuj</string>
<string name="delete_download_storage_dialog_summary">Miej na uwadze to że kontynuowanie tej operacji spowoduje usunięcie wszystkich pobranych plików z wszystkich serwerów.</string> <string name="delete_download_storage_dialog_summary">Miej na uwadze to że kontynuowanie tej operacji spowoduje usunięcie wszystkich pobranych plików z wszystkich serwerów.</string>
<string name="delete_download_storage_dialog_title">Usuwanie zapisanych plików</string> <string name="delete_download_storage_dialog_title">Usuwanie zapisanych plików</string>
<string name="description_empty_title">Brak opisu</string> <string name="description_empty_title">Brak tekstu</string>
<string name="disc_titlefull">Płyta %1$s - %2$s</string> <string name="disc_titlefull">Płyta %1$s - %2$s</string>
<string name="disc_titleless">Płyta %1$s</string> <string name="disc_titleless">Płyta %1$s</string>
<string name="download_directory_dialog_negative_button">Anuluj</string> <string name="download_directory_dialog_negative_button">Anuluj</string>
@ -124,6 +124,10 @@
<string name="home_sync_starred_albums_subtitle">Albumy oznaczone gwiazdką będą dostępne offline</string> <string name="home_sync_starred_albums_subtitle">Albumy oznaczone gwiazdką będą dostępne offline</string>
<string name="home_sync_starred_artists_title">Synchronizacja wykonawców oznaczonych gwiazdką</string> <string name="home_sync_starred_artists_title">Synchronizacja wykonawców oznaczonych gwiazdką</string>
<string name="home_sync_starred_artists_subtitle">Masz wykonawców oznaczonych gwiazdką, bez pobranej muzyki</string> <string name="home_sync_starred_artists_subtitle">Masz wykonawców oznaczonych gwiazdką, bez pobranej muzyki</string>
<plurals name="home_sync_starred_songs_count">
<item quantity="one">%d piosenka wymaga synchronizacji</item>
<item quantity="other">%d piosenek wymaga synchronizacji</item>
</plurals>
<string name="home_title_best_of">Najlepsze</string> <string name="home_title_best_of">Najlepsze</string>
<string name="home_title_discovery">Odkrywanie</string> <string name="home_title_discovery">Odkrywanie</string>
<string name="home_title_discovery_shuffle_all_button">Odtwórz wszystkie losowo</string> <string name="home_title_discovery_shuffle_all_button">Odtwórz wszystkie losowo</string>
@ -527,4 +531,6 @@
<string name="folder_play_collecting">Zbieranie piosenek z folderu…</string> <string name="folder_play_collecting">Zbieranie piosenek z folderu…</string>
<string name="folder_play_playing">Odtwarzanie %d piosenek</string> <string name="folder_play_playing">Odtwarzanie %d piosenek</string>
<string name="folder_play_no_songs">Nie znaleziono piosenek w folderze</string> <string name="folder_play_no_songs">Nie znaleziono piosenek w folderze</string>
<string name="search_sort_title">Sortuj ostatnie wyszukiwania chronologicznie</string>
<string name="search_sort_summary">Jeżeli włączone, sortuje wyszukiwania chronologicznie. Sortuje po naziwe jeżeli wyłączone.</string>
</resources> </resources>

View file

@ -61,7 +61,7 @@
<string name="delete_download_storage_dialog_positive_button">Continue</string> <string name="delete_download_storage_dialog_positive_button">Continue</string>
<string name="delete_download_storage_dialog_summary">Please be aware that continuing with this action will result in the permanent deletion of all saved items downloaded from all servers.</string> <string name="delete_download_storage_dialog_summary">Please be aware that continuing with this action will result in the permanent deletion of all saved items downloaded from all servers.</string>
<string name="delete_download_storage_dialog_title">Delete saved items</string> <string name="delete_download_storage_dialog_title">Delete saved items</string>
<string name="description_empty_title">No description available</string> <string name="description_empty_title">No lyrics available</string>
<string name="disc_titlefull">Disc %1$s - %2$s</string> <string name="disc_titlefull">Disc %1$s - %2$s</string>
<string name="disc_titleless">Disc %1$s</string> <string name="disc_titleless">Disc %1$s</string>
<string name="download_directory_dialog_negative_button">Cancel</string> <string name="download_directory_dialog_negative_button">Cancel</string>