mirror of
https://github.com/antebudimir/tempus.git
synced 2026-01-01 09:53:33 +00:00
feat: download starred artists. (#137)
This commit is contained in:
commit
8bb6c02e46
14 changed files with 612 additions and 41 deletions
|
|
@ -11,7 +11,7 @@ android {
|
||||||
targetSdk 35
|
targetSdk 35
|
||||||
|
|
||||||
versionCode 32
|
versionCode 32
|
||||||
versionName '3.15.0'
|
versionName '3.15.1'
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
|
|
||||||
javaCompileOptions {
|
javaCompileOptions {
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ package com.cappielloantonio.tempo.repository;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.cappielloantonio.tempo.App;
|
import com.cappielloantonio.tempo.App;
|
||||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistInfo2;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistInfo2;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.IndexID3;
|
import com.cappielloantonio.tempo.subsonic.models.IndexID3;
|
||||||
|
|
@ -13,12 +15,92 @@ import com.cappielloantonio.tempo.subsonic.models.IndexID3;
|
||||||
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.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import retrofit2.Call;
|
import retrofit2.Call;
|
||||||
import retrofit2.Callback;
|
import retrofit2.Callback;
|
||||||
import retrofit2.Response;
|
import retrofit2.Response;
|
||||||
|
|
||||||
public class ArtistRepository {
|
public class ArtistRepository {
|
||||||
|
private final AlbumRepository albumRepository;
|
||||||
|
|
||||||
|
public ArtistRepository() {
|
||||||
|
this.albumRepository = new AlbumRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getArtistAllSongs(String artistId, ArtistSongsCallback callback) {
|
||||||
|
Log.d("ArtistSync", "Getting albums for artist: " + artistId);
|
||||||
|
|
||||||
|
// Get the artist info first, which contains the albums
|
||||||
|
App.getSubsonicClientInstance(false)
|
||||||
|
.getBrowsingClient()
|
||||||
|
.getArtist(artistId)
|
||||||
|
.enqueue(new Callback<ApiResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null &&
|
||||||
|
response.body().getSubsonicResponse().getArtist() != null &&
|
||||||
|
response.body().getSubsonicResponse().getArtist().getAlbums() != null) {
|
||||||
|
|
||||||
|
List<AlbumID3> albums = response.body().getSubsonicResponse().getArtist().getAlbums();
|
||||||
|
Log.d("ArtistSync", "Got albums directly: " + albums.size());
|
||||||
|
|
||||||
|
if (!albums.isEmpty()) {
|
||||||
|
fetchAllAlbumSongsWithCallback(albums, callback);
|
||||||
|
} else {
|
||||||
|
Log.d("ArtistSync", "No albums found in artist response");
|
||||||
|
callback.onSongsCollected(new ArrayList<>());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("ArtistSync", "Failed to get artist info");
|
||||||
|
callback.onSongsCollected(new ArrayList<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||||
|
Log.d("ArtistSync", "Error getting artist info: " + t.getMessage());
|
||||||
|
callback.onSongsCollected(new ArrayList<>());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchAllAlbumSongsWithCallback(List<AlbumID3> albums, ArtistSongsCallback callback) {
|
||||||
|
if (albums == null || albums.isEmpty()) {
|
||||||
|
Log.d("ArtistSync", "No albums to process");
|
||||||
|
callback.onSongsCollected(new ArrayList<>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Child> allSongs = new ArrayList<>();
|
||||||
|
AtomicInteger remainingAlbums = new AtomicInteger(albums.size());
|
||||||
|
Log.d("ArtistSync", "Processing " + albums.size() + " albums");
|
||||||
|
|
||||||
|
for (AlbumID3 album : albums) {
|
||||||
|
Log.d("ArtistSync", "Getting tracks for album: " + album.getName());
|
||||||
|
MutableLiveData<List<Child>> albumTracks = albumRepository.getAlbumTracks(album.getId());
|
||||||
|
albumTracks.observeForever(songs -> {
|
||||||
|
Log.d("ArtistSync", "Got " + (songs != null ? songs.size() : 0) + " songs from album");
|
||||||
|
if (songs != null) {
|
||||||
|
allSongs.addAll(songs);
|
||||||
|
}
|
||||||
|
albumTracks.removeObservers(null);
|
||||||
|
|
||||||
|
int remaining = remainingAlbums.decrementAndGet();
|
||||||
|
Log.d("ArtistSync", "Remaining albums: " + remaining);
|
||||||
|
|
||||||
|
if (remaining == 0) {
|
||||||
|
Log.d("ArtistSync", "All albums processed. Total songs: " + allSongs.size());
|
||||||
|
callback.onSongsCollected(allSongs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ArtistSongsCallback {
|
||||||
|
void onSongsCollected(List<Child> songs);
|
||||||
|
}
|
||||||
|
|
||||||
public MutableLiveData<List<ArtistID3>> getStarredArtists(boolean random, int size) {
|
public MutableLiveData<List<ArtistID3>> getStarredArtists(boolean random, int size) {
|
||||||
MutableLiveData<List<ArtistID3>> starredArtists = new MutableLiveData<>(new ArrayList<>());
|
MutableLiveData<List<ArtistID3>> starredArtists = new MutableLiveData<>(new ArrayList<>());
|
||||||
|
|
||||||
|
|
@ -89,7 +171,7 @@ public class ArtistRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Metodo che mi restituisce le informazioni essenzionali dell'artista (cover, numero di album...)
|
* Method that returns essential artist information (cover, album number, etc.)
|
||||||
*/
|
*/
|
||||||
public void getArtistInfo(List<ArtistID3> artists, MutableLiveData<List<ArtistID3>> list) {
|
public void getArtistInfo(List<ArtistID3> artists, MutableLiveData<List<ArtistID3>> list) {
|
||||||
List<ArtistID3> liveArtists = list.getValue();
|
List<ArtistID3> liveArtists = list.getValue();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
package com.cappielloantonio.tempo.ui.dialog;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.R;
|
||||||
|
import com.cappielloantonio.tempo.databinding.DialogStarredArtistSyncBinding;
|
||||||
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
import com.cappielloantonio.tempo.viewmodel.StarredArtistsSyncViewModel;
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@OptIn(markerClass = UnstableApi.class)
|
||||||
|
public class StarredArtistSyncDialog extends DialogFragment {
|
||||||
|
private StarredArtistsSyncViewModel starredArtistsSyncViewModel;
|
||||||
|
|
||||||
|
private Runnable onCancel;
|
||||||
|
|
||||||
|
public StarredArtistSyncDialog(Runnable onCancel) {
|
||||||
|
this.onCancel = onCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
DialogStarredArtistSyncBinding bind = DialogStarredArtistSyncBinding.inflate(getLayoutInflater());
|
||||||
|
|
||||||
|
starredArtistsSyncViewModel = new ViewModelProvider(requireActivity()).get(StarredArtistsSyncViewModel.class);
|
||||||
|
|
||||||
|
return new MaterialAlertDialogBuilder(getActivity())
|
||||||
|
.setView(bind.getRoot())
|
||||||
|
.setTitle(R.string.starred_artist_sync_dialog_title)
|
||||||
|
.setPositiveButton(R.string.starred_sync_dialog_positive_button, null)
|
||||||
|
.setNeutralButton(R.string.starred_sync_dialog_neutral_button, null)
|
||||||
|
.setNegativeButton(R.string.starred_sync_dialog_negative_button, null)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
setButtonAction(requireContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAction(Context context) {
|
||||||
|
androidx.appcompat.app.AlertDialog dialog = (androidx.appcompat.app.AlertDialog) getDialog();
|
||||||
|
|
||||||
|
if (dialog != null) {
|
||||||
|
Button positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE);
|
||||||
|
positiveButton.setOnClickListener(v -> {
|
||||||
|
starredArtistsSyncViewModel.getStarredArtistSongs(requireActivity()).observe(this, allSongs -> {
|
||||||
|
if (allSongs != null && !allSongs.isEmpty()) {
|
||||||
|
DownloadUtil.getDownloadTracker(context).download(
|
||||||
|
MappingUtil.mapDownloads(allSongs),
|
||||||
|
allSongs.stream().map(Download::new).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Button neutralButton = dialog.getButton(Dialog.BUTTON_NEUTRAL);
|
||||||
|
neutralButton.setOnClickListener(v -> {
|
||||||
|
Preferences.setStarredArtistsSyncEnabled(true);
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
Button negativeButton = dialog.getButton(Dialog.BUTTON_NEGATIVE);
|
||||||
|
negativeButton.setOnClickListener(v -> {
|
||||||
|
Preferences.setStarredArtistsSyncEnabled(false);
|
||||||
|
if (onCancel != null) onCancel.run();
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
@ -40,6 +41,7 @@ import com.cappielloantonio.tempo.service.MediaService;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.Share;
|
import com.cappielloantonio.tempo.subsonic.models.Share;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
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.AlbumHorizontalAdapter;
|
import com.cappielloantonio.tempo.ui.adapter.AlbumHorizontalAdapter;
|
||||||
|
|
@ -116,6 +118,7 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||||
|
|
||||||
initSyncStarredView();
|
initSyncStarredView();
|
||||||
initSyncStarredAlbumsView();
|
initSyncStarredAlbumsView();
|
||||||
|
initSyncStarredArtistsView();
|
||||||
initDiscoverSongSlideView();
|
initDiscoverSongSlideView();
|
||||||
initSimilarSongView();
|
initSimilarSongView();
|
||||||
initArtistRadio();
|
initArtistRadio();
|
||||||
|
|
@ -327,32 +330,12 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||||
|
|
||||||
private void initSyncStarredAlbumsView() {
|
private void initSyncStarredAlbumsView() {
|
||||||
if (Preferences.isStarredAlbumsSyncEnabled()) {
|
if (Preferences.isStarredAlbumsSyncEnabled()) {
|
||||||
homeViewModel.getStarredAlbums(getViewLifecycleOwner()).observeForever(new Observer<List<AlbumID3>>() {
|
homeViewModel.getStarredAlbums(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), new Observer<List<AlbumID3>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(List<AlbumID3> albums) {
|
public void onChanged(List<AlbumID3> albums) {
|
||||||
if (albums != null) {
|
if (albums != null && !albums.isEmpty()) {
|
||||||
DownloaderManager manager = DownloadUtil.getDownloadTracker(requireContext());
|
checkIfAlbumsNeedSync(albums);
|
||||||
List<String> albumsToSync = new ArrayList<>();
|
|
||||||
int albumCount = 0;
|
|
||||||
|
|
||||||
for (AlbumID3 album : albums) {
|
|
||||||
boolean needsSync = false;
|
|
||||||
albumCount++;
|
|
||||||
albumsToSync.add(album.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (albumCount > 0) {
|
|
||||||
bind.homeSyncStarredAlbumsCard.setVisibility(View.VISIBLE);
|
|
||||||
String message = getResources().getQuantityString(
|
|
||||||
R.plurals.home_sync_starred_albums_count,
|
|
||||||
albumCount,
|
|
||||||
albumCount
|
|
||||||
);
|
|
||||||
bind.homeSyncStarredAlbumsToSync.setText(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
homeViewModel.getStarredAlbums(getViewLifecycleOwner()).removeObserver(this);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -362,26 +345,157 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||||
});
|
});
|
||||||
|
|
||||||
bind.homeSyncStarredAlbumsDownload.setOnClickListener(v -> {
|
bind.homeSyncStarredAlbumsDownload.setOnClickListener(v -> {
|
||||||
homeViewModel.getAllStarredAlbumSongs().observeForever(new Observer<List<Child>>() {
|
homeViewModel.getAllStarredAlbumSongs().observe(getViewLifecycleOwner(), new Observer<List<Child>>() {
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(List<Child> allSongs) {
|
public void onChanged(List<Child> allSongs) {
|
||||||
if (allSongs != null) {
|
if (allSongs != null && !allSongs.isEmpty()) {
|
||||||
DownloaderManager manager = DownloadUtil.getDownloadTracker(requireContext());
|
DownloaderManager manager = DownloadUtil.getDownloadTracker(requireContext());
|
||||||
|
int songsToDownload = 0;
|
||||||
|
|
||||||
for (Child song : allSongs) {
|
for (Child song : allSongs) {
|
||||||
if (!manager.isDownloaded(song.getId())) {
|
if (!manager.isDownloaded(song.getId())) {
|
||||||
manager.download(MappingUtil.mapDownload(song), new Download(song));
|
manager.download(MappingUtil.mapDownload(song), new Download(song));
|
||||||
|
songsToDownload++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
homeViewModel.getAllStarredAlbumSongs().removeObserver(this);
|
if (songsToDownload > 0) {
|
||||||
|
Toast.makeText(requireContext(),
|
||||||
|
getResources().getQuantityString(R.plurals.songs_download_started, songsToDownload, songsToDownload),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bind.homeSyncStarredAlbumsCard.setVisibility(View.GONE);
|
bind.homeSyncStarredAlbumsCard.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkIfAlbumsNeedSync(List<AlbumID3> albums) {
|
||||||
|
homeViewModel.getAllStarredAlbumSongs().observe(getViewLifecycleOwner(), new Observer<List<Child>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<Child> allSongs) {
|
||||||
|
if (allSongs != null) {
|
||||||
|
DownloaderManager manager = DownloadUtil.getDownloadTracker(requireContext());
|
||||||
|
int songsToDownload = 0;
|
||||||
|
List<String> albumsNeedingSync = new ArrayList<>();
|
||||||
|
|
||||||
|
for (AlbumID3 album : albums) {
|
||||||
|
boolean albumNeedsSync = false;
|
||||||
|
// Check if any songs from this album need downloading
|
||||||
|
for (Child song : allSongs) {
|
||||||
|
if (song.getAlbumId() != null && song.getAlbumId().equals(album.getId()) &&
|
||||||
|
!manager.isDownloaded(song.getId())) {
|
||||||
|
songsToDownload++;
|
||||||
|
albumNeedsSync = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (albumNeedsSync) {
|
||||||
|
albumsNeedingSync.add(album.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songsToDownload > 0) {
|
||||||
|
bind.homeSyncStarredAlbumsCard.setVisibility(View.VISIBLE);
|
||||||
|
String message = getResources().getQuantityString(
|
||||||
|
R.plurals.home_sync_starred_albums_count,
|
||||||
|
albumsNeedingSync.size(),
|
||||||
|
albumsNeedingSync.size()
|
||||||
|
);
|
||||||
|
bind.homeSyncStarredAlbumsToSync.setText(message);
|
||||||
|
} else {
|
||||||
|
bind.homeSyncStarredAlbumsCard.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSyncStarredArtistsView() {
|
||||||
|
if (Preferences.isStarredArtistsSyncEnabled()) {
|
||||||
|
homeViewModel.getStarredArtists(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), new Observer<List<ArtistID3>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<ArtistID3> artists) {
|
||||||
|
if (artists != null && !artists.isEmpty()) {
|
||||||
|
checkIfArtistsNeedSync(artists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bind.homeSyncStarredArtistsCancel.setOnClickListener(v -> {
|
||||||
|
bind.homeSyncStarredArtistsCard.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
|
||||||
|
bind.homeSyncStarredArtistsDownload.setOnClickListener(v -> {
|
||||||
|
homeViewModel.getAllStarredArtistSongs().observe(getViewLifecycleOwner(), new Observer<List<Child>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<Child> allSongs) {
|
||||||
|
if (allSongs != null && !allSongs.isEmpty()) {
|
||||||
|
DownloaderManager manager = DownloadUtil.getDownloadTracker(requireContext());
|
||||||
|
int songsToDownload = 0;
|
||||||
|
|
||||||
|
for (Child song : allSongs) {
|
||||||
|
if (!manager.isDownloaded(song.getId())) {
|
||||||
|
manager.download(MappingUtil.mapDownload(song), new Download(song));
|
||||||
|
songsToDownload++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songsToDownload > 0) {
|
||||||
|
Toast.makeText(requireContext(),
|
||||||
|
getResources().getQuantityString(R.plurals.songs_download_started, songsToDownload, songsToDownload),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bind.homeSyncStarredArtistsCard.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkIfArtistsNeedSync(List<ArtistID3> artists) {
|
||||||
|
homeViewModel.getAllStarredArtistSongs().observe(getViewLifecycleOwner(), new Observer<List<Child>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<Child> allSongs) {
|
||||||
|
if (allSongs != null) {
|
||||||
|
DownloaderManager manager = DownloadUtil.getDownloadTracker(requireContext());
|
||||||
|
int songsToDownload = 0;
|
||||||
|
List<String> artistsNeedingSync = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ArtistID3 artist : artists) {
|
||||||
|
boolean artistNeedsSync = false;
|
||||||
|
// Check if any songs from this artist need downloading
|
||||||
|
for (Child song : allSongs) {
|
||||||
|
if (song.getArtistId() != null && song.getArtistId().equals(artist.getId()) &&
|
||||||
|
!manager.isDownloaded(song.getId())) {
|
||||||
|
songsToDownload++;
|
||||||
|
artistNeedsSync = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (artistNeedsSync) {
|
||||||
|
artistsNeedingSync.add(artist.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songsToDownload > 0) {
|
||||||
|
bind.homeSyncStarredArtistsCard.setVisibility(View.VISIBLE);
|
||||||
|
String message = getResources().getQuantityString(
|
||||||
|
R.plurals.home_sync_starred_artists_count,
|
||||||
|
artistsNeedingSync.size(),
|
||||||
|
artistsNeedingSync.size()
|
||||||
|
);
|
||||||
|
bind.homeSyncStarredArtistsToSync.setText(message);
|
||||||
|
} else {
|
||||||
|
bind.homeSyncStarredArtistsCard.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void initDiscoverSongSlideView() {
|
private void initDiscoverSongSlideView() {
|
||||||
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_DISCOVERY)) return;
|
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_DISCOVERY)) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.StarredSyncDialog;
|
import com.cappielloantonio.tempo.ui.dialog.StarredSyncDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.StarredAlbumSyncDialog;
|
import com.cappielloantonio.tempo.ui.dialog.StarredAlbumSyncDialog;
|
||||||
|
import com.cappielloantonio.tempo.ui.dialog.StarredArtistSyncDialog;
|
||||||
import com.cappielloantonio.tempo.ui.dialog.StreamingCacheStorageDialog;
|
import com.cappielloantonio.tempo.ui.dialog.StreamingCacheStorageDialog;
|
||||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
import com.cappielloantonio.tempo.util.Preferences;
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
@ -110,6 +111,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
actionScan();
|
actionScan();
|
||||||
actionSyncStarredAlbums();
|
actionSyncStarredAlbums();
|
||||||
actionSyncStarredTracks();
|
actionSyncStarredTracks();
|
||||||
|
actionSyncStarredArtists();
|
||||||
actionChangeStreamingCacheStorage();
|
actionChangeStreamingCacheStorage();
|
||||||
actionChangeDownloadStorage();
|
actionChangeDownloadStorage();
|
||||||
actionDeleteDownloadStorage();
|
actionDeleteDownloadStorage();
|
||||||
|
|
@ -296,7 +298,21 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void actionSyncStarredArtists() {
|
||||||
|
findPreference("sync_starred_artists_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
|
if (newValue instanceof Boolean) {
|
||||||
|
if ((Boolean) newValue) {
|
||||||
|
StarredArtistSyncDialog dialog = new StarredArtistSyncDialog(() -> {
|
||||||
|
((SwitchPreference)preference).setChecked(false);
|
||||||
|
});
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void actionChangeStreamingCacheStorage() {
|
private void actionChangeStreamingCacheStorage() {
|
||||||
findPreference("streaming_cache_storage").setOnPreferenceClickListener(preference -> {
|
findPreference("streaming_cache_storage").setOnPreferenceClickListener(preference -> {
|
||||||
StreamingCacheStorageDialog dialog = new StreamingCacheStorageDialog(new DialogClickCallback() {
|
StreamingCacheStorageDialog dialog = new StreamingCacheStorageDialog(new DialogClickCallback() {
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
||||||
super.onStop();
|
super.onStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Utilizzare il viewmodel come tramite ed evitare le chiamate dirette
|
// TODO Use the viewmodel as a conduit and avoid direct calls
|
||||||
private void init(View view) {
|
private void init(View view) {
|
||||||
ImageView coverArtist = view.findViewById(R.id.artist_cover_image_view);
|
ImageView coverArtist = view.findViewById(R.id.artist_cover_image_view);
|
||||||
CustomGlideRequest.Builder
|
CustomGlideRequest.Builder
|
||||||
|
|
@ -81,7 +81,7 @@ public class ArtistBottomSheetDialog extends BottomSheetDialogFragment implement
|
||||||
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
ToggleButton favoriteToggle = view.findViewById(R.id.button_favorite);
|
||||||
favoriteToggle.setChecked(artistBottomSheetViewModel.getArtist().getStarred() != null);
|
favoriteToggle.setChecked(artistBottomSheetViewModel.getArtist().getStarred() != null);
|
||||||
favoriteToggle.setOnClickListener(v -> {
|
favoriteToggle.setOnClickListener(v -> {
|
||||||
artistBottomSheetViewModel.setFavorite();
|
artistBottomSheetViewModel.setFavorite(requireContext());
|
||||||
});
|
});
|
||||||
|
|
||||||
TextView playRadio = view.findViewById(R.id.play_radio_text_view);
|
TextView playRadio = view.findViewById(R.id.play_radio_text_view);
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ object Preferences {
|
||||||
private const val WIFI_ONLY = "wifi_only"
|
private const val WIFI_ONLY = "wifi_only"
|
||||||
private const val DATA_SAVING_MODE = "data_saving_mode"
|
private const val DATA_SAVING_MODE = "data_saving_mode"
|
||||||
private const val SERVER_UNREACHABLE = "server_unreachable"
|
private const val SERVER_UNREACHABLE = "server_unreachable"
|
||||||
|
private const val SYNC_STARRED_ARTISTS_FOR_OFFLINE_USE = "sync_starred_artists_for_offline_use"
|
||||||
private const val SYNC_STARRED_ALBUMS_FOR_OFFLINE_USE = "sync_starred_albums_for_offline_use"
|
private const val SYNC_STARRED_ALBUMS_FOR_OFFLINE_USE = "sync_starred_albums_for_offline_use"
|
||||||
private const val SYNC_STARRED_TRACKS_FOR_OFFLINE_USE = "sync_starred_tracks_for_offline_use"
|
private const val SYNC_STARRED_TRACKS_FOR_OFFLINE_USE = "sync_starred_tracks_for_offline_use"
|
||||||
private const val QUEUE_SYNCING = "queue_syncing"
|
private const val QUEUE_SYNCING = "queue_syncing"
|
||||||
|
|
@ -303,6 +304,18 @@ object Preferences {
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isStarredArtistsSyncEnabled(): Boolean {
|
||||||
|
return App.getInstance().preferences.getBoolean(SYNC_STARRED_ARTISTS_FOR_OFFLINE_USE, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setStarredArtistsSyncEnabled(isStarredSyncEnabled: Boolean) {
|
||||||
|
App.getInstance().preferences.edit().putBoolean(
|
||||||
|
SYNC_STARRED_ARTISTS_FOR_OFFLINE_USE, isStarredSyncEnabled
|
||||||
|
).apply()
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isStarredAlbumsSyncEnabled(): Boolean {
|
fun isStarredAlbumsSyncEnabled(): Boolean {
|
||||||
return App.getInstance().preferences.getBoolean(SYNC_STARRED_ALBUMS_FOR_OFFLINE_USE, false)
|
return App.getInstance().preferences.getBoolean(SYNC_STARRED_ALBUMS_FOR_OFFLINE_USE, false)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
package com.cappielloantonio.tempo.viewmodel;
|
package com.cappielloantonio.tempo.viewmodel;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.AndroidViewModel;
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.model.Download;
|
||||||
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
import com.cappielloantonio.tempo.interfaces.StarCallback;
|
||||||
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||||
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
import com.cappielloantonio.tempo.repository.FavoriteRepository;
|
||||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
import com.cappielloantonio.tempo.util.NetworkUtil;
|
import com.cappielloantonio.tempo.util.NetworkUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||||
|
import com.cappielloantonio.tempo.util.Preferences;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
private final ArtistRepository artistRepository;
|
private final ArtistRepository artistRepository;
|
||||||
|
|
@ -34,7 +42,7 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
this.artist = artist;
|
this.artist = artist;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFavorite() {
|
public void setFavorite(Context context) {
|
||||||
if (artist.getStarred() != null) {
|
if (artist.getStarred() != null) {
|
||||||
if (NetworkUtil.isOffline()) {
|
if (NetworkUtil.isOffline()) {
|
||||||
removeFavoriteOffline();
|
removeFavoriteOffline();
|
||||||
|
|
@ -43,9 +51,9 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (NetworkUtil.isOffline()) {
|
if (NetworkUtil.isOffline()) {
|
||||||
setFavoriteOffline();
|
setFavoriteOffline(context);
|
||||||
} else {
|
} else {
|
||||||
setFavoriteOnline();
|
setFavoriteOnline(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +67,6 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
favoriteRepository.unstar(null, null, artist.getId(), new StarCallback() {
|
favoriteRepository.unstar(null, null, artist.getId(), new StarCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onError() {
|
public void onError() {
|
||||||
// artist.setStarred(new Date());
|
|
||||||
favoriteRepository.starLater(null, null, artist.getId(), false);
|
favoriteRepository.starLater(null, null, artist.getId(), false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -67,20 +74,45 @@ public class ArtistBottomSheetViewModel extends AndroidViewModel {
|
||||||
artist.setStarred(null);
|
artist.setStarred(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFavoriteOffline() {
|
private void setFavoriteOffline(Context context) {
|
||||||
favoriteRepository.starLater(null, null, artist.getId(), true);
|
favoriteRepository.starLater(null, null, artist.getId(), true);
|
||||||
artist.setStarred(new Date());
|
artist.setStarred(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFavoriteOnline() {
|
private void setFavoriteOnline(Context context) {
|
||||||
favoriteRepository.star(null, null, artist.getId(), new StarCallback() {
|
favoriteRepository.star(null, null, artist.getId(), new StarCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onError() {
|
public void onError() {
|
||||||
// artist.setStarred(null);
|
|
||||||
favoriteRepository.starLater(null, null, artist.getId(), true);
|
favoriteRepository.starLater(null, null, artist.getId(), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
artist.setStarred(new Date());
|
artist.setStarred(new Date());
|
||||||
|
|
||||||
|
Log.d("ArtistSync", "Checking preference: " + Preferences.isStarredArtistsSyncEnabled());
|
||||||
|
|
||||||
|
if (Preferences.isStarredArtistsSyncEnabled()) {
|
||||||
|
Log.d("ArtistSync", "Starting artist sync for: " + artist.getName());
|
||||||
|
|
||||||
|
artistRepository.getArtistAllSongs(artist.getId(), new ArtistRepository.ArtistSongsCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSongsCollected(List<Child> songs) {
|
||||||
|
Log.d("ArtistSync", "Callback triggered with songs: " + (songs != null ? songs.size() : 0));
|
||||||
|
if (songs != null && !songs.isEmpty()) {
|
||||||
|
Log.d("ArtistSync", "Starting download of " + songs.size() + " songs");
|
||||||
|
DownloadUtil.getDownloadTracker(context).download(
|
||||||
|
MappingUtil.mapDownloads(songs),
|
||||||
|
songs.stream().map(Download::new).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
Log.d("ArtistSync", "Download started successfully");
|
||||||
|
} else {
|
||||||
|
Log.d("ArtistSync", "No songs to download");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Log.d("ArtistSync", "Artist sync preference is disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
///
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ public class HomeViewModel extends AndroidViewModel {
|
||||||
private final SharingRepository sharingRepository;
|
private final SharingRepository sharingRepository;
|
||||||
|
|
||||||
private final StarredAlbumsSyncViewModel albumsSyncViewModel;
|
private final StarredAlbumsSyncViewModel albumsSyncViewModel;
|
||||||
|
private final StarredArtistsSyncViewModel artistSyncViewModel;
|
||||||
|
|
||||||
private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null);
|
private final MutableLiveData<List<Child>> dicoverSongSample = new MutableLiveData<>(null);
|
||||||
private final MutableLiveData<List<AlbumID3>> newReleasedAlbum = new MutableLiveData<>(null);
|
private final MutableLiveData<List<AlbumID3>> newReleasedAlbum = new MutableLiveData<>(null);
|
||||||
|
|
@ -85,6 +86,7 @@ public class HomeViewModel extends AndroidViewModel {
|
||||||
sharingRepository = new SharingRepository();
|
sharingRepository = new SharingRepository();
|
||||||
|
|
||||||
albumsSyncViewModel = new StarredAlbumsSyncViewModel(application);
|
albumsSyncViewModel = new StarredAlbumsSyncViewModel(application);
|
||||||
|
artistSyncViewModel = new StarredArtistsSyncViewModel(application);
|
||||||
|
|
||||||
setOfflineFavorite();
|
setOfflineFavorite();
|
||||||
}
|
}
|
||||||
|
|
@ -174,6 +176,10 @@ public class HomeViewModel extends AndroidViewModel {
|
||||||
return albumsSyncViewModel.getAllStarredAlbumSongs();
|
return albumsSyncViewModel.getAllStarredAlbumSongs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Child>> getAllStarredArtistSongs() {
|
||||||
|
return artistSyncViewModel.getAllStarredArtistSongs();
|
||||||
|
}
|
||||||
|
|
||||||
public LiveData<List<ArtistID3>> getStarredArtists(LifecycleOwner owner) {
|
public LiveData<List<ArtistID3>> getStarredArtists(LifecycleOwner owner) {
|
||||||
if (starredArtists.getValue() == null) {
|
if (starredArtists.getValue() == null) {
|
||||||
artistRepository.getStarredArtists(true, 20).observe(owner, starredArtists::postValue);
|
artistRepository.getStarredArtists(true, 20).observe(owner, starredArtists::postValue);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.cappielloantonio.tempo.viewmodel;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.tempo.repository.ArtistRepository;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||||
|
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class StarredArtistsSyncViewModel extends AndroidViewModel {
|
||||||
|
private final ArtistRepository artistRepository;
|
||||||
|
|
||||||
|
private final MutableLiveData<List<ArtistID3>> starredArtists = new MutableLiveData<>(null);
|
||||||
|
private final MutableLiveData<List<Child>> starredArtistSongs = new MutableLiveData<>(null);
|
||||||
|
|
||||||
|
public StarredArtistsSyncViewModel(@NonNull Application application) {
|
||||||
|
super(application);
|
||||||
|
artistRepository = new ArtistRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<List<ArtistID3>> getStarredArtists(LifecycleOwner owner) {
|
||||||
|
artistRepository.getStarredArtists(false, -1).observe(owner, starredArtists::postValue);
|
||||||
|
return starredArtists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Child>> getAllStarredArtistSongs() {
|
||||||
|
artistRepository.getStarredArtists(false, -1).observeForever(new Observer<List<ArtistID3>>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(List<ArtistID3> artists) {
|
||||||
|
if (artists != null && !artists.isEmpty()) {
|
||||||
|
collectAllArtistSongs(artists, starredArtistSongs::postValue);
|
||||||
|
} else {
|
||||||
|
starredArtistSongs.postValue(new ArrayList<>());
|
||||||
|
}
|
||||||
|
artistRepository.getStarredArtists(false, -1).removeObserver(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return starredArtistSongs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Child>> getStarredArtistSongs(Activity activity) {
|
||||||
|
artistRepository.getStarredArtists(false, -1).observe((LifecycleOwner) activity, artists -> {
|
||||||
|
if (artists != null && !artists.isEmpty()) {
|
||||||
|
collectAllArtistSongs(artists, starredArtistSongs::postValue);
|
||||||
|
} else {
|
||||||
|
starredArtistSongs.postValue(new ArrayList<>());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return starredArtistSongs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectAllArtistSongs(List<ArtistID3> artists, ArtistSongsCallback callback) {
|
||||||
|
if (artists == null || artists.isEmpty()) {
|
||||||
|
callback.onSongsCollected(new ArrayList<>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Child> allSongs = new ArrayList<>();
|
||||||
|
AtomicInteger remainingArtists = new AtomicInteger(artists.size());
|
||||||
|
|
||||||
|
for (ArtistID3 artist : artists) {
|
||||||
|
artistRepository.getArtistAllSongs(artist.getId(), new ArtistRepository.ArtistSongsCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSongsCollected(List<Child> songs) {
|
||||||
|
if (songs != null) {
|
||||||
|
allSongs.addAll(songs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int remaining = remainingArtists.decrementAndGet();
|
||||||
|
if (remaining == 0) {
|
||||||
|
callback.onSongsCollected(allSongs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface ArtistSongsCallback {
|
||||||
|
void onSongsCollected(List<Child> songs);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
app/src/main/res/layout/dialog_starred_artist_sync.xml
Normal file
14
app/src/main/res/layout/dialog_starred_artist_sync.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/starred_artist_sync_dialog_summary" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -198,6 +198,98 @@
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Download/Sync starred artists -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/home_sync_starred_artists_card"
|
||||||
|
style="?attr/materialCardViewOutlinedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="20dp"
|
||||||
|
android:paddingVertical="12dp">
|
||||||
|
|
||||||
|
<!-- Title, secondary and supporting text -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/home_sync_starred_artists_title"
|
||||||
|
style="@style/TitleLarge"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/home_sync_starred_artists_title"
|
||||||
|
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||||
|
android:textFontWeight="600"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/vertical_guideline_artists"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/home_sync_starred_artists_subtitle"
|
||||||
|
style="@style/TitleMedium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/home_sync_starred_artists_subtitle"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/home_sync_starred_artists_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/home_sync_starred_artists_to_sync"
|
||||||
|
style="@style/TitleSmall"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:text="@string/home_sync_starred_artists_subtitle"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/home_sync_starred_artists_subtitle" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/home_sync_starred_artists_to_sync">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_sync_starred_artists_cancel"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="@string/home_sync_starred_cancel" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/home_sync_starred_artists_download"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/home_sync_starred_download" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/vertical_guideline_artists"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="0.90" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<!-- Discover music -->
|
<!-- Discover music -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/home_discover_sector"
|
android:id="@+id/home_discover_sector"
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,8 @@
|
||||||
<string name="home_sync_starred_title">Looks like there are some starred tracks to sync</string>
|
<string name="home_sync_starred_title">Looks like there are some starred tracks to sync</string>
|
||||||
<string name="home_sync_starred_albums_title">Sync Starred Albums</string>
|
<string name="home_sync_starred_albums_title">Sync Starred Albums</string>
|
||||||
<string name="home_sync_starred_albums_subtitle">Albums marked with a star will be available offline</string>
|
<string name="home_sync_starred_albums_subtitle">Albums marked with a star will be available offline</string>
|
||||||
|
<string name="home_sync_starred_artists_title">Starred Artists Sync</string>
|
||||||
|
<string name="home_sync_starred_artists_subtitle">You have starred artists with music not downloaded</string>
|
||||||
<string name="home_title_best_of">Best of</string>
|
<string name="home_title_best_of">Best of</string>
|
||||||
<string name="home_title_discovery">Discovery</string>
|
<string name="home_title_discovery">Discovery</string>
|
||||||
<string name="home_title_discovery_shuffle_all_button">Shuffle all</string>
|
<string name="home_title_discovery_shuffle_all_button">Shuffle all</string>
|
||||||
|
|
@ -346,6 +348,8 @@
|
||||||
<string name="settings_summary_transcoding">Priority given to the transcoding mode. If set to \"Direct play\" the bitrate of the file will not be changed.</string>
|
<string name="settings_summary_transcoding">Priority given to the transcoding mode. If set to \"Direct play\" the bitrate of the file will not be changed.</string>
|
||||||
<string name="settings_summary_transcoding_download">Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format for donwloads\" is set to \"Direct download\" the bitrate of the file will not be changed.</string>
|
<string name="settings_summary_transcoding_download">Download transcoded media. If enabled, the download endpoint will not be used, but the following settings. \n\n If \"Transcode format for donwloads\" is set to \"Direct download\" the bitrate of the file will not be changed.</string>
|
||||||
<string name="settings_summary_transcoding_estimate_content_length">When the file is transcoded on the fly, the client usually does not show the track length. It is possible to request the servers that support the functionality to estimate the duration of the track being played, but the response times may take longer.</string>
|
<string name="settings_summary_transcoding_estimate_content_length">When the file is transcoded on the fly, the client usually does not show the track length. It is possible to request the servers that support the functionality to estimate the duration of the track being played, but the response times may take longer.</string>
|
||||||
|
<string name="settings_sync_starred_artists_for_offline_use_summary">If enabled, starred artists will be downloaded for offline use.</string>
|
||||||
|
<string name="settings_sync_starred_artists_for_offline_use_title">Sync starred artists for offline use</string>
|
||||||
<string name="settings_sync_starred_albums_for_offline_use_summary">If enabled, starred albums will be downloaded for offline use.</string>
|
<string name="settings_sync_starred_albums_for_offline_use_summary">If enabled, starred albums will be downloaded for offline use.</string>
|
||||||
<string name="settings_sync_starred_albums_for_offline_use_title">Sync starred albums for offline use</string>
|
<string name="settings_sync_starred_albums_for_offline_use_title">Sync starred albums for offline use</string>
|
||||||
<string name="settings_sync_starred_tracks_for_offline_use_summary">If enabled, starred tracks will be downloaded for offline use.</string>
|
<string name="settings_sync_starred_tracks_for_offline_use_summary">If enabled, starred tracks will be downloaded for offline use.</string>
|
||||||
|
|
@ -403,6 +407,8 @@
|
||||||
<string name="starred_sync_dialog_positive_button">Continue and download</string>
|
<string name="starred_sync_dialog_positive_button">Continue and download</string>
|
||||||
<string name="starred_sync_dialog_summary">Downloading starred tracks may require a large amount of data.</string>
|
<string name="starred_sync_dialog_summary">Downloading starred tracks may require a large amount of data.</string>
|
||||||
<string name="starred_sync_dialog_title">Sync starred tracks</string>
|
<string name="starred_sync_dialog_title">Sync starred tracks</string>
|
||||||
|
<string name="starred_artist_sync_dialog_summary">Downloading starred artists may require a large amount of data.</string>
|
||||||
|
<string name="starred_artist_sync_dialog_title">Sync starred artists</string>
|
||||||
<string name="starred_album_sync_dialog_summary">Downloading starred albums may require a large amount of data.</string>
|
<string name="starred_album_sync_dialog_summary">Downloading starred albums may require a large amount of data.</string>
|
||||||
<string name="starred_album_sync_dialog_title">Sync starred albums</string>
|
<string name="starred_album_sync_dialog_title">Sync starred albums</string>
|
||||||
<string name="streaming_cache_storage_dialog_sub_summary">For the changes to take effect, restart the app.</string>
|
<string name="streaming_cache_storage_dialog_sub_summary">For the changes to take effect, restart the app.</string>
|
||||||
|
|
@ -410,7 +416,7 @@
|
||||||
<string name="streaming_cache_storage_dialog_title">Select storage option</string>
|
<string name="streaming_cache_storage_dialog_title">Select storage option</string>
|
||||||
<string name="streaming_cache_storage_external_dialog_positive_button">External</string>
|
<string name="streaming_cache_storage_external_dialog_positive_button">External</string>
|
||||||
<string name="streaming_cache_storage_internal_dialog_negative_button">Internal</string>
|
<string name="streaming_cache_storage_internal_dialog_negative_button">Internal</string>
|
||||||
<string name="support_url">https://buymeacoffee.com/a.cappiello</string>
|
<string name="support_url">https://ko-fi.com/eddyizm</string>
|
||||||
<string name="track_info_album">Album</string>
|
<string name="track_info_album">Album</string>
|
||||||
<string name="track_info_artist">Artist</string>
|
<string name="track_info_artist">Artist</string>
|
||||||
<string name="track_info_bit_depth">Bit depth</string>
|
<string name="track_info_bit_depth">Bit depth</string>
|
||||||
|
|
@ -443,6 +449,14 @@
|
||||||
<item quantity="one">%d album to sync</item>
|
<item quantity="one">%d album to sync</item>
|
||||||
<item quantity="other">%d albums to sync</item>
|
<item quantity="other">%d albums to sync</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<plurals name="home_sync_starred_artists_count">
|
||||||
|
<item quantity="one">%d artist to sync</item>
|
||||||
|
<item quantity="other">%d artists to sync</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="songs_download_started">
|
||||||
|
<item quantity="one">Downloading %d song</item>
|
||||||
|
<item quantity="other">Downloading %d songs</item>
|
||||||
|
</plurals>
|
||||||
<string name="equalizer_fragment_title">Equalizer</string>
|
<string name="equalizer_fragment_title">Equalizer</string>
|
||||||
<string name="equalizer_reset">Reset</string>
|
<string name="equalizer_reset">Reset</string>
|
||||||
<string name="equalizer_enable">Enable</string>
|
<string name="equalizer_enable">Enable</string>
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,12 @@
|
||||||
android:summary="@string/settings_sync_starred_albums_for_offline_use_summary"
|
android:summary="@string/settings_sync_starred_albums_for_offline_use_summary"
|
||||||
android:key="sync_starred_albums_for_offline_use" />
|
android:key="sync_starred_albums_for_offline_use" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:title="@string/settings_sync_starred_artists_for_offline_use_title"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:summary="@string/settings_sync_starred_artists_for_offline_use_summary"
|
||||||
|
android:key="sync_starred_artists_for_offline_use" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:defaultValue="1"
|
app:defaultValue="1"
|
||||||
app:dialogTitle="@string/settings_buffering_strategy"
|
app:dialogTitle="@string/settings_buffering_strategy"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue