package org.moire.ultrasonic.fragment; import android.os.Bundle; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ListView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import org.moire.ultrasonic.R; import org.moire.ultrasonic.data.ActiveServerProvider; import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.service.DownloadFile; import org.moire.ultrasonic.service.MediaPlayerController; import org.moire.ultrasonic.service.MusicService; import org.moire.ultrasonic.service.MusicServiceFactory; import org.moire.ultrasonic.subsonic.ImageLoaderProvider; import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker; import org.moire.ultrasonic.subsonic.VideoPlayer; import org.moire.ultrasonic.util.CancellationToken; import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.FragmentBackgroundTask; import org.moire.ultrasonic.util.Util; import org.moire.ultrasonic.view.EntryAdapter; import java.util.ArrayList; import java.util.List; import kotlin.Lazy; import static org.koin.java.KoinJavaComponent.inject; /** * Lists the Bookmarks available on the server */ public class BookmarksFragment extends Fragment { private SwipeRefreshLayout refreshAlbumListView; private ListView albumListView; private View albumButtons; private View emptyView; private ImageView playNowButton; private ImageView pinButton; private ImageView unpinButton; private ImageView downloadButton; private ImageView deleteButton; private final Lazy mediaPlayerController = inject(MediaPlayerController.class); private final Lazy imageLoader = inject(ImageLoaderProvider.class); private final Lazy networkAndStorageChecker = inject(NetworkAndStorageChecker.class); private CancellationToken cancellationToken; @Override public void onCreate(@Nullable Bundle savedInstanceState) { Util.applyTheme(this.getContext()); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.select_album, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { cancellationToken = new CancellationToken(); albumButtons = view.findViewById(R.id.menu_album); super.onViewCreated(view, savedInstanceState); refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh); albumListView = view.findViewById(R.id.select_album_entries_list); refreshAlbumListView.setOnRefreshListener(() -> { enableButtons(); getBookmarks(); }); albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); albumListView.setOnItemClickListener((parent, view17, position, id) -> { if (position >= 0) { MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position); if (entry != null) { if (entry.isVideo()) { VideoPlayer.Companion.playVideo(getContext(), entry); } else { enableButtons(); } } } }); ImageView selectButton = view.findViewById(R.id.select_album_select); playNowButton = view.findViewById(R.id.select_album_play_now); ImageView playNextButton = view.findViewById(R.id.select_album_play_next); ImageView playLastButton = view.findViewById(R.id.select_album_play_last); pinButton = view.findViewById(R.id.select_album_pin); unpinButton = view.findViewById(R.id.select_album_unpin); downloadButton = view.findViewById(R.id.select_album_download); deleteButton = view.findViewById(R.id.select_album_delete); ImageView oreButton = view.findViewById(R.id.select_album_more); emptyView = view.findViewById(R.id.select_album_empty); selectButton.setVisibility(View.GONE); playNextButton.setVisibility(View.GONE); playLastButton.setVisibility(View.GONE); oreButton.setVisibility(View.GONE); playNowButton.setOnClickListener(view16 -> playNow(getSelectedSongs(albumListView))); selectButton.setOnClickListener(view15 -> selectAllOrNone()); pinButton.setOnClickListener(view14 -> { downloadBackground(true); selectAll(false, false); }); unpinButton.setOnClickListener(view13 -> { unpin(); selectAll(false, false); }); downloadButton.setOnClickListener(view12 -> { downloadBackground(false); selectAll(false, false); }); deleteButton.setOnClickListener(view1 -> { delete(); selectAll(false, false); }); registerForContextMenu(albumListView); FragmentTitle.Companion.setTitle(this, R.string.button_bar_bookmarks); enableButtons(); getBookmarks(); } @Override public void onDestroyView() { cancellationToken.cancel(); super.onDestroyView(); } private void getBookmarks() { new LoadTask() { @Override protected MusicDirectory load(MusicService service) throws Exception { return Util.getSongsFromBookmarks(service.getBookmarks()); } }.execute(); } private void playNow(List songs) { if (!getSelectedSongs(albumListView).isEmpty()) { int position = songs.get(0).getBookmarkPosition(); mediaPlayerController.getValue().restore(songs, 0, position, true, true); selectAll(false, false); } } private static List getSelectedSongs(ListView albumListView) { List songs = new ArrayList<>(10); if (albumListView != null) { int count = albumListView.getCount(); for (int i = 0; i < count; i++) { if (albumListView.isItemChecked(i)) { MusicDirectory.Entry song = (MusicDirectory.Entry) albumListView.getItemAtPosition(i); if (song != null) songs.add(song); } } } return songs; } private void selectAllOrNone() { boolean someUnselected = false; int count = albumListView.getCount(); for (int i = 0; i < count; i++) { if (!albumListView.isItemChecked(i) && albumListView.getItemAtPosition(i) instanceof MusicDirectory.Entry) { someUnselected = true; break; } } selectAll(someUnselected, true); } private void selectAll(boolean selected, boolean toast) { int count = albumListView.getCount(); int selectedCount = 0; for (int i = 0; i < count; i++) { MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(i); if (entry != null && !entry.isDirectory() && !entry.isVideo()) { albumListView.setItemChecked(i, selected); selectedCount++; } } // Display toast: N tracks selected / N tracks unselected if (toast) { int toastResId = selected ? R.string.select_album_n_selected : R.string.select_album_n_unselected; Util.toast(getContext(), getString(toastResId, selectedCount)); } enableButtons(); } private void enableButtons() { List selection = getSelectedSongs(albumListView); boolean enabled = !selection.isEmpty(); boolean unpinEnabled = false; boolean deleteEnabled = false; int pinnedCount = 0; for (MusicDirectory.Entry song : selection) { if (song == null) continue; DownloadFile downloadFile = mediaPlayerController.getValue().getDownloadFileForSong(song); if (downloadFile.isWorkDone()) { deleteEnabled = true; } if (downloadFile.isSaved()) { pinnedCount++; unpinEnabled = true; } } playNowButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE); pinButton.setVisibility((enabled && !ActiveServerProvider.Companion.isOffline() && selection.size() > pinnedCount) ? View.VISIBLE : View.GONE); unpinButton.setVisibility(enabled && unpinEnabled ? View.VISIBLE : View.GONE); downloadButton.setVisibility(enabled && !deleteEnabled && !ActiveServerProvider.Companion.isOffline() ? View.VISIBLE : View.GONE); deleteButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE); } private void downloadBackground(final boolean save) { List songs = getSelectedSongs(albumListView); if (songs.isEmpty()) { selectAll(true, false); songs = getSelectedSongs(albumListView); } downloadBackground(save, songs); } private void downloadBackground(final boolean save, final List songs) { Runnable onValid = () -> { networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable(); mediaPlayerController.getValue().downloadBackground(songs, save); if (save) { Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size())); } else { Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size())); } }; onValid.run(); } private void delete() { List songs = getSelectedSongs(albumListView); if (songs.isEmpty()) { selectAll(true, false); songs = getSelectedSongs(albumListView); } mediaPlayerController.getValue().delete(songs); } private void unpin() { List songs = getSelectedSongs(albumListView); Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size())); mediaPlayerController.getValue().unpin(songs); } private abstract class LoadTask extends FragmentBackgroundTask> { public LoadTask() { super(BookmarksFragment.this.getActivity(), true, refreshAlbumListView, cancellationToken); } protected abstract MusicDirectory load(MusicService service) throws Exception; @Override protected Pair doInBackground() throws Throwable { MusicService musicService = MusicServiceFactory.getMusicService(); MusicDirectory dir = load(musicService); boolean valid = musicService.isLicenseValid(); return new Pair<>(dir, valid); } @Override protected void done(Pair result) { MusicDirectory musicDirectory = result.first; List entries = musicDirectory.getChildren(); int songCount = 0; for (MusicDirectory.Entry entry : entries) { if (!entry.isDirectory()) { songCount++; } } final int listSize = getArguments() == null? 0 : getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0); if (songCount > 0) { pinButton.setVisibility(View.VISIBLE); unpinButton.setVisibility(View.VISIBLE); downloadButton.setVisibility(View.VISIBLE); deleteButton.setVisibility(View.VISIBLE); playNowButton.setVisibility(View.VISIBLE); } else { pinButton.setVisibility(View.GONE); unpinButton.setVisibility(View.GONE); downloadButton.setVisibility(View.GONE); deleteButton.setVisibility(View.GONE); playNowButton.setVisibility(View.GONE); if (listSize == 0 || result.first.getChildren().size() < listSize) { albumButtons.setVisibility(View.GONE); } } enableButtons(); emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE); albumListView.setAdapter(new EntryAdapter(getContext(), imageLoader.getValue().getImageLoader(), entries, true)); } } }